Import cargo-gnaw from fuchsia repo: https://fuchsia.googlesource.com/fuchsia ref: 492f241811526190ec6d504fe1cf4cfaabaf5301 Change-Id: I6cd76890f9411e516928cfb27df9ee362963b073 Reviewed-on: https://pigweed-review.googlesource.com/c/third_party/fuchsia/cargo-gnaw/+/235274 Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com> Reviewed-by: Rob Mohr <mohrr@google.com>
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c169014 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,2 @@ +target/ +!/Cargo.toml
diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 0000000..2337b99 --- /dev/null +++ b/BUILD.gn
@@ -0,0 +1,119 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/host.gni") +import("//build/rust/rustc_binary.gni") +import("//build/rust/rustc_library.gni") +import("//build/rust/rustc_test.gni") + +if (is_host) { + rustc_binary("cargo-gnaw-bin") { + edition = "2021" + output_name = "gnaw" + + deps = [ + ":cargo-gnaw-lib", + "//third_party/rust_crates:anyhow", + ] + + sources = [ "src/main.rs" ] + } + + rustc_library("cargo-gnaw-lib") { + edition = "2021" + name = "gnaw_lib" + + deps = [ + "//third_party/rust_crates:anyhow", + "//third_party/rust_crates:argh", + "//third_party/rust_crates:camino", + "//third_party/rust_crates:cargo_metadata", + "//third_party/rust_crates:semver", + "//third_party/rust_crates:serde", + "//third_party/rust_crates:serde_derive", + "//third_party/rust_crates:serde_json", + "//third_party/rust_crates:toml", + "//third_party/rust_crates:walkdir", + ] + + sources = [ + "src/build.rs", + "src/cfg.rs", + "src/gn.rs", + "src/graph.rs", + "src/lib.rs", + "src/target.rs", + "src/types.rs", + ] + + inputs = [ + "templates/gn_header.template", + "templates/gn_import.template", + "templates/gn_license.template", + "templates/gn_rule.template", + "templates/gn_sdk_metadata_header.template", + "templates/top_level_binary_gn_rule.template", + "templates/top_level_gn_rule.template", + ] + } + + rustc_test("cargo-gnaw-tests") { + edition = "2021" + source_root = "src/lib.rs" + deps = [ + "//third_party/rust_crates:anyhow", + "//third_party/rust_crates:argh", + "//third_party/rust_crates:camino", + "//third_party/rust_crates:cargo_metadata", + "//third_party/rust_crates:semver", + "//third_party/rust_crates:serde", + "//third_party/rust_crates:serde_derive", + "//third_party/rust_crates:serde_json", + "//third_party/rust_crates:toml", + "//third_party/rust_crates:walkdir", + ] + + sources = [ + "src/build.rs", + "src/cfg.rs", + "src/gn.rs", + "src/graph.rs", + "src/lib.rs", + "src/target.rs", + "src/types.rs", + ] + + inputs = [ + "templates/gn_header.template", + "templates/gn_import.template", + "templates/gn_license.template", + "templates/gn_rule.template", + "templates/gn_sdk_metadata_header.template", + "templates/top_level_binary_gn_rule.template", + "templates/top_level_gn_rule.template", + ] + } +} + +install_host_tools("install-cargo-gnaw") { + deps = [ ":cargo-gnaw-bin" ] + outputs = [ "gnaw" ] +} + +group("cargo-gnaw") { + testonly = true + deps = [ + ":cargo-gnaw-bin($host_toolchain)", + ":install-cargo-gnaw", + ":tests", + ] +} + +group("tests") { + testonly = true + deps = [ + ":cargo-gnaw-tests($host_toolchain)", + "tests", + ] +}
diff --git a/Cargo.toml b/Cargo.toml new file mode 120000 index 0000000..68b7940 --- /dev/null +++ b/Cargo.toml
@@ -0,0 +1 @@ +Cargo.toml.crates-io \ No newline at end of file
diff --git a/Cargo.toml.crates-io b/Cargo.toml.crates-io new file mode 100644 index 0000000..e40852c --- /dev/null +++ b/Cargo.toml.crates-io
@@ -0,0 +1,33 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This file is used when publishing to crates.io + +[package] +edition = "2018" +name = "gnaw" +version = "0.1.0" +authors = ["The Fuchsia Authors"] +description = "Tooling to convert Cargo.toml files into native GN rules" +license-file = "../../LICENSE" +repository = "https://fuchsia.googlesource.com/fuchsia/+/HEAD/tools/cargo-gnaw" + +[lib] +name = "gnaw_lib" + +[dependencies] +anyhow = "1.0.38" +argh = "0.1.7" +camino = "1.0.5" +cargo_metadata = "0.18.1" +serde = "1.0.116" +serde_derive = "1.0.116" +serde_json = "1.0.59" +toml = "0.5.6" +walkdir = "2.3.3" +semver = "1.0.23" + +[dev-dependencies] +pretty_assertions = "0.5.1" +tempfile = "3.2.0"
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7ed244f --- /dev/null +++ b/LICENSE
@@ -0,0 +1,24 @@ +Copyright 2019 The Fuchsia Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ee7e90 --- /dev/null +++ b/README.md
@@ -0,0 +1,145 @@ +# Cargo GNaw +**Tooling to convert Cargo.toml files into native GN rules** + +[](https://crates.io/crates/cargo-gnaw) +[](https://github.com/google/cargo-gnaw/LICENSE) +[](https://docs.rs/crate/cargo-gnaw/) + + +## Install +```sh +$ cargo install --path ~/cargo-gnaw +``` + +## Run +```sh +$ cargo gnaw --manifest-path ~/fuchsia/third_party/rust_crates/Cargo.toml -o ~/fuchsia/third_party/rust_crates/BUILD.gn +``` + +### Options +* --skip-root - Skip the root package in the Cargo.toml and treat it's dependencies as the top-level targets +* --gn-bin - Path to GN binary for formatting the output + +## How it works + +Cargo GNaw operates on vendored crates to convert them into [GN](https://gn.googlesource.com/gn/+/HEAD/docs/reference.md) rules. The resulting BUILD.gn file is expected +to be vendored with the crates and provides targets for the GN build system to reference. + +All top-level crates are given an easy to use GN alias group that references the version exposed in the Cargo.toml. +Direct dependencies of the root crate can be "lifted" to the top-level by skipping the default +root crate. + +### Simple Example +```toml +[package] +name = "simple" +version = "1.0.25" +authors = ["Benjamin Brittain <bwb@google.com>"] +edition = "2018" + +[dependencies] +``` + +converts to: + +```gn + +group("simple") { + deps = [":simple-1-0-25"] +} + +rust_library("simple-1-0-25") { + crate_name = "simple" + crate_root = "//tools/cargo-gnaw/src/tests/simple/src/lib.rs" + output_name = "simple-9ac42213326ac72d" + + deps = [] + + rustenv = [] + rustflags = ["--cap-lints=allow", + "--edition=2018", + "-Cmetadata=9ac42213326ac72d", + "-Cextra-filename=-9ac42213326ac72d"] +} + +``` + +### Build Scripts +GNaw intentionally does not handle build.rs scripts at compilation time. Any evaluation of a build.rs script is done when the crate is vendored. +The resulting configuration which is usually produced by the build.rs script is put into [a +section in the source Cargo.toml](#gn-configs). +Simple build.rs scripts (ones that only depend upon Rust's `std` library) evaluate and +automatically provide the author with the expected configuration. + +### Packages vs. targets vs. crates +The difference between a "package", "target", and a "crate" is useful knowledge when working with GNaw: + - A Cargo "target" corresponds to source files that can be compiled into a Rust "crate". + There are multiple target types, including library and binary. + - A Cargo "package" contains one or more "targets", and can contain at most one library target. + +(Paraphrased from https://doc.rust-lang.org/cargo/reference/cargo-targets.html) + +Many Cargo packages only contain a single library target that produces a single library crate. +Because of this (and because of the catchiness of the term) "crate" is often used imprecisely to +refer to any of those three items depending on the context. However, when packages contain +multiple targets, like one or more binaries alongside the library, it becomes important to +distinguish between these terms. + +## GN configs +Some targets, including but not limited to those that use build.rs scripts, require additional +configuration that can be specified in the Cargo.toml file. This configuration is consumed by +GNaw, not Cargo, and used when generating GN targets. + +### Basic configuration +Basic configuration for a package's library target is specified as +`gn.package.<PackageName>.<ExactVersion>`. This configuration will be applied to the GN library +target unconditionally (for all platforms) and can include the following arrays: + +* `configs` - native GN config +* `deps` - native GN dependency +* `env_vars` - environment variables, usually used for pretending to be Cargo +* `rustflags` - flags to pass through to rustc + +#### Example +```toml +[gn.package.anyhow."1.0.25"] +rustflags = [ "--cfg=backtrace" ] +``` + +### Platform-specific configuration +Configuration can also be applied to only specific platforms, for example only when building for +Fuchsia or only when building for a specific host platform. + +Platforms are specified in the Rust cfg format (e.g. `cfg(unix)`). The same four configuration fields (`configs`, `deps`, `env_vars`, `rustflags`) documented above are supported. + +#### Example +```toml +[gn.package.foo."1.2.3".platform."cfg(target_os = \"fuchsia\")"] +configs = [ "//some:fuchsia_specific_config" ] +``` + +### Generating executables for binary targets +GNaw also supports generating GN executable targets for Cargo binary targets. GNaw must be told +to generate such targets; none are generated by default. + +Platform-independent or -dependent configuration can be specified as well, similar to above. The +same four configuration fields (`configs`, `deps`, `env_vars`, `rustflags`) documented above are +supported. + +In the example below, `my-gn-name` will become both the name of the group target to depend upon +and the executable's output_name, so usages can assume output_name == target_name (and must since +get_target_outputs only works within the same file). + +#### Example +This example generates a group target named `my-gn-name` and an executable target that produces +`my-gn-name` from the binary `my-cargo-target` target inside package `my-cargo-package` version +1.2.3. Also, when building for Unix this example applies an extra GN config. + +```toml +[gn.package.my-cargo-package."1.2.3".binary.my-cargo-target] +output_name = "my-gn-name" +rustflags = [ "--cfg=feature" ] + +[gn.package.my-cargo-package."1.2.3".binary.my-cargo-target.platform."cfg(unix)"] +configs = [ "//some:unix_specific_config" ] +```
diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..a43558d --- /dev/null +++ b/src/build.rs
@@ -0,0 +1,142 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::target::GnTarget; +use anyhow::{anyhow, Result}; +use std::path::PathBuf; +use std::process::Command; +use std::str::FromStr; + +pub struct BuildScriptOutput { + pub rustflags: Vec<String>, + pub rustenv: Vec<String>, +} + +impl BuildScriptOutput { + pub fn parse_from_file(file: PathBuf) -> Result<Self> { + let contents = std::fs::read_to_string(file)?; + let configs: Vec<&str> = contents.split('\n').collect(); + Ok(Self::parse(configs)) + } + + pub fn parse(lines: Vec<&str>) -> Self { + let mut bs = BuildScriptOutput { rustflags: vec![], rustenv: vec![] }; + + for line in lines { + if line.is_empty() { + continue; + } + + let keyval = line.strip_prefix("cargo:"); + if keyval.is_none() { + eprintln!("warning: build script output includes non-cargo lines: {}", line); + continue; + } + + // More information on the available cargo instructions is here: + // https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script + // + // The following cases are currently unsupported. They all have to do with specific + // artifact type link arguments. The more general cargo:rustc-link-arg is covered. + // + // cargo:rustc-link-arg-bin=BIN=FLAG — Passes custom flags to a linker for the + // binary BIN. + // + // cargo:rustc-link-arg-bins=FLAG — Passes custom flags to a linker for binaries. + // + // cargo:rustc-link-arg-tests=FLAG — Passes custom flags to a linker for tests. + // + // cargo:rustc-link-arg-examples=FLAG — Passes custom flags to a linker for + // examples. + // + // cargo:rustc-link-arg-benches=FLAG — Passes custom flags to a linker for + // benchmarks. + // + // cargo:rustc-cdylib-link-arg=FLAG — Passes custom flags to a linker for cdylib crates. + + let (key, value) = keyval.unwrap().split_once('=').unwrap(); + match key { + "rerun-if-changed" => continue, // ignored because these are always vendored + "rerun-if-env-changed" => continue, // ignored because these are always vendored + "rustc-cfg" => bs.rustflags.push(format!("\"--cfg={}\"", value)), + "rustc-env" => bs.rustenv.push(format!("\"{}\"", value.to_string())), + "rustc-flags" => bs.rustflags.push(format!("\"{}\"", value.to_string())), + "rustc-link-arg" => bs.rustflags.push(format!("\"-C link-arg={}\"", value)), + "rustc-link-lib" => bs.rustflags.push(format!("\"-l {}\"", value)), + // "rustc-link-search" => bs.rustflags.push(format!("-L {}", value)), + "warning" => eprintln!("warning: {}", value), + &_ => eprintln!("warning: unable to parse build script output: {}", value), + } + } + + bs + } +} + +pub struct BuildScript {} + +impl BuildScript { + pub fn execute(target: &GnTarget<'_>) -> Result<BuildScriptOutput> { + // Previously, this code would compile build.rs scripts directly with rustc. However, build + // scripts can have dependencies of their own (e.g. build-dependencies in Cargo.toml). + // Rather than resolve the entire dependency chain ourselves, we call out to cargo to build + // the crate, performing the build.rs compilation along the way. This does more work than + // compiling the build.rs directly but we don't want to get into the dependency resolution + // business when cargo can do this already. + // + // Unfortunately, there is no way to tell cargo to build only the build script. We could + // link in the cargo library itself and call the correct functions to do only that part of + // the build but that seems overkill. + let cargo = std::env::var_os("CARGO").unwrap_or_else(|| std::ffi::OsString::from("cargo")); + + let command = Command::new(cargo) + .current_dir(target.package_root()) + .arg("check") + .arg("--message-format=json-render-diagnostics") + .output() + .expect("failed to execute cargo crate build"); + + if !command.status.success() { + return Err(anyhow!( + "Failed to compile {}:\n{}", + target.gn_target_name(), + String::from_utf8_lossy(&command.stderr) + )); + } + + let stdout = String::from_utf8(command.stdout)?; + let messages: Vec<&str> = stdout.trim().split('\n').collect(); + for message in messages { + let json: serde_json::Value = + serde_json::from_str(message).expect("JSON was not well-formatted"); + + let reason = json.get("reason").unwrap(); + if reason != "build-script-executed" { + continue; + } + + let package_id = json.get("package_id").unwrap().to_string().replace('"', ""); + let package_elements: Vec<&str> = package_id.split(' ').collect(); + let crate_name = package_elements[0]; + + // this build script execution was for the crate that we are expecting to have a + // build script (avoids issues where a dependency has a build script and we use + // that one by mistake) + if crate_name != target.target_name { + continue; + } + + // cargo executes the build script for us and stores the output in its cache, we just + // need to parse it + let out_dir = json.get("out_dir").unwrap().as_str().unwrap(); + let output = PathBuf::from_str(out_dir).unwrap().parent().unwrap().join("output"); + return BuildScriptOutput::parse_from_file(output); + } + + Err(anyhow!( + "We detected a build script in {} but cargo didn't compile it", + target.gn_target_name() + )) + } +}
diff --git a/src/cfg.rs b/src/cfg.rs new file mode 100644 index 0000000..75aa505 --- /dev/null +++ b/src/cfg.rs
@@ -0,0 +1,259 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use anyhow::{anyhow, Result}; + +/// Converts a cargo platform-specific dependency target into GN imperative control flow. +/// +/// `Cargo.toml` files can define [platform-specific dependencies] in two ways: +/// - Using `#[cfg]` syntax to match classes of targets. +/// ```text +/// [target.'cfg(unix)'.dependencies] +/// foo = "0.1" +/// ``` +/// +/// - Listing the full target the dependencies would apply to. +/// ```text +/// [target.aarch64-apple-darwin.dependencies] +/// bar = "0.1" +/// ``` +/// +/// This function is a wrapper around [`cfg_to_gn_conditional`] that converts full targets +/// into `#[cfg]` syntax. +/// +/// [platform-specific dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies +pub fn target_to_gn_conditional(target: &str) -> Result<String> { + if target.starts_with("cfg") { + // This is #[cfg] syntax already. + cfg_to_gn_conditional(target) + } else if target.contains('-') { + // Handle this as a target triple. We only need to support targets in crates we + // depend on, so we use a simple exact match list that can be updated as needed. + let (target_arch, target_os) = match target { + "aarch64-apple-darwin" => ("aarch64", "macos"), + "riscv32i-unknown-none-elf" => ("riscv32i", "unknown"), + "riscv32imc-unknown-none-elf" => ("riscv32imc", "unknown"), + "thumbv6m-none-eabi" => ("thumbv6m", "unknown"), + // Return an error for unknown targets, to notify the dev that they should + // update this list. + _ => { + return Err(anyhow!( + "Unknown target: {}. Please add this to 'tools/cargo-gnaw/src/cfg.rs'.", + target + )) + } + }; + cfg_to_gn_conditional(&format!( + "cfg(all(target_arch = \"{}\", target_os = \"{}\"))", + target_arch, target_os + )) + } else { + Err(anyhow!("Unknown platform-specific dependency target syntax: {}", target)) + } +} + +/// Convert a cargo cfg conditional into GN imperative control flow +// TODO This should consume some information in the Cargo.toml file +// to establish conventions for the carge -> GN build. This is hardcoded +// to support Fuchsia at the moment. +// +// wow. an interview question in real life. +pub fn cfg_to_gn_conditional(cfg: &str) -> Result<String> { + #[allow(clippy::if_same_then_else)] + if cfg.starts_with("cfg") { + Ok(cfg_to_gn_conditional(&cfg[4..cfg.len() - 1])?) + } else if cfg.starts_with("not") { + let section = &cfg[4..]; + let mut paren_count = 1; + for (idx, c) in section.chars().enumerate() { + if c == ')' { + paren_count -= 1; + if paren_count == 0 { + return Ok(format!("!({})", cfg_to_gn_conditional(§ion[..idx])?)); + } + } else if c == '(' { + paren_count += 1; + } + } + Err(anyhow!("bad not statement")) + } else if cfg == "any()" { + Ok(String::from("true")) + } else if cfg.starts_with("any") { + let section = &cfg[4..cfg.len()]; + let mut accum = vec![]; + let mut paren_count = 1; + let mut start_idx = 0; + for (idx, c) in section.chars().enumerate() { + if c == ')' { + paren_count -= 1; + if paren_count == 0 { + accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?); + } + } else if c == '(' { + paren_count += 1; + } else if c == ',' && paren_count <= 1 { + accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?); + start_idx = idx + 2; // skip ", " + } + } + Ok(format!("({})", accum.join(" || "))) + } else if cfg.starts_with("all") { + let section = &cfg[4..cfg.len()]; + let mut accum = vec![]; + let mut paren_count = 1; + let mut start_idx = 0; + for (idx, c) in section.chars().enumerate() { + if c == ')' { + paren_count -= 1; + if paren_count == 0 { + accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?); + } + } else if c == '(' { + paren_count += 1; + } else if c == ',' && paren_count <= 1 { + accum.push(cfg_to_gn_conditional(§ion[start_idx..idx])?); + start_idx = idx + 2; // skip ", " + } + } + Ok(format!("({})", accum.join(" && "))) + } else if cfg == "target_os = \"fuchsia\"" { + Ok(String::from("current_os == \"fuchsia\"")) + } else if cfg == "target_os = \"macos\"" { + Ok(String::from("current_os == \"mac\"")) + } else if cfg == "target_os = \"linux\"" { + Ok(String::from("current_os == \"linux\"")) + } else if cfg == "target_os = \"unknown\"" { + Ok(String::from("current_os == \"unknown\"")) + } else if cfg == "unix" { + // all our platforms are unix + Ok(String::from("true")) + } else if cfg == "feature = \"std\"" { + // need to detect std usage + Ok(String::from("true")) + } else if cfg == "target_arch = \"aarch64\"" { + Ok(String::from("current_cpu == \"arm64\"")) + } else if cfg == "target_arch = \"x86_64\"" { + Ok(String::from("current_cpu == \"x64\"")) + } else if cfg == "target_arch = \"wasm32\"" { + Ok(String::from("current_cpu == \"wasm32\"")) + } else if cfg == "target_arch = \"riscv32i\"" { + Ok(String::from("current_cpu == \"riscv32\"")) + } else if cfg == "target_arch = \"riscv32imc\"" { + Ok(String::from("current_cpu == \"riscv32\"")) + } else if cfg == "target_arch = \"thumbv6m\"" { + Ok(String::from("current_cpu == \"arm\"")) + } else if cfg == "windows" || cfg == "target_family = \"windows\"" { + // don't support host builds on windows right now + Ok(String::from("false")) + + // Everything below is random cfgs that we don't know anything about + } else if cfg.starts_with("target_os") { + Ok(String::from("false")) + } else if cfg.starts_with("target_arch") { + Ok(String::from("false")) + } else if cfg.starts_with("target_env") { + Ok(String::from("false")) + } else if cfg.starts_with("target_feature") { + Ok(String::from("false")) + } else if cfg.starts_with("target_vendor") { + Ok(String::from("false")) + } else if cfg.starts_with("tokio_taskdump") { + Ok(String::from("false")) + } else if cfg.starts_with("tokio_unstable") { + Ok(String::from("false")) + } else if cfg.starts_with("tracing_unstable") { + Ok(String::from("false")) + } else if cfg.starts_with("docsrs") { + Ok(String::from("false")) + } else { + // TODO(https://fxbug.dev/42061225) better handling needed for these cases. + Err(anyhow!("Unknown cfg option used: {}", cfg)) + } +} + +#[test] +fn basic_fuchsia() { + let cfg_str = r#"cfg(target_os = "fuchsia")"#; + let output = cfg_to_gn_conditional(cfg_str).unwrap(); + assert_eq!(output, "current_os == \"fuchsia\""); +} + +#[test] +fn conditonal_empty_any() { + let cfg_str = r#"cfg(any())"#; + let output = cfg_to_gn_conditional(cfg_str).unwrap(); + assert_eq!(output, "true"); +} + +#[test] +fn conditonal_any() { + let cfg_str = r#"cfg(any(target_os = "fuchsia", target_os = "macos"))"#; + let output = cfg_to_gn_conditional(cfg_str).unwrap(); + assert_eq!(output, "(current_os == \"fuchsia\" || current_os == \"mac\")"); +} + +#[test] +fn conditonal_all() { + let cfg_str = r#"cfg(all(target_os = "fuchsia", target_os = "macos"))"#; + let output = cfg_to_gn_conditional(cfg_str).unwrap(); + assert_eq!(output, "(current_os == \"fuchsia\" && current_os == \"mac\")"); +} + +#[test] +fn conditonal_all_not() { + let cfg_str = r#"cfg(all(target_os = "fuchsia", not(target_os = "macos")))"#; + let output = cfg_to_gn_conditional(cfg_str).unwrap(); + assert_eq!(output, "(current_os == \"fuchsia\" && !(current_os == \"mac\"))"); +} + +#[test] +fn conditonal_fail() { + let cfg_str = r#"cfg(everything(target_os = "fuchsia"))"#; + let output = cfg_to_gn_conditional(cfg_str).unwrap_err(); + assert_eq!(output.to_string(), r#"Unknown cfg option used: everything(target_os = "fuchsia")"#); +} + +#[test] +fn nested_cfgs() { + let cfg_str = + r#"cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))"#; + let output = cfg_to_gn_conditional(cfg_str).unwrap(); + assert_eq!( + output.to_string(), + r#"((current_cpu == "x64" || current_cpu == "arm64") && false)"# + ); +} + +#[test] +fn target_cfg() { + let target_str = r#"cfg(target_os = "fuchsia")"#; + let output = target_to_gn_conditional(target_str).unwrap(); + assert_eq!(output, "current_os == \"fuchsia\""); +} + +#[test] +fn target_full() { + let target_str = r#"aarch64-apple-darwin"#; + let output = target_to_gn_conditional(target_str).unwrap(); + assert_eq!(output, "(current_cpu == \"arm64\" && current_os == \"mac\")"); +} + +#[test] +fn target_unknown_full() { + // We use a placeholder target "guaranteed" to never be used anywhere. + let target_str = r#"foo-bar-baz"#; + let err = target_to_gn_conditional(target_str).unwrap_err(); + assert_eq!( + err.to_string(), + "Unknown target: foo-bar-baz. Please add this to 'tools/cargo-gnaw/src/cfg.rs'.", + ); +} + +#[test] +fn target_unknown_syntax() { + // No leading "cfg", no hyphens. + let target_str = r#"fizzbuzz"#; + let err = target_to_gn_conditional(target_str).unwrap_err(); + assert_eq!(err.to_string(), "Unknown platform-specific dependency target syntax: fizzbuzz"); +}
diff --git a/src/gn.rs b/src/gn.rs new file mode 100644 index 0000000..d2479f4 --- /dev/null +++ b/src/gn.rs
@@ -0,0 +1,887 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::cfg::{cfg_to_gn_conditional, target_to_gn_conditional}; +use crate::target::GnTarget; +use crate::types::*; +use crate::{ + BinaryRenderOptions, CombinedTargetCfg, GlobalTargetCfgs, GroupVisibility, RuleRenaming, +}; +use anyhow::{Context, Result}; +use cargo_metadata::Package; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::fmt::Display; +use std::io::{self, Write}; +use std::path::Path; +use walkdir::WalkDir; + +/// Utility to add a version suffix to a GN target name. +pub fn add_version_suffix(prefix: &str, version: &impl ToString) -> String { + let mut accum = String::new(); + accum.push_str(&prefix); + accum.push_str("-v"); + accum.push_str(version.to_string().replace('.', "_").as_str()); + accum +} + +/// Write a header for the output GN file +pub fn write_header<W: io::Write>(output: &mut W, _cargo_file: &Path) -> Result<()> { + writeln!( + output, + include_str!("../templates/gn_header.template"), + // TODO set this, but in a way that tests don't fail on Jan 1st + year = "2020", + ) + .map_err(Into::into) +} + +pub fn write_fuchsia_sdk_metadata_header<W: io::Write>(output: &mut W) -> Result<()> { + writeln!(output, include_str!("../templates/gn_sdk_metadata_header.template"),) + .map_err(Into::into) +} + +/// Write an import stament for the output GN file +pub fn write_import<W: io::Write>(output: &mut W, file_name: &str) -> Result<()> { + writeln!(output, include_str!("../templates/gn_import.template"), file_name = file_name) + .map_err(Into::into) +} + +/// Writes rules at the top of the GN file that don't have the version appended +/// +/// Args: +/// - `rule_renaming`: if set, the renaming rules will be used to generate +/// appropriate group and rule names. See [RuleRenaming] for details. +pub fn write_top_level_rule<W: io::Write>( + output: &mut W, + platform: Option<&str>, + pkg: &Package, + group_visibility: Option<&GroupVisibility>, + rule_renaming: Option<&RuleRenaming>, + testonly: bool, + has_tests: bool, +) -> Result<()> { + let target_name = if pkg.is_proc_macro() { + format!("{}($host_toolchain)", pkg.gn_name()) + } else { + pkg.gn_name() + }; + if let Some(ref platform) = platform { + writeln!( + output, + "if ({conditional}) {{\n", + conditional = target_to_gn_conditional(platform)? + )?; + } + + let group_rule = rule_renaming.and_then(|t| t.group_name.as_deref()).unwrap_or("group"); + let optional_visibility = + group_visibility.map(|v| format!("visibility = {}", v.variable)).unwrap_or_default(); + writeln!( + output, + include_str!("../templates/top_level_gn_rule.template"), + group_name = pkg.name, + dep_name = target_name, + group_rule_name = group_rule, + optional_visibility = optional_visibility, + optional_testonly = if testonly { "testonly = true" } else { "" }, + )?; + + if has_tests { + let name = pkg.name.clone() + "-test"; + writeln!( + output, + include_str!("../templates/top_level_gn_rule.template"), + group_name = name, + group_rule_name = group_rule, + dep_name = target_name + "-test", + optional_visibility = optional_visibility, + optional_testonly = "testonly = true", + )?; + } + + if platform.is_some() { + writeln!(output, "}}\n")?; + } + Ok(()) +} + +/// Writes Fuchsia SDK metadata for a top-level rule. +pub fn write_fuchsia_sdk_metadata<W: io::Write>( + output: &mut W, + platform: Option<&str>, + pkg: &Package, + abs_dir: &Path, + rel_dir: &Path, +) -> Result<()> { + // TODO: add features, and registry + std::fs::create_dir_all(abs_dir) + .with_context(|| format!("while making directories for {}", abs_dir.display()))?; + + let file_name = format!("{}.sdk.meta.json", pkg.name); + let abs_path = abs_dir.join(&file_name); + let rel_path = rel_dir.join(&file_name); + let mut metadata_output = std::fs::File::create(&abs_path) + .with_context(|| format!("while writing {}", abs_path.display()))?; + writeln!( + metadata_output, + r#"{{ + "type": "{sdk_atom_type}", + "name": "{group_name}", + "version": "{version}" +}}"#, + sdk_atom_type = if pkg.is_proc_macro() { "rust_3p_proc_macro" } else { "rust_3p_library" }, + group_name = pkg.name, + version = pkg.version, + )?; + + let platform_constraint = if let Some(p) = platform { + format!(" && {}", target_to_gn_conditional(p)?) + } else { + "".to_owned() + }; + + writeln!( + output, + r#" if (_generating_sdk{constraint}) {{ + sdk_atom("{group_name}_sdk") {{ + id = "sdk://${{_sdk_prefix}}third_party/rust_crates/{group_name}" + category = "internal" + meta = {{ + source = "{source}" + dest = "${{_sdk_prefix}}third_party/rust_crates/{group_name}/meta.json" + schema = "3p_rust_library" + }} + }} + }} + "#, + constraint = platform_constraint, + source = rel_path.display(), + group_name = pkg.name, + )?; + + Ok(()) +} + +/// Writes rules at the top of the GN file that don't have the version appended +pub fn write_binary_top_level_rule<'a, W: io::Write>( + output: &mut W, + platform: Option<String>, + target: &GnTarget<'a>, + options: &BinaryRenderOptions<'_>, +) -> Result<()> { + if let Some(ref platform) = platform { + writeln!( + output, + "if ({conditional}) {{\n", + conditional = cfg_to_gn_conditional(platform)? + )?; + } + writeln!( + output, + include_str!("../templates/top_level_binary_gn_rule.template"), + group_name = options.binary_name, + dep_name = target.gn_target_name(), + optional_testonly = "", + )?; + + if options.tests_enabled { + let name = options.binary_name.to_owned() + "-test"; + let dep_name = target.gn_target_name() + "-test"; + writeln!( + output, + include_str!("../templates/top_level_binary_gn_rule.template"), + group_name = name, + dep_name = dep_name, + optional_testonly = "testonly = true", + )?; + } + + if platform.is_some() { + writeln!(output, "}}\n")?; + } + Ok(()) +} + +struct GnField { + ty: String, + exists: bool, + // Use BTreeMap so that iteration over platforms is stable. + add_fields: BTreeMap<Option<Platform>, Vec<String>>, + remove_fields: BTreeMap<Option<Platform>, Vec<String>>, +} +impl GnField { + /// If defining a new field in the template + pub fn new(ty: &str) -> GnField { + GnField { + ty: ty.to_string(), + exists: false, + add_fields: BTreeMap::new(), + remove_fields: BTreeMap::new(), + } + } + + /// If the field already exists in the template + pub fn exists(ty: &str) -> GnField { + GnField { exists: true, ..Self::new(ty) } + } + + pub fn add_platform_cfg<T: AsRef<str> + Display>(&mut self, platform: Option<String>, cfg: T) { + let field = self.add_fields.entry(platform).or_default(); + field.push(format!("\"{}\"", cfg)); + } + + pub fn remove_platform_cfg<T: AsRef<str> + Display>( + &mut self, + platform: Option<String>, + cfg: T, + ) { + let field = self.remove_fields.entry(platform).or_default(); + field.push(format!("\"{}\"", cfg)); + } + + pub fn add_cfg<T: AsRef<str> + Display>(&mut self, cfg: T) { + self.add_platform_cfg(None, cfg) + } + + pub fn remove_cfg<T: AsRef<str> + Display>(&mut self, cfg: T) { + self.remove_platform_cfg(None, cfg) + } + + pub fn render_gn(&self) -> String { + let mut output = if self.exists { + // We don't create an empty [] if the field already exists + match self.add_fields.get(&None) { + Some(add_fields) => format!("{} += [{}]\n", self.ty, add_fields.join(",")), + None => "".to_string(), + } + } else { + format!("{} = [{}]\n", self.ty, self.add_fields.get(&None).unwrap_or(&vec![]).join(",")) + }; + + // remove platfrom independent configs + if let Some(rm_fields) = self.remove_fields.get(&None) { + output.push_str(format!("{} -= [{}]\n", self.ty, rm_fields.join(",")).as_str()); + } + + // Add logic for specific platforms + for platform in self.add_fields.keys().filter(|k| k.is_some()) { + output.push_str( + format!( + "if ({}) {{\n", + cfg_to_gn_conditional(platform.as_ref().unwrap()).expect("valid cfg") + ) + .as_str(), + ); + output.push_str( + format!( + "{} += [{}]", + self.ty, + self.add_fields.get(platform).unwrap_or(&vec![]).join(",") + ) + .as_str(), + ); + output.push_str("}\n"); + } + + // Remove logic for specific platforms + for platform in self.remove_fields.keys().filter(|k| k.is_some()) { + output.push_str( + format!( + "if ({}) {{\n", + cfg_to_gn_conditional(platform.as_ref().unwrap()).expect("valid cfg") + ) + .as_str(), + ); + output.push_str( + format!( + "{} -= [{}]", + self.ty, + self.remove_fields.get(platform).unwrap_or(&vec![]).join(",") + ) + .as_str(), + ); + output.push_str("}\n"); + } + output + } +} + +/// Write a Target to the GN file. +/// +/// Includes information from the build script. +/// +/// Args: +/// - `renamed_rule`: if this is set, the rule that is written out is changed +/// to be the content of this arg. +pub fn write_rule<W: io::Write>( + output: &mut W, + target: &GnTarget<'_>, + project_root: &Path, + global_target_cfgs: Option<&GlobalTargetCfgs>, + custom_build: Option<&CombinedTargetCfg<'_>>, + output_name: Option<&str>, + is_testonly: bool, + is_test: bool, + renamed_rule: Option<&str>, + scan_for_licenses: bool, +) -> Result<()> { + // Generate a section for dependencies that is paramaterized on toolchain + let mut dependencies = String::from("deps = []\n"); + let mut aliased_deps = vec![]; + + // Stable output of platforms + let mut platform_deps: Vec<( + &Option<String>, + &Vec<(&cargo_metadata::Package, std::string::String)>, + )> = target.dependencies.iter().collect(); + platform_deps.sort_by(|p, p2| p.0.cmp(p2.0)); + + for (platform, deps) in platform_deps { + // sort for stable output + let mut deps = deps.clone(); + deps.sort_by(|a, b| (a.0).id.cmp(&(b.0).id)); + + // TODO(bwb) feed GN toolchain mapping in as a configuration to make more generic + match platform.as_ref().map(String::as_str) { + None => { + for pkg in deps { + dependencies.push_str(" deps += ["); + if pkg.0.is_proc_macro() { + dependencies.push_str( + format!("\":{}($host_toolchain)\"", pkg.0.gn_name()).as_str(), + ); + } else { + dependencies.push_str(format!("\":{}\"", pkg.0.gn_name()).as_str()); + } + dependencies.push_str("]\n"); + if pkg.0.name.replace("-", "_") != pkg.1 { + aliased_deps.push(format!("{} = \":{}\" ", pkg.1, pkg.0.gn_name())); + } + } + } + Some(platform) => { + dependencies.push_str( + format!("if ({}) {{\n", target_to_gn_conditional(platform)?).as_str(), + ); + for pkg in deps { + dependencies.push_str(" deps += ["); + if pkg.0.is_proc_macro() { + dependencies.push_str( + format!("\":{}($host_toolchain)\"", pkg.0.gn_name()).as_str(), + ); + } else { + dependencies.push_str(format!("\":{}\"", pkg.0.gn_name()).as_str()); + } + dependencies.push_str("]\n"); + + if pkg.0.name.replace("-", "_") != pkg.1 { + aliased_deps.push(format!("{} = \":{}\" ", pkg.1, pkg.0.gn_name())); + } + } + dependencies.push_str("}\n"); + } + } + } + + // write the features into the configs + let mut rustflags = GnField::new("rustflags"); + let mut rustenv = GnField::new("rustenv"); + let mut configs = GnField::exists("configs"); + let mut require_licenses = false; + + if let Some(global_cfg) = global_target_cfgs { + for cfg in &global_cfg.remove_cfgs { + configs.remove_cfg(cfg); + } + for cfg in &global_cfg.add_cfgs { + configs.add_cfg(cfg); + } + if let Some(ref require) = global_cfg.require_licenses { + require_licenses = *require; + } + } + + // Associate unique metadata with this crate + rustflags.add_cfg("--cap-lints=allow"); + rustflags.add_cfg(format!("--edition={}", target.edition)); + rustflags.add_cfg(format!("-Cmetadata={}", target.metadata_hash())); + rustflags.add_cfg(format!("-Cextra-filename=-{}", target.metadata_hash())); + if is_test { + rustflags.add_cfg("--test"); + } + + // Aggregate feature flags + for feature in target.features { + rustflags.add_cfg(format!("--cfg=feature=\\\"{}\\\"", feature)); + } + + // From the gn custom configs, add flags, env vars, and visibility + let mut visibility = vec![]; + + let mut uses_fuchsia_license = false; + + if let Some(custom_build) = custom_build { + for (platform, cfg) in custom_build { + if let Some(ref deps) = cfg.deps { + let build_deps = |dependencies: &mut String| { + for dep in deps { + dependencies.push_str(format!(" deps += [\"{}\"]", dep).as_str()); + } + }; + if let Some(platform) = platform { + dependencies.push_str( + format!("if ({}) {{\n", target_to_gn_conditional(platform)?).as_str(), + ); + build_deps(&mut dependencies); + dependencies.push_str("}\n"); + } else { + build_deps(&mut dependencies); + } + } + if let Some(ref flags) = cfg.rustflags { + for flag in flags { + rustflags.add_platform_cfg(platform.cloned(), flag.to_string()); + } + } + if let Some(ref env_vars) = cfg.env_vars { + for flag in env_vars { + rustenv.add_platform_cfg(platform.cloned(), flag.to_string()); + } + } + if let Some(ref crate_configs) = cfg.configs { + for config in crate_configs { + configs.add_platform_cfg(platform.cloned(), config); + } + } + if let Some(ref vis) = cfg.visibility { + visibility.extend(vis.iter().map(|v| format!(" visibility += [\"{}\"]", v))); + } + if let Some(ref uses) = cfg.uses_fuchsia_license { + uses_fuchsia_license = *uses; + } + } + } + + let visibility = if visibility.is_empty() { + String::from("visibility = [\":*\"]\n") + } else { + let mut v = String::from("visibility = []\n"); + v.extend(visibility); + v + }; + + // making the templates more readable. + let aliased_deps_str = if aliased_deps.is_empty() { + String::from("") + } else { + format!("aliased_deps = {{{}}}", aliased_deps.join("\n")) + }; + + // GN root relative path + let root_relative_path = format!( + "//{}", + target + .crate_root + .canonicalize_utf8() + .unwrap() + .strip_prefix(project_root) + .with_context(|| format!( + "{} is located outside of the project. Check your vendoring setup", + target.name() + ))? + .to_string() + ); + let output_name = if is_test { + output_name.map_or_else( + || { + Cow::Owned(format!( + "{}-{}-test", + target.name().replace('-', "_"), + target.metadata_hash() + )) + }, + |n| Cow::Owned(format!("{}-test", n)), + ) + } else { + output_name.map_or_else( + || { + Cow::Owned(format!( + "{}-{}", + target.name().replace('-', "_"), + target.metadata_hash() + )) + }, + Cow::Borrowed, + ) + }; + let mut target_name = target.gn_target_name(); + if is_test { + target_name.push_str("-test"); + } + + let optional_testonly = if is_testonly || is_test { "testonly = true" } else { "" }; + + let gn_rule = if let Some(renamed_rule) = renamed_rule { + renamed_rule.to_owned() + } else if is_test { + "executable".to_owned() + } else { + target.gn_target_type() + }; + + let mut license_files_found = false; + let mut applicable_licenses = GnField::new("applicable_licenses"); + let gn_crate_name = target.name().replace('-', "_"); + + if scan_for_licenses { + // Scan for LICENSE* files in the crate's root dir. + // Disabled in unit tests, where package_root always fails. + let mut license_files = vec![]; + + for entry in WalkDir::new(target.package_root()).follow_links(false).into_iter() { + let entry = entry.unwrap(); + let file_name_lower = entry.file_name().to_string_lossy().to_lowercase(); + if file_name_lower.starts_with("license") + || file_name_lower.starts_with("licence") + || file_name_lower.starts_with("copyright") + { + let license_file_label = format!( + "//{}", + entry + .path() + .canonicalize() + .unwrap() + .strip_prefix(project_root) + .unwrap() + .to_string_lossy() + ); + license_files.push(license_file_label); + license_files_found = true; + } + } + + if license_files_found { + if uses_fuchsia_license { + anyhow::bail!("ERROR: Crate {}.{} has license files but is set with uses_fuchsia_license = true", target.name(), target.version()) + } + + // Define a license target + let license_target_name = format!("{}.license", target_name); + // Directory iteration order is random, so sort alphabetically. + license_files.sort(); + let mut license_files_param = GnField::new("license_files"); + for entry in license_files { + license_files_param.add_cfg(entry); + } + + writeln!( + output, + include_str!("../templates/gn_license.template"), + target_name = license_target_name, + public_package_name = gn_crate_name, + license_files = license_files_param.render_gn(), + )?; + + applicable_licenses.add_cfg(format!(":{}", license_target_name)); + } else if uses_fuchsia_license || + // Empty crates are stubs crates authored by Fuchsia. + target.package_root().parent().unwrap().ends_with("third_party/rust_crates/empty") + { + applicable_licenses.add_cfg("//build/licenses:fuchsia_license"); + } else if require_licenses { + anyhow::bail!( + "ERROR: Crate at {} must have LICENSE* or COPYRIGHT files. + +Make sure such files are placed at the crate's root folder. + +Alternatively, if the crate's license is the same as Fuchsia's, +modify //third_party/rust_crates/Cargo.toml by adding: + +``` +[gn.package.{}.\"{}\"] +uses_fuchsia_license = true +```", + target.package_root(), + target.name(), + target.version() + ); + } + } + + writeln!( + output, + include_str!("../templates/gn_rule.template"), + gn_rule = gn_rule, + target_name = target_name, + crate_name = gn_crate_name, + output_name = output_name, + root_path = root_relative_path, + aliased_deps = aliased_deps_str, + dependencies = dependencies, + cfgs = configs.render_gn(), + rustenv = rustenv.render_gn(), + rustflags = rustflags.render_gn(), + visibility = visibility, + optional_testonly = optional_testonly, + applicable_licenses = applicable_licenses.render_gn(), + ) + .map_err(Into::into) +} + +#[cfg(test)] +mod tests { + use super::*; + use camino::Utf8Path; + use cargo_metadata::Edition; + use semver::Version; + use std::collections::HashMap; + + // WARNING: the expected output tests below have non-printable artifacts + // due to the way the templates are generated. Do not remove extra spaces + // in the quoted strings. + + #[test] + fn canonicalized_paths() { + let pkg_id = cargo_metadata::PackageId { repr: String::from("42") }; + let version = Version::new(0, 1, 0); + + let mut project_root = std::env::temp_dir(); + project_root.push(Path::new("canonicalized_paths")); + std::fs::write(&project_root, "").expect("write to temp file"); + + let target = GnTarget::new( + &pkg_id, + "test_target", + "test_package", + Edition::E2018, + Utf8Path::from_path(project_root.as_path()).unwrap(), + &version, + GnRustType::Library, + &[], + false, + HashMap::new(), + ); + + let not_prefix = Path::new("/foo"); + let prefix = std::env::temp_dir(); + + let mut output = vec![]; + assert!(write_rule( + &mut output, + &target, + not_prefix, + None, + None, + None, + false, + false, + None, + false + ) + .is_err()); + assert!(write_rule( + &mut output, + &target, + prefix.as_path(), + None, + None, + None, + false, + false, + None, + false, + ) + .is_ok()); + } + + #[test] + fn simple_target() { + let pkg_id = cargo_metadata::PackageId { repr: String::from("42") }; + let version = Version::new(0, 1, 0); + + let mut project_root = std::env::temp_dir(); + project_root.push(Path::new("simple_target")); + std::fs::write(&project_root, "").expect("write to temp file"); + + let target = GnTarget::new( + &pkg_id, + "test_target", + "test_package", + Edition::E2018, + Utf8Path::from_path(project_root.as_path()).unwrap(), + &version, + GnRustType::Library, + &[], + false, + HashMap::new(), + ); + + let mut output = vec![]; + write_rule( + &mut output, + &target, + std::env::temp_dir().as_path(), + None, + None, + None, + false, + false, + None, + false, + ) + .unwrap(); + let output = String::from_utf8(output).unwrap(); + assert_eq!( + output, + r#"rust_library("test_package-v0_1_0") { + crate_name = "test_target" + crate_root = "//simple_target" + output_name = "test_target-c5bf97c44457465a" + + deps = [] + + rustenv = [] + + rustflags = ["--cap-lints=allow","--edition=2018","-Cmetadata=c5bf97c44457465a","-Cextra-filename=-c5bf97c44457465a"] + + + visibility = [":*"] + + + applicable_licenses = [] + +} + +"# + ); + } + + #[test] + fn binary_target() { + let pkg_id = cargo_metadata::PackageId { repr: String::from("42") }; + let version = Version::new(0, 1, 0); + + let mut project_root = std::env::temp_dir(); + project_root.push(Path::new("binary_target")); + std::fs::write(&project_root, "").expect("write to temp file"); + + let target = GnTarget::new( + &pkg_id, + "test_target", + "test_package", + Edition::E2018, + Utf8Path::from_path(project_root.as_path()).unwrap(), + &version, + GnRustType::Binary, + &[], + false, + HashMap::new(), + ); + + let outname = Some("rainbow_binary"); + let mut output = vec![]; + write_rule( + &mut output, + &target, + std::env::temp_dir().as_path(), + None, + None, + outname, + false, + false, + None, + false, + ) + .unwrap(); + + let output = String::from_utf8(output).unwrap(); + assert_eq!( + output, + r#"executable("test_package-test_target-v0_1_0") { + crate_name = "test_target" + crate_root = "//binary_target" + output_name = "rainbow_binary" + + deps = [] + + rustenv = [] + + rustflags = ["--cap-lints=allow","--edition=2018","-Cmetadata=bf8f4a806276c599","-Cextra-filename=-bf8f4a806276c599"] + + + visibility = [":*"] + + + applicable_licenses = [] + +} + +"# + ); + } + + #[test] + fn renamed_target() { + let pkg_id = cargo_metadata::PackageId { repr: String::from("42") }; + let version = Version::new(0, 1, 0); + + let mut project_root = std::env::temp_dir(); + project_root.push(Path::new("renamed_target")); + std::fs::write(&project_root, "").expect("write to temp file"); + + let target = GnTarget::new( + &pkg_id, + "test_target", + "test_package", + Edition::E2018, + Utf8Path::from_path(project_root.as_path()).unwrap(), + &version, + GnRustType::Binary, + &[], + false, + HashMap::new(), + ); + + let outname = Some("rainbow_binary"); + let mut output = vec![]; + write_rule( + &mut output, + &target, + std::env::temp_dir().as_path(), + None, + None, + outname, + false, + false, + Some("renamed_rule"), + false, + ) + .unwrap(); + let output = String::from_utf8(output).unwrap(); + assert_eq!( + output, + r#"renamed_rule("test_package-test_target-v0_1_0") { + crate_name = "test_target" + crate_root = "//renamed_target" + output_name = "rainbow_binary" + + deps = [] + + rustenv = [] + + rustflags = ["--cap-lints=allow","--edition=2018","-Cmetadata=bf8f4a806276c599","-Cextra-filename=-bf8f4a806276c599"] + + + visibility = [":*"] + + + applicable_licenses = [] + +} + +"# + ); + } +}
diff --git a/src/graph.rs b/src/graph.rs new file mode 100644 index 0000000..34e4e24 --- /dev/null +++ b/src/graph.rs
@@ -0,0 +1,191 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::target::GnTarget; +use crate::types::*; +use anyhow::{anyhow, Context as _, Result}; +use cargo_metadata::{DependencyKind, Metadata, Package, PackageId}; +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; + +pub struct GnBuildGraph<'a> { + metadata: &'a Metadata, + // hashset because the same target can get added multiple times + targets: HashSet<GnTarget<'a>>, +} + +impl<'a> GnBuildGraph<'a> { + pub fn new(metadata: &'a Metadata) -> Self { + GnBuildGraph { metadata, targets: HashSet::new() } + } + + pub fn targets(&'a self) -> impl Iterator<Item = &'a GnTarget<'_>> { + self.targets.iter() + } + + pub fn find_library_target(&self, package: &str, version: &str) -> Option<&GnTarget<'_>> { + self.targets().find(|t| match t.target_type { + GnRustType::Library + | GnRustType::Rlib + | GnRustType::Staticlib + | GnRustType::Dylib + | GnRustType::Cdylib + | GnRustType::ProcMacro => t.pkg_name == package && t.version() == version, + _ => false, + }) + } + + pub fn find_binary_target( + &self, + package: &str, + version: &str, + target: &str, + ) -> Option<&GnTarget<'_>> { + self.targets().find(|t| match t.target_type { + GnRustType::Binary => { + t.pkg_name == package && t.version() == version && t.target_name == target + } + _ => false, + }) + } + + /// Add a cargo package to the target list. If the dependencies + /// are not already in the target graph, add them as well + pub fn add_cargo_package(&mut self, cargo_pkg_id: PackageId) -> Result<()> { + let package = &self.metadata[&cargo_pkg_id]; + for node in self + .metadata + .resolve + .as_ref() + .unwrap() + .nodes + .iter() + .filter(|node| node.id == cargo_pkg_id) + { + let mut dependencies = HashMap::<Option<Platform>, Vec<(&'a Package, String)>>::new(); + + // collect the dependency edges for this node + for node_dep in node.deps.iter() { + let id = &node_dep.pkg; + if node_dep.dep_kinds.is_empty() { + return Err(anyhow!("Must use rustc 1.41+ to use cargo-gnaw. Needs dependency kind information.")); + } + for kinds in &node_dep.dep_kinds { + let kind = &kinds.kind; + let target = &kinds.target; + match kind { + DependencyKind::Normal => { + self.add_cargo_package(id.clone())?; + let platform = target.as_ref().map(|x| format!("{}", x)); + let platform_deps = dependencies.entry(platform).or_default(); + + // Somehow we get duplicates of the same dependency + // from cargo on some targets. Filter those out + // here. This is technically quadratic, but that's + // unlikely to matter for the number of deps on a + // single crate. + if !platform_deps.iter().any(|dep| dep.1 == node_dep.name.clone()) { + platform_deps.push((&self.metadata[id], node_dep.name.clone())); + } + } + DependencyKind::Build => {} + DependencyKind::Development => {} + err => { + return Err(anyhow!( + "Don't know how to handle this kind of dependency edge: {:?}", + err + )) + } + } + } + } + + let has_build_script = package.targets.iter().any(|target| { + for kind in &target.kind { + let gn_rust_type = GnRustType::try_from(kind.as_str()) + .with_context(|| { + format!("Failed to resolve GN target type for: {:?}", target) + }) + .unwrap(); + + if gn_rust_type == GnRustType::BuildScript { + return true; + } + } + + false + }); + + for rust_target in package.targets.iter() { + for kind in &rust_target.kind { + let target_type = GnRustType::try_from(kind.as_str())?; + match target_type { + GnRustType::Library | GnRustType::Rlib | GnRustType::ProcMacro => { + let gn_target = GnTarget::new( + &node.id, + &rust_target.name, + &package.name, + package.edition, + &rust_target.src_path, + &package.version, + target_type, + node.features.as_slice(), + has_build_script, + dependencies.clone(), + ); + self.targets.insert(gn_target); + } + GnRustType::Binary => { + // If a crate contains both a library and binary targets, + // cargo implicitly adds a dependency from each binary to the library. + // + // This logic imitates that behaviour. + let mut deps = dependencies.clone(); + let maybe_library = package.targets.iter().find(|v| { + v.kind.iter().any(|kind| { + matches!( + GnRustType::try_from(kind.as_str()), + Ok(GnRustType::Library) + ) + }) + }); + if let Some(lib) = maybe_library { + deps.entry(None).or_default().push(( + &self.metadata[&node.id], + lib.name.clone().replace('-', "_"), + )); + } + + let gn_target = GnTarget::new( + &node.id, + &rust_target.name, + &package.name, + package.edition, + &rust_target.src_path, + &package.version, + target_type, + node.features.as_slice(), + has_build_script, + deps, + ); + self.targets.insert(gn_target); + } + + // FIXME(https://fxbug.dev/42173393): support staticlib, dylib, and + // cdylib crate types. + GnRustType::Staticlib | GnRustType::Dylib | GnRustType::Cdylib => (), + + // BuildScripts are handled as part of the targets + GnRustType::BuildScript => (), + + // TODO support building tests. Should integrate with whatever GN + // metadata collection system used by the main build. + GnRustType::Example | GnRustType::Bench | GnRustType::Test => (), + } + } + } + } + Ok(()) + } +}
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2570bb6 --- /dev/null +++ b/src/lib.rs
@@ -0,0 +1,830 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::build::BuildScript; +use crate::graph::GnBuildGraph; +use crate::target::GnTarget; +use crate::types::*; +use anyhow::{Context, Result}; +use argh::FromArgs; +use camino::Utf8PathBuf; +use cargo_metadata::{CargoOpt, DependencyKind, Package}; +use serde_derive::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fs::File; +use std::io::{self, Read, Write}; +use std::path::PathBuf; +use std::process::Command; + +mod build; +mod cfg; +mod gn; +mod graph; +mod target; +mod types; + +#[derive(FromArgs, Debug)] +/// Generate a GN manifest for your vendored Cargo dependencies. +pub struct Opt { + /// cargo manifest path + #[argh(option)] + manifest_path: PathBuf, + + /// root of GN project + #[argh(option)] + project_root: PathBuf, + + /// cargo binary to use (for vendored toolchains) + #[argh(option)] + cargo: Option<PathBuf>, + + /// already generated configs from cargo build scripts + #[argh(option, short = 'p')] + // TODO(https://fxbug.dev/42165549) + #[allow(unused)] + cargo_configs: Option<PathBuf>, + + /// location of GN file + #[argh(option, short = 'o')] + output: PathBuf, + + /// location of JSON file with crate metadata + #[argh(option)] + emit_metadata: Option<PathBuf>, + + /// location of GN binary to use for formating. + /// If no path is provided, no format will be run. + #[argh(option)] + gn_bin: Option<PathBuf>, + + /// don't generate a target for the root crate + #[argh(switch)] + skip_root: bool, + + /// run cargo with `--all-features` + #[argh(switch)] + all_features: bool, + + /// run cargo with `--no-default-features` + #[argh(switch)] + no_default_features: bool, + + /// run cargo with `--features <FEATURES>` + #[argh(option)] + features: Vec<String>, + + /// directory to emit Fuchsia SDK metadata atoms into + #[argh(option)] + output_fuchsia_sdk_metadata: Option<PathBuf>, +} + +type PackageName = String; +type TargetName = String; +type Version = String; + +/// Per-target metadata in the Cargo.toml for Rust crates that +/// require extra information to in the BUILD.gn +#[derive(Default, Clone, Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct TargetCfg { + /// Config flags for rustc. Ex: --cfg=std + rustflags: Option<Vec<String>>, + /// Environment variables. These are usually from Cargo or the + /// build.rs file in the crate. + env_vars: Option<Vec<String>>, + /// GN Targets that this crate should depend on. Generally for + /// crates that build C libraries and link against. + deps: Option<Vec<String>>, + /// GN Configs that this crate should depend on. Used to add + /// crate-specific configs. + configs: Option<Vec<String>>, + /// GN Visibility that controls which targets can depend on this target. + visibility: Option<Vec<String>>, + + // Whether the package uses the Fuchsia license. + uses_fuchsia_license: Option<bool>, +} + +/// Configuration for a single GN executable target to generate from a Cargo binary target. +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct BinaryCfg { + /// Name to use as both the top-level GN group target and the executable's output name. + output_name: String, + /// Binary target configuration for all platforms. + #[serde(default, flatten)] + default_cfg: TargetCfg, + /// Per-platform binary target configuration. + #[serde(default)] + #[serde(rename = "platform")] + platform_cfg: HashMap<Platform, TargetCfg>, +} + +/// Visibility list to use for a forwarding group +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct GroupVisibility { + /// .gni file to import which defines the variable + import: String, + /// Name of variable defined by the gni file containing the visibility list to use + variable: String, +} + +/// Defines a per-target rule for rule renaming. +/// +/// Some external crates require additional post-processing. For those we define custom rules, +/// and force a rename of the rule and the group so that post-processing can be done by GN. +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct RuleRenaming { + /// The label of the `.gni` file used to make the group and target renaming + /// available. + /// + /// Only a single file can be imported. This is on purpose so as + /// not to pollute the generated file with many imports. The same file will + /// be imported only once. + import: String, + /// If set, this name will be used instead of `group` for the top level group. + group_name: Option<String>, + /// If set, this rule name will be used instead of the name suggested by + /// the target type (e.g. `my_rule` instead of `rust_library`). + rule_name: Option<String>, +} + +/// Configuration for a Cargo package. Contains configuration for its (single) library target at the +/// top level and optionally zero or more binaries to generate. +#[derive(Default, Clone, Serialize, Deserialize, Debug)] +#[serde(default, deny_unknown_fields)] +pub struct PackageCfg { + /// Library target configuration for all platforms. + #[serde(flatten)] + default_cfg: TargetCfg, + /// Per-platform library target configuration. + #[serde(rename = "platform")] + platform_cfg: HashMap<Platform, TargetCfg>, + /// Configuration for GN binary targets to generate from one of the package's binary targets. + /// The map key identifies the cargo target name within this cargo package. + binary: HashMap<TargetName, BinaryCfg>, + /// List of cargo features which have been code reviewed for this cargo package + /// + /// Must be set if require_feature_reviews mentions this package. + reviewed_features: Option<Vec<String>>, + /// Visibility list to use for the forwarding group, for use with fixits which seek to remove + /// the use of a specific crate from the tree. + group_visibility: Option<GroupVisibility>, + /// Whether or not this target my only be used in tests. + testonly: Option<bool>, + /// Build tests for this target. + tests: bool, + /// Used to rename the group and target names used to bring this particular package + /// in. Normally, `cargo-gnaw` will use `group` and `rust_*` target name, but this + /// allows us to define custom replacement targets. An import is required, but the + /// feature is deliberately limited to just a single definition and a limited number + /// of renames, as it should be used in very special cases only. + target_renaming: Option<RuleRenaming>, +} + +/// Configs added to all GN targets in the BUILD.gn +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct GlobalTargetCfgs { + remove_cfgs: Vec<String>, + add_cfgs: Vec<String>, + /// When true, crates must have license files. + require_licenses: Option<bool>, +} + +/// Extra metadata in the Cargo.toml file that feeds into the +/// BUILD.gn file. +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +struct GnBuildMetadata { + /// global configs + config: Option<GlobalTargetCfgs>, + /// packages for which only some features will be code reviewed + #[serde(default)] + require_feature_reviews: HashSet<PackageName>, + /// map of per-Cargo package configuration + package: HashMap<PackageName, HashMap<Version, PackageCfg>>, +} + +impl GnBuildMetadata { + fn find_package<'a>(&'a self, pkg: &Package) -> Option<&'a PackageCfg> { + self.package.get(&pkg.name).and_then(|p| p.get(&pkg.version.to_string())) + } +} + +#[derive(Serialize, Deserialize, Debug)] +struct BuildMetadata { + gn: Option<GnBuildMetadata>, +} + +/// Used for identifying 3p owners via reverse dependencies. Ties together several pieces of +/// metadata needed to associate a GN target with an OWNERS file and vice versa. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd, Ord, Serialize)] +#[serde(deny_unknown_fields)] +pub struct CrateOutputMetadata { + /// Name of the crate as specified in Cargo.toml. + pub name: String, + + /// Version of the crate, used for disambiguating between duplicated transitive deps. + pub version: String, + + /// Full GN target for depending on the crate. + /// + /// For example, Rust targets all have a canonical target like + /// `//third_party/rust_crates:foo-v1_0_0`. + pub canonical_target: String, + + /// Shorthand GN target for depending on the crate. + /// + /// For example, Rust targets listed in `third_party/rust_crates/Cargo.toml` have a + /// shortcut target like `//third_party/rust_crates:foo`. + pub shortcut_target: Option<String>, + + /// Filesystem path to the directory containing `Cargo.toml`. + pub path: Utf8PathBuf, +} + +// Use BTreeMap so that iteration over platforms is stable. +type CombinedTargetCfg<'a> = BTreeMap<Option<&'a Platform>, &'a TargetCfg>; + +/// Render options for binary. +pub struct BinaryRenderOptions<'a> { + /// Name of the binary. + binary_name: &'a str, + /// If true, this binary target is a test target. + tests_enabled: bool, +} + +macro_rules! define_combined_cfg { + ($t:ty) => { + impl $t { + fn combined_target_cfg(&self) -> CombinedTargetCfg<'_> { + let mut combined: CombinedTargetCfg<'_> = + self.platform_cfg.iter().map(|(k, v)| (Some(k), v)).collect(); + assert!( + combined.insert(None, &self.default_cfg).is_none(), + "Default platform (None) already present in combined cfg" + ); + combined + } + } + }; +} +define_combined_cfg!(PackageCfg); +define_combined_cfg!(BinaryCfg); + +/// Writes out an import to the provided `output`. +/// +/// The import is written out only the first time it appears. `imported_files` +/// is updated so that the next appearance of the same import does not result +/// in a repeated import. +fn write_import_once<W: io::Write>( + mut output: &mut W, + imported_files: &mut HashSet<String>, + import: &String, +) -> Result<()> { + if !imported_files.contains(import) { + gn::write_import(&mut output, import).with_context(|| "writing import")?; + imported_files.insert(import.clone()); + } + Ok(()) +} + +pub fn generate_from_manifest<W: io::Write>(mut output: &mut W, opt: &Opt) -> Result<()> { + let manifest_path = &opt.manifest_path; + let path_from_root_to_generated = opt + .output + .parent() + .unwrap() + .strip_prefix(&opt.project_root) + .expect("--project-root must be a parent of --output"); + let fuchsia_sdk_metadata_paths = + opt.output_fuchsia_sdk_metadata.as_ref().map(|output_fuchsia_sdk_metadata| { + let path_from_root_to_generated = output_fuchsia_sdk_metadata + .strip_prefix(opt.output.parent().unwrap()) + .expect("--output_fuchsia_sdk_metadata must be in the same directory as --output"); + (output_fuchsia_sdk_metadata, path_from_root_to_generated) + }); + let mut emitted_metadata: Vec<CrateOutputMetadata> = Vec::new(); + let mut top_level_metadata: HashSet<String> = HashSet::new(); + let mut imported_files: HashSet<String> = HashSet::new(); + + // generate cargo metadata + let mut cmd = cargo_metadata::MetadataCommand::new(); + let parent_dir = manifest_path + .parent() + .with_context(|| format!("while parsing parent path: {}", manifest_path.display()))?; + cmd.current_dir(parent_dir); + cmd.manifest_path(manifest_path); + if let Some(ref cargo_path) = opt.cargo { + cmd.cargo_path(cargo_path); + } + if opt.all_features { + cmd.features(CargoOpt::AllFeatures); + } + if opt.no_default_features { + cmd.features(CargoOpt::NoDefaultFeatures); + } + if !opt.features.is_empty() { + cmd.features(CargoOpt::SomeFeatures(opt.features.clone())); + } + cmd.other_options([String::from("--frozen")]); + let metadata = cmd.exec().with_context(|| { + format!("while running cargo metadata: supplied cargo binary: {:?}", &opt.cargo) + })?; + + // read out custom gn commands from the toml file + let mut file = File::open(&manifest_path) + .with_context(|| format!("opening {}", manifest_path.display()))?; + let mut contents = String::new(); + file.read_to_string(&mut contents) + .with_context(|| format!("while reading manifest: {}", manifest_path.display()))?; + let metadata_configs: BuildMetadata = + toml::from_str(&contents).context("parsing manifest toml")?; + + gn::write_header(&mut output, manifest_path).context("writing header")?; + + if opt.output_fuchsia_sdk_metadata.is_some() { + gn::write_fuchsia_sdk_metadata_header(&mut output) + .context("writing Fuchsia SDK metadata header")?; + } + + // Construct a build graph of all the targets for GN + let mut build_graph = GnBuildGraph::new(&metadata); + match metadata.resolve.as_ref() { + Some(resolve) => { + let top_level_id = + resolve.root.as_ref().expect("the Cargo.toml file must define a package"); + if opt.skip_root { + let top_level_node = resolve + .nodes + .iter() + .find(|node| node.id == *top_level_id) + .expect("top level node not in node graph"); + for dep in &top_level_node.deps { + build_graph + .add_cargo_package(dep.pkg.clone()) + .context("adding cargo package")?; + for kinds in dep.dep_kinds.iter() { + if kinds.kind == DependencyKind::Normal { + let platform = kinds.target.as_ref().map(|t| format!("{}", t)); + let package = &metadata[&dep.pkg]; + top_level_metadata.insert(package.name.to_owned()); + let cfg = metadata_configs + .gn + .as_ref() + .and_then(|cfg| cfg.find_package(package)); + + let visibility = cfg.and_then(|cfg| cfg.group_visibility.as_ref()); + if let Some(visibility) = visibility { + write_import_once( + &mut output, + &mut imported_files, + &visibility.import, + )?; + } + + let target_renaming = cfg.and_then(|cfg| cfg.target_renaming.as_ref()); + if let Some(target_renaming) = target_renaming { + write_import_once( + &mut output, + &mut imported_files, + &target_renaming.import, + )?; + } + + gn::write_top_level_rule( + &mut output, + platform.as_deref(), + package, + visibility, + target_renaming, + cfg.and_then(|cfg| cfg.testonly).unwrap_or(false), + cfg.map(|c| c.tests).unwrap_or(false), + ) + .with_context(|| { + format!("while writing top level rule for package: {}", &dep.pkg) + }) + .context("writing top level rule")?; + + if let Some((abs_path, rel_path)) = &fuchsia_sdk_metadata_paths { + gn::write_fuchsia_sdk_metadata( + &mut output, + platform.as_deref(), + package, + abs_path, + rel_path, + ) + .with_context(|| { + format!( + "while writing top level rule for package: {}", + &dep.pkg + ) + }) + .context("writing Fuchsia SDK metadata for top level rule")?; + } + } + } + } + } else { + build_graph + .add_cargo_package(top_level_id.clone()) + .with_context(|| "could not add cargo package")?; + let package = &metadata[&top_level_id]; + top_level_metadata.insert(package.name.to_owned()); + let cfg = metadata_configs.gn.as_ref().and_then(|cfg| cfg.find_package(package)); + + let visibility = cfg.and_then(|cfg| cfg.group_visibility.as_ref()); + if let Some(visibility) = visibility { + write_import_once(&mut output, &mut imported_files, &visibility.import)?; + } + + let target_renaming = cfg.and_then(|cfg| cfg.target_renaming.as_ref()); + if let Some(target_renaming) = target_renaming { + write_import_once(&mut output, &mut imported_files, &target_renaming.import)?; + } + + gn::write_top_level_rule( + &mut output, + None, + package, + visibility, + target_renaming, + cfg.and_then(|cfg| cfg.testonly).unwrap_or(false), + cfg.map(|c| c.tests).unwrap_or(false), + ) + .with_context(|| "writing top level rule")?; + + if let Some((abs_path, rel_path)) = &fuchsia_sdk_metadata_paths { + gn::write_fuchsia_sdk_metadata(&mut output, None, package, abs_path, rel_path) + .with_context(|| "writing Fuchsia SDK metadata for top level rule")?; + } + } + } + None => anyhow::bail!("Failed to resolve a build graph for the package tree"), + } + + // Sort targets for stable output to minimize diff churn + let mut graph_targets: Vec<&GnTarget<'_>> = build_graph.targets().collect(); + graph_targets.sort(); + + let global_config = match metadata_configs.gn { + Some(ref gn_configs) => gn_configs.config.as_ref(), + None => None, + }; + + let empty_hash_set = &HashSet::new(); + let require_feature_reviews = metadata_configs + .gn + .as_ref() + .map(|gn| &gn.require_feature_reviews) + .unwrap_or(empty_hash_set); + + // Grab the per-package configs. + let gn_pkg_cfgs = metadata_configs.gn.as_ref().map(|i| &i.package); + + // Iterate through the target configs, verifying that the build graph contains the configured + // targets, then save off a mapping of GnTarget to the target config. + let mut target_cfgs = HashMap::<&GnTarget<'_>, CombinedTargetCfg<'_>>::new(); + let mut target_binaries = HashMap::<&GnTarget<'_>, BinaryRenderOptions<'_>>::new(); + let mut testonly_targets = HashSet::<&GnTarget<'_>>::new(); + let mut targets_with_tests = HashSet::<&GnTarget<'_>>::new(); + let mut reviewed_features_map = HashMap::<&GnTarget<'_>, Option<&[String]>>::new(); + // An entry for each target that needs a renamed rule. There is no connection between + // PackageCfg and GnTarget, so to use the setting from PackageCfg, we need to build this map as + // we iterate through targets. + let mut renamed_rules = HashMap::<&GnTarget<'_>, &'_ str>::new(); + let mut unused_configs = String::new(); + if let Some(gn_pkg_cfgs) = gn_pkg_cfgs { + for (pkg_name, versions) in gn_pkg_cfgs { + for (pkg_version, pkg_cfg) in versions { + // Search the build graph for the library target. + if let Some(target) = build_graph.find_library_target(pkg_name, pkg_version) { + assert!( + target_cfgs.insert(target, pkg_cfg.combined_target_cfg()).is_none(), + "Duplicate library config somehow specified" + ); + assert!( + reviewed_features_map + .insert(target, pkg_cfg.reviewed_features.as_deref()) + .is_none(), + "Duplicate library config somehow specified" + ); + + if let Some(renamed_rule) = pkg_cfg + .target_renaming + .as_ref() + .and_then(|r| r.rule_name.as_ref()) + .map(|x| x.as_str()) + { + renamed_rules.insert(target, renamed_rule); + } + + if pkg_cfg.testonly == Some(true) { + testonly_targets.insert(target); + } + + if pkg_cfg.tests { + targets_with_tests.insert(target); + } + } else { + unused_configs.push_str(&format!( + "library crate, package {} version {}\n", + pkg_name, pkg_version + )); + } + + // Handle binaries that should be built for this package, similarly searching the + // build graph for the binary targets. + for (bin_cargo_target, bin_cfg) in &pkg_cfg.binary { + if let Some(target) = + build_graph.find_binary_target(pkg_name, pkg_version, bin_cargo_target) + { + if let Some(old_options) = target_binaries.insert( + target, + BinaryRenderOptions { + binary_name: &bin_cfg.output_name, + tests_enabled: pkg_cfg.tests, + }, + ) { + anyhow::bail!( + "A given binary target ({} in package {} version {}) can only be \ + used for a single GN target, but multiple exist, including {} \ + and {}", + bin_cargo_target, + pkg_name, + pkg_version, + &bin_cfg.output_name, + old_options.binary_name, + ); + } + assert!( + target_cfgs.insert(target, bin_cfg.combined_target_cfg()).is_none(), + "Should have bailed above" + ); + + if pkg_cfg.tests { + targets_with_tests.insert(target); + } + } else { + unused_configs.push_str(&format!( + "binary crate {}, package {} version {}\n", + bin_cargo_target, pkg_name, pkg_version + )); + } + } + } + } + } + if !unused_configs.is_empty() { + anyhow::bail!( + "GNaw config exists for crates that were not found in the Cargo build graph:\n\n{}", + unused_configs + ); + } + + // Write the top-level GN rules for binaries. Verify that the names are unique, otherwise a + // build failure will result. + { + let mut names = HashSet::new(); + for (target, options) in &target_binaries { + if !names.insert(options.binary_name) { + anyhow::bail!( + "Multiple targets are configured to generate executables named \"{}\"", + options.binary_name + ); + } + + emitted_metadata.push(CrateOutputMetadata { + name: options.binary_name.to_string(), + version: target.version(), + canonical_target: format!( + "//{}:{}", + path_from_root_to_generated.display(), + target.gn_target_name() + ), + shortcut_target: Some(format!( + "//{}:{}", + path_from_root_to_generated.display(), + options.binary_name + )), + path: target.package_root(), + }); + gn::write_binary_top_level_rule(&mut output, None, target, options) + .context(format!("writing binary top level rule: {}", target.name()))?; + } + } + + // Write out a GN rule for each target in the build graph + for target in graph_targets { + // Check whether we should generate a target if this is a binary. + let binary_name = if let GnRustType::Binary = target.target_type { + let name = target_binaries.get(target).map(|opt| opt.binary_name); + if name.is_none() { + continue; + } + name + } else { + None + }; + + let target_cfg = target_cfgs.get(target); + if target.has_build_script && target_cfg.is_none() { + let build_output = BuildScript::execute(target); + match build_output { + Ok(rules) => { + anyhow::bail!( + "Add this to your Cargo.toml located at {}:\n\ + [gn.package.{}.\"{}\"]\n\ + rustflags = [{}]\n\ + rustenv = [{}]", + manifest_path.display(), + target.name(), + target.version(), + rules.rustflags.join(", "), + rules.rustenv.join(", ") + ); + } + Err(err) => anyhow::bail!( + "{} {} uses a build script but no section defined in the GN section \ + nor can we automatically generate the appropriate rules:\n{}", + target.name(), + target.version(), + err, + ), + } + } + + // Check to see if we need to review individual features for this crate + let reviewed_features = reviewed_features_map.get(target); + if require_feature_reviews.contains(&target.name()) { + match reviewed_features { + Some(Some(_)) => {} + _ => { + anyhow::bail!( + "{name} {version} requires feature review but reviewed features not found.\n\n\ + Make sure to conduct code review assuming the following features are enabled, \ + and then add this to your Cargo.toml located at {manifest_path}:\n\ + [gn.package.{name}.\"{version}\"]\n\ + reviewed_features = {features}", + manifest_path = manifest_path.display(), + name = target.name(), + version = target.version(), + features = toml::to_string(target.features).unwrap() + ); + } + } + } else if let Some(Some(_)) = reviewed_features { + anyhow::bail!( + "{name} {version} sets reviewed_features but {name} was not found in \ + require_feature_reviews.\n\n\ + Make sure to add it there so that reviewed_features is not accidentally \ + removed during future crate version bumps.", + name = target.name(), + version = target.version(), + ); + } + + if let Some(&Some(reviewed_features)) = reviewed_features { + let reviewed_features_set = + reviewed_features.iter().map(|s| s.as_str()).collect::<HashSet<_>>(); + let unreviewed_features = target + .features + .iter() + .filter(|f| !reviewed_features_set.contains(f.as_str())) + .collect::<Vec<_>>(); + if !unreviewed_features.is_empty() { + anyhow::bail!( + "{name} {version} is included with unreviewed features {unreviewed:?}\n\n\ + Make sure to additionally review code gated by these features, then add them \ + to reviewed_features under [gn.package.{name}.\"{version}\"] in {manifest_path}", + name = target.name(), + version = target.version(), + unreviewed = unreviewed_features, + manifest_path = manifest_path.display(), + ) + } + } + + let package_root = target.package_root(); + package_root.strip_prefix(&opt.project_root).unwrap_or_else(|e| { + panic!( + "{}: {} is not under the project_root ({})", + e, + target.package_root(), + opt.project_root.display() + ) + }); + + let shortcut_target = if top_level_metadata.contains(target.pkg_name) { + Some(format!("//{}:{}", path_from_root_to_generated.display(), target.pkg_name)) + } else { + None + }; + + emitted_metadata.push(CrateOutputMetadata { + name: target.name(), + version: target.version(), + canonical_target: format!( + "//{}:{}", + path_from_root_to_generated.display(), + target.gn_target_name() + ), + shortcut_target, + path: package_root.to_owned(), + }); + + gn::write_rule( + &mut output, + target, + &opt.project_root, + global_config, + target_cfg, + binary_name, + testonly_targets.contains(target), + false, + renamed_rules.get(target).copied(), + true, + ) + .with_context(|| format!("writing rule for: {} {}", target.name(), target.version()))?; + + if targets_with_tests.contains(target) { + gn::write_rule( + &mut output, + target, + &opt.project_root, + global_config, + target_cfg, + binary_name, + true, + true, + renamed_rules.get(&target).copied(), + true, + ) + .with_context(|| format!("writing rule for: {} {}", target.name(), target.version()))?; + } + } + + if let Some(metadata_path) = &opt.emit_metadata { + eprintln!("Emitting external crate metadata: {}", metadata_path.display()); + emitted_metadata.sort(); + let metadata_json = serde_json::to_string_pretty(&emitted_metadata) + .context("serializing metadata to json")?; + std::fs::create_dir_all( + metadata_path + .parent() + .expect("The metadata path must include a valid parent directory"), + )?; + std::fs::write(metadata_path, &metadata_json).context("writing metadata file")?; + } + + Ok(()) +} + +pub fn run(args: &[impl AsRef<str>]) -> Result<()> { + // Check if running through cargo or stand-alone before arg parsing + let mut strs: Vec<&str> = args.iter().map(|s| s.as_ref()).collect(); + if strs.get(1) == Some(&"gnaw") { + // If the second command is "gnaw" this likely invoked by `cargo gnaw` + // shift all args by one. + strs = strs[1..].to_vec() + } + let opt = match Opt::from_args(&[strs[0]], &strs[1..]) { + Ok(opt) => opt, + Err(e) if e.status.is_ok() => { + println!("{}", e.output); + return Ok(()); + } + Err(e) => return Err(anyhow::Error::msg(e.output)), + }; + + eprintln!("Generating GN file from {}", opt.manifest_path.to_string_lossy()); + + // redirect to stdout if no GN output file specified + // Stores data in a buffer in-case to prevent creating bad BUILD.gn + let mut gn_output_buffer = vec![]; + generate_from_manifest(&mut gn_output_buffer, &opt).context("generating manifest")?; + + // Write the file buffer to an actual file + File::create(&opt.output) + .context("creating output file")? + .write_all(&gn_output_buffer) + .context("writing output contents")?; + + if let Some(gn_bin) = opt.gn_bin { + eprintln!("Formatting output file: {}", opt.output.display()); + let status = Command::new(&gn_bin) + .arg("format") + .arg(opt.output) + .status() + .with_context(|| format!("could not spawn GN: {}", gn_bin.display()))?; + if !status.success() { + anyhow::bail!("GN format command failed:{:?}", status); + } + } + + Ok(()) +}
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4600346 --- /dev/null +++ b/src/main.rs
@@ -0,0 +1,10 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use anyhow::Result; + +fn main() -> Result<()> { + let args: Vec<String> = std::env::args().collect(); + gnaw_lib::run(&args[..]) +}
diff --git a/src/target.rs b/src/target.rs new file mode 100644 index 0000000..46d0777 --- /dev/null +++ b/src/target.rs
@@ -0,0 +1,170 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::gn::add_version_suffix; +use crate::types::*; +use camino::{Utf8Path, Utf8PathBuf}; +use cargo_metadata::{Edition, Package, PackageId}; +use semver::Version; +use std::borrow::Cow; +use std::cmp::Ordering; +use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; + +pub struct GnTarget<'a> { + /// Package ID from the Cargo metadata + cargo_pkg_id: &'a PackageId, + /// Version of the Package from Cargo + version: &'a Version, + /// Name of the target given in Cargo.toml + pub target_name: &'a str, + /// Name of the package given in Cargo.toml + pub pkg_name: &'a str, + /// Path to the root of the crate + pub crate_root: &'a Utf8Path, + /// Rust Edition of the target + /// rustc: --edition + pub edition: Edition, + /// Type of crate + /// rustc: --crate-type + pub target_type: GnRustType, + /// Rust features enabled on this target + /// rustc: --cfg=feature=<string> + pub features: &'a [String], + /// Target depends on Cargo running a custom build-script + pub has_build_script: bool, + /// Target depends on Cargo running a custom build-script + pub dependencies: HashMap<Option<Platform>, Vec<(&'a Package, String)>>, +} + +impl std::fmt::Debug for GnTarget<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let display_name = if self.target_name != self.pkg_name { + Cow::Owned(format!("{}.{}", self.pkg_name, self.target_name)) + } else { + Cow::Borrowed(self.pkg_name) + }; + if self.has_build_script { + write!(f, "{:?} with custom-build: {}", self.target_type, display_name) + } else { + write!(f, "{:?}: {}", self.target_type, display_name) + } + } +} + +impl PartialEq for GnTarget<'_> { + fn eq(&self, other: &Self) -> bool { + self.cargo_pkg_id == other.cargo_pkg_id + && self.target_name == other.target_name + && self.target_type == other.target_type + } +} +impl Eq for GnTarget<'_> {} + +impl PartialOrd for GnTarget<'_> { + fn partial_cmp(&self, other: &GnTarget<'_>) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for GnTarget<'_> { + fn cmp(&self, other: &GnTarget<'_>) -> Ordering { + self.target_name + .cmp(other.target_name) + .then(self.target_type.cmp(&other.target_type)) + .then(self.cargo_pkg_id.cmp(other.cargo_pkg_id)) + } +} + +impl Hash for GnTarget<'_> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.cargo_pkg_id.hash(state); + self.target_name.hash(state); + self.target_type.hash(state); + } +} + +impl<'a> GnTarget<'a> { + pub fn new( + cargo_pkg_id: &'a PackageId, + target_name: &'a str, + pkg_name: &'a str, + edition: Edition, + crate_root: &'a Utf8Path, + version: &'a Version, + target_type: GnRustType, + features: &'a [Feature], + has_build_script: bool, + dependencies: HashMap<Option<Platform>, Vec<(&'a Package, String)>>, + ) -> Self { + GnTarget { + cargo_pkg_id, + target_name, + pkg_name, + edition, + crate_root, + version, + target_type, + features, + has_build_script, + dependencies, + } + } + + /// Name of the target given in Cargo.toml + pub fn name(&self) -> String { + self.target_name.to_owned() + } + + /// Version of the Package from Cargo + pub fn version(&self) -> String { + self.version.to_string() + } + + pub fn metadata_hash(&self) -> String { + let mut hasher = DefaultHasher::new(); + self.gn_target_name().hash(&mut hasher); + format!("{:x}", hasher.finish()) + } + + /// with version + pub fn gn_target_name(&self) -> String { + let prefix = match self.target_type { + GnRustType::Library | GnRustType::Rlib | GnRustType::ProcMacro => { + Cow::Borrowed(self.pkg_name) + } + GnRustType::Binary => Cow::Owned(format!("{}-{}", self.pkg_name, self.target_name)), + ty => panic!("Don't know how to represent this type \"{:?}\" in GN", ty), + }; + add_version_suffix(&prefix, &self.version) + } + + pub fn package_root(&self) -> Utf8PathBuf { + let mut package_root = self.crate_root.canonicalize_utf8().unwrap(); + + while !package_root.join("Cargo.toml").exists() { + package_root = package_root + .parent() + .expect("searching up from the crate root we must find a cargo.toml") + .to_path_buf(); + } + + assert!( + !package_root.ends_with("third_party/rust_crates"), + "must find a cargo.toml before the root one" + ); + + package_root.to_owned() + } + + pub fn gn_target_type(&self) -> String { + match self.target_type { + GnRustType::Library | GnRustType::Rlib => String::from("rust_library"), + GnRustType::Binary => String::from("executable"), + GnRustType::ProcMacro => String::from("rust_proc_macro"), + ty => panic!("Don't know how to represent this type \"{:?}\" in GN", ty), + } + } +}
diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..6a035d4 --- /dev/null +++ b/src/types.rs
@@ -0,0 +1,69 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use crate::gn::add_version_suffix; +use anyhow::{anyhow, Error}; +use cargo_metadata::Package; +use std::convert::TryFrom; + +pub type Feature = String; +pub type Platform = String; + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum GnRustType { + ProcMacro, + Library, + Rlib, + Staticlib, + Dylib, + Cdylib, + Binary, + Example, + Test, + Bench, + BuildScript, +} + +impl<'a> TryFrom<&'a str> for GnRustType { + type Error = Error; + + fn try_from(value: &str) -> Result<Self, Self::Error> { + match value { + "bin" => Ok(GnRustType::Binary), + "lib" => Ok(GnRustType::Library), + "rlib" => Ok(GnRustType::Rlib), + "staticlib" => Ok(GnRustType::Staticlib), + "dylib" => Ok(GnRustType::Dylib), + "cdylib" => Ok(GnRustType::Cdylib), + "proc-macro" => Ok(GnRustType::ProcMacro), + "test" => Ok(GnRustType::Test), + "example" => Ok(GnRustType::Example), + "bench" => Ok(GnRustType::Bench), + "custom-build" => Ok(GnRustType::BuildScript), + value => Err(anyhow!("unknown crate type: {}", value)), + } + } +} + +pub trait GnData { + fn gn_name(&self) -> String; + fn is_proc_macro(&self) -> bool; +} + +impl GnData for Package { + fn gn_name(&self) -> String { + add_version_suffix(&self.name, &self.version) + } + + fn is_proc_macro(&self) -> bool { + for target in &self.targets { + for kind in &target.kind { + if kind == "proc-macro" { + return true; + } + } + } + false + } +}
diff --git a/templates/gn_header.template b/templates/gn_header.template new file mode 100644 index 0000000..9edf89d --- /dev/null +++ b/templates/gn_header.template
@@ -0,0 +1,11 @@ +# Copyright {year} The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed(["gn_source_root"])
diff --git a/templates/gn_import.template b/templates/gn_import.template new file mode 100644 index 0000000..f9d091f --- /dev/null +++ b/templates/gn_import.template
@@ -0,0 +1 @@ +import("{file_name}")
diff --git a/templates/gn_license.template b/templates/gn_license.template new file mode 100644 index 0000000..d44999e --- /dev/null +++ b/templates/gn_license.template
@@ -0,0 +1,4 @@ +license("{target_name}") {{ + public_package_name = "{public_package_name}" + {license_files} +}}
diff --git a/templates/gn_rule.template b/templates/gn_rule.template new file mode 100644 index 0000000..38040c3 --- /dev/null +++ b/templates/gn_rule.template
@@ -0,0 +1,13 @@ +{gn_rule}("{target_name}") {{ + crate_name = "{crate_name}" + crate_root = "{root_path}" + output_name = "{output_name}" + {cfgs} + {dependencies} + {rustenv} + {rustflags} + {aliased_deps} + {visibility} + {optional_testonly} + {applicable_licenses} +}}
diff --git a/templates/gn_sdk_metadata_header.template b/templates/gn_sdk_metadata_header.template new file mode 100644 index 0000000..7abba7c --- /dev/null +++ b/templates/gn_sdk_metadata_header.template
@@ -0,0 +1,14 @@ +import("//build/sdk/sdk_atom.gni") + +# True if the current GN toolchain instance can be used to create +# Rust sdk_atom() targets. +_generating_sdk = false + +# The SDK prefix for the current toolchain. +if (current_toolchain == default_toolchain) {{ + _generating_sdk = true + _sdk_prefix = "" +}} else if (is_host) {{ + _generating_sdk = true + _sdk_prefix = "rust_proc_macros/" +}}
diff --git a/templates/top_level_binary_gn_rule.template b/templates/top_level_binary_gn_rule.template new file mode 100644 index 0000000..f7a7ec5 --- /dev/null +++ b/templates/top_level_binary_gn_rule.template
@@ -0,0 +1,4 @@ +group("{group_name}") {{ + public_deps = [":{dep_name}"] + {optional_testonly} +}}
diff --git a/templates/top_level_gn_rule.template b/templates/top_level_gn_rule.template new file mode 100644 index 0000000..878377f --- /dev/null +++ b/templates/top_level_gn_rule.template
@@ -0,0 +1,5 @@ +{group_rule_name}("{group_name}") {{ + public_deps = [":{dep_name}"] + {optional_testonly} + {optional_visibility} +}}
diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..e36b973 --- /dev/null +++ b/tests/.gitignore
@@ -0,0 +1,3 @@ +!Cargo.toml +!Cargo.lock +!.cargo
diff --git a/tests/BUILD.gn b/tests/BUILD.gn new file mode 100644 index 0000000..459c95b --- /dev/null +++ b/tests/BUILD.gn
@@ -0,0 +1,227 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/rust/rustc_binary.gni") +import("//build/rust/rustc_library.gni") +import("//build/rust/rustc_test.gni") +import("//build/testing/host_test_data.gni") + +group("tests") { + testonly = true + deps = [ ":cargo_gnaw_golden_integration_host_test($host_toolchain)" ] +} + +if (is_host) { + host_test("cargo_gnaw_golden_integration_host_test") { + binary_path = "$root_out_dir/cargo_gnaw_golden_integration_test" + + args = [ + "--test-base-dir", + rebase_path(".", root_build_dir), + "--rustc-binary-path", + "$out_rustc_prefix/bin/rustc", + "--gn-binary-path", + rebase_path("//prebuilt/third_party/gn/${host_platform}/gn", + root_build_dir), + "--cargo-binary-path", + "$out_rustc_prefix/bin/cargo", + "--lib-path", + "$out_rustc_prefix/lib", + ] + + # Host tests are tested on different workers from those the artifacts were + # built on. This means, if we want to use binaries from the source tree, + # we need to add them to test dependencies explicitly. + deps = [ + ":cargo_gnaw_golden_integration_test", + ":cargo_gnaw_prebuilt_bin_libs", + ":cargo_gnaw_tests", + "//build/rust:prebuilt_toolchain_host_test_data", + ] + } + + rustc_binary("cargo_gnaw_golden_integration_test") { + edition = "2021" + source_root = "golden.rs" + sources = [ "golden.rs" ] + deps = [ + "//third_party/rust_crates:anyhow", + "//third_party/rust_crates:argh", + "//third_party/rust_crates:pretty_assertions", + "//third_party/rust_crates:tempfile", + "//third_party/rust_crates:walkdir", + "//tools/cargo-gnaw:cargo-gnaw-lib", + ] + } + + visibility = [ ":*" ] + + host_test_data("cargo_gnaw_tests") { + sources = [ + "binary/BUILD.gn", + "binary/Cargo.lock", + "binary/Cargo.toml", + "binary/src/lib.rs", + "binary/src/main.rs", + "binary_with_tests/BUILD.gn", + "binary_with_tests/Cargo.lock", + "binary_with_tests/Cargo.toml", + "binary_with_tests/src/lib.rs", + "binary_with_tests/src/main.rs", + "cargo_features/BUILD-all-features.gn", + "cargo_features/BUILD-default.gn", + "cargo_features/BUILD-featurefoo.gn", + "cargo_features/BUILD-no-default-features.gn", + "cargo_features/Cargo.lock", + "cargo_features/Cargo.toml", + "cargo_features/src/lib.rs", + "cargo_features/sub-crate-default/Cargo.toml", + "cargo_features/sub-crate-default/src/lib.rs", + "cargo_features/sub-crate-for-feature/Cargo.toml", + "cargo_features/sub-crate-for-feature/src/lib.rs", + "cargo_features/sub-crate-non-default/Cargo.toml", + "cargo_features/sub-crate-non-default/src/lib.rs", + "cargo_features/sub-crate-with-feature/Cargo.toml", + "cargo_features/sub-crate-with-feature/src/lib.rs", + "feature_review/BUILD.gn", + "feature_review/Cargo.lock", + "feature_review/Cargo.toml", + "feature_review/Cargo_extra_review.toml", + "feature_review/Cargo_missing_review.toml", + "feature_review/Cargo_unreviewed_feature.toml", + "feature_review/crate_with_features/Cargo.toml", + "feature_review/crate_with_features/src/lib.rs", + "feature_review/needs_feature/Cargo.toml", + "feature_review/needs_feature/src/lib.rs", + "feature_review/src/lib.rs", + "multiple_crate_types/BUILD.gn", + "multiple_crate_types/Cargo.lock", + "multiple_crate_types/Cargo.toml", + "multiple_crate_types/src/lib.rs", + "multiple_crate_types/sub-crate-with-cdylib/Cargo.toml", + "multiple_crate_types/sub-crate-with-cdylib/src/lib.rs", + "multiple_crate_types/sub-crate-with-dylib/Cargo.toml", + "multiple_crate_types/sub-crate-with-dylib/src/lib.rs", + "multiple_crate_types/sub-crate-with-rlib/Cargo.toml", + "multiple_crate_types/sub-crate-with-rlib/src/lib.rs", + "platform_deps/.cargo/config", + "platform_deps/BUILD.gn", + "platform_deps/Cargo.lock", + "platform_deps/Cargo.toml", + "platform_deps/src/lib.rs", + "platform_deps/vendor/anyhow/.cargo-checksum.json", + "platform_deps/vendor/anyhow/Cargo.toml", + "platform_deps/vendor/anyhow/LICENSE-APACHE", + "platform_deps/vendor/anyhow/LICENSE-MIT", + "platform_deps/vendor/anyhow/README.md", + "platform_deps/vendor/anyhow/build.rs", + "platform_deps/vendor/anyhow/src/backtrace.rs", + "platform_deps/vendor/anyhow/src/chain.rs", + "platform_deps/vendor/anyhow/src/context.rs", + "platform_deps/vendor/anyhow/src/error.rs", + "platform_deps/vendor/anyhow/src/fmt.rs", + "platform_deps/vendor/anyhow/src/kind.rs", + "platform_deps/vendor/anyhow/src/lib.rs", + "platform_deps/vendor/anyhow/src/macros.rs", + "platform_deps/vendor/anyhow/src/wrapper.rs", + "platform_deps/vendor/anyhow/tests/common/mod.rs", + "platform_deps/vendor/anyhow/tests/compiletest.rs", + "platform_deps/vendor/anyhow/tests/drop/mod.rs", + "platform_deps/vendor/anyhow/tests/test_autotrait.rs", + "platform_deps/vendor/anyhow/tests/test_backtrace.rs", + "platform_deps/vendor/anyhow/tests/test_boxed.rs", + "platform_deps/vendor/anyhow/tests/test_chain.rs", + "platform_deps/vendor/anyhow/tests/test_context.rs", + "platform_deps/vendor/anyhow/tests/test_convert.rs", + "platform_deps/vendor/anyhow/tests/test_downcast.rs", + "platform_deps/vendor/anyhow/tests/test_fmt.rs", + "platform_deps/vendor/anyhow/tests/test_macros.rs", + "platform_deps/vendor/anyhow/tests/test_repr.rs", + "platform_deps/vendor/anyhow/tests/test_source.rs", + "platform_deps/vendor/anyhow/tests/ui/no-impl.rs", + "platform_deps/vendor/anyhow/tests/ui/no-impl.stderr", + "platform_features/BUILD.gn", + "platform_features/Cargo.lock", + "platform_features/Cargo.toml", + "platform_features/a/Cargo.toml", + "platform_features/a/src/lib.rs", + "platform_features/b/Cargo.toml", + "platform_features/b/src/lib.rs", + "platform_features/src/lib.rs", + "sdk_metadata/BUILD.gn", + "sdk_metadata/Cargo.lock", + "sdk_metadata/Cargo.toml", + "sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json", + "sdk_metadata/src/lib.rs", + "sdk_metadata/sub-crate-proc-macro/Cargo.toml", + "sdk_metadata/sub-crate-proc-macro/src/lib.rs", + "sdk_metadata/sub-crate/Cargo.toml", + "sdk_metadata/sub-crate/src/lib.rs", + "simple/BUILD.gn", + "simple/Cargo.lock", + "simple/Cargo.toml", + "simple/src/lib.rs", + "simple_deps/.cargo/config", + "simple_deps/BUILD.gn", + "simple_deps/BUILD_WITH_NO_ROOT.gn", + "simple_deps/Cargo.lock", + "simple_deps/Cargo.toml", + "simple_deps/src/lib.rs", + "simple_deps/vendor/anyhow/.cargo-checksum.json", + "simple_deps/vendor/anyhow/Cargo.toml", + "simple_deps/vendor/anyhow/LICENSE-APACHE", + "simple_deps/vendor/anyhow/LICENSE-MIT", + "simple_deps/vendor/anyhow/README.md", + "simple_deps/vendor/anyhow/build.rs", + "simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING", + "simple_deps/vendor/anyhow/src/backtrace.rs", + "simple_deps/vendor/anyhow/src/chain.rs", + "simple_deps/vendor/anyhow/src/context.rs", + "simple_deps/vendor/anyhow/src/error.rs", + "simple_deps/vendor/anyhow/src/fmt.rs", + "simple_deps/vendor/anyhow/src/kind.rs", + "simple_deps/vendor/anyhow/src/lib.rs", + "simple_deps/vendor/anyhow/src/macros.rs", + "simple_deps/vendor/anyhow/src/wrapper.rs", + "simple_deps/vendor/anyhow/tests/common/mod.rs", + "simple_deps/vendor/anyhow/tests/compiletest.rs", + "simple_deps/vendor/anyhow/tests/drop/mod.rs", + "simple_deps/vendor/anyhow/tests/test_autotrait.rs", + "simple_deps/vendor/anyhow/tests/test_backtrace.rs", + "simple_deps/vendor/anyhow/tests/test_boxed.rs", + "simple_deps/vendor/anyhow/tests/test_chain.rs", + "simple_deps/vendor/anyhow/tests/test_context.rs", + "simple_deps/vendor/anyhow/tests/test_convert.rs", + "simple_deps/vendor/anyhow/tests/test_downcast.rs", + "simple_deps/vendor/anyhow/tests/test_fmt.rs", + "simple_deps/vendor/anyhow/tests/test_macros.rs", + "simple_deps/vendor/anyhow/tests/test_repr.rs", + "simple_deps/vendor/anyhow/tests/test_source.rs", + "simple_deps/vendor/anyhow/tests/ui/no-impl.rs", + "simple_deps/vendor/anyhow/tests/ui/no-impl.stderr", + "target_renaming/BUILD.gn", + "target_renaming/Cargo.lock", + "target_renaming/Cargo.toml", + "target_renaming/example_lib/Cargo.toml", + "target_renaming/example_lib/src/lib.rs", + "target_renaming/src/lib.rs", + "testonly/BUILD.gn", + "testonly/Cargo.lock", + "testonly/Cargo.toml", + "testonly/example_lib/Cargo.toml", + "testonly/example_lib/src/lib.rs", + "testonly/src/lib.rs", + "visibility/BUILD.gn", + "visibility/Cargo.lock", + "visibility/Cargo.toml", + "visibility/example_lib/Cargo.toml", + "visibility/example_lib/src/lib.rs", + "visibility/src/lib.rs", + ] + } + + host_test_data("cargo_gnaw_prebuilt_bin_libs") { + sources = [ "//prebuilt/third_party/gn/${host_platform}/gn" ] + } +}
diff --git a/tests/binary/BUILD.gn b/tests/binary/BUILD.gn new file mode 100644 index 0000000..a8c2048 --- /dev/null +++ b/tests/binary/BUILD.gn
@@ -0,0 +1,67 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("package-with-bin") { + public_deps = [ ":package-with-bin-v0_1_0" ] +} + +group("foo-output") { + public_deps = [ ":package-with-bin-foo-binary-v0_1_0" ] +} + +executable("package-with-bin-foo-binary-v0_1_0") { + crate_name = "foo_binary" + crate_root = "//binary/src/main.rs" + output_name = "foo-output" + if (current_os == "fuchsia") { + configs += [ "//extra/fuchsia/config" ] + } + if (true) { + configs += [ "//applied/to/all" ] + } + + deps = [] + deps += [ ":package-with-bin-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=afa00503dca78ba8", + "-Cextra-filename=-afa00503dca78ba8", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("package-with-bin-v0_1_0") { + crate_name = "package_with_bin" + crate_root = "//binary/src/lib.rs" + output_name = "package_with_bin-b6c5dfd7f886ecb4" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=b6c5dfd7f886ecb4", + "-Cextra-filename=-b6c5dfd7f886ecb4", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/binary/Cargo.lock b/tests/binary/Cargo.lock new file mode 100644 index 0000000..b254950 --- /dev/null +++ b/tests/binary/Cargo.lock
@@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "package-with-bin" +version = "0.1.0"
diff --git a/tests/binary/Cargo.toml b/tests/binary/Cargo.toml new file mode 100644 index 0000000..334e83b --- /dev/null +++ b/tests/binary/Cargo.toml
@@ -0,0 +1,23 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "package-with-bin" +version = "0.1.0" +authors = ["Bryan Henry <bryanhenry@google.com>"] +edition = "2018" + +[[bin]] +name = "foo-binary" +path = "src/main.rs" + +[gn.package.package-with-bin."0.1.0".binary.foo-binary] +output_name = "foo-output" + +[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(unix)"] +configs = [ "//applied/to/all" ] + +[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(target_os = \"fuchsia\")"] +configs = [ "//extra/fuchsia/config" ] +
diff --git a/tests/binary/src/lib.rs b/tests/binary/src/lib.rs new file mode 100644 index 0000000..5f482a8 --- /dev/null +++ b/tests/binary/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +fn some_fn() { + println!("Hello, world!"); +}
diff --git a/tests/binary/src/main.rs b/tests/binary/src/main.rs new file mode 100644 index 0000000..8821b24 --- /dev/null +++ b/tests/binary/src/main.rs
@@ -0,0 +1,5 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +fn main() {}
diff --git a/tests/binary_with_tests/BUILD.gn b/tests/binary_with_tests/BUILD.gn new file mode 100644 index 0000000..43533b3 --- /dev/null +++ b/tests/binary_with_tests/BUILD.gn
@@ -0,0 +1,130 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("package-with-bin") { + public_deps = [ ":package-with-bin-v0_1_0" ] +} + +group("package-with-bin-test") { + public_deps = [ ":package-with-bin-v0_1_0-test" ] + testonly = true +} + +group("foo-output") { + public_deps = [ ":package-with-bin-foo-binary-v0_1_0" ] +} + +group("foo-output-test") { + public_deps = [ ":package-with-bin-foo-binary-v0_1_0-test" ] + testonly = true +} + +executable("package-with-bin-foo-binary-v0_1_0") { + crate_name = "foo_binary" + crate_root = "//binary_with_tests/src/main.rs" + output_name = "foo-output" + if (current_os == "fuchsia") { + configs += [ "//extra/fuchsia/config" ] + } + if (true) { + configs += [ "//applied/to/all" ] + } + + deps = [] + deps += [ ":package-with-bin-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=afa00503dca78ba8", + "-Cextra-filename=-afa00503dca78ba8", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +executable("package-with-bin-foo-binary-v0_1_0-test") { + crate_name = "foo_binary" + crate_root = "//binary_with_tests/src/main.rs" + output_name = "foo-output-test" + if (current_os == "fuchsia") { + configs += [ "//extra/fuchsia/config" ] + } + if (true) { + configs += [ "//applied/to/all" ] + } + + deps = [] + deps += [ ":package-with-bin-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=afa00503dca78ba8", + "-Cextra-filename=-afa00503dca78ba8", + "--test", + ] + + visibility = [ ":*" ] + + testonly = true + applicable_licenses = [] +} + +rust_library("package-with-bin-v0_1_0") { + crate_name = "package_with_bin" + crate_root = "//binary_with_tests/src/lib.rs" + output_name = "package_with_bin-b6c5dfd7f886ecb4" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=b6c5dfd7f886ecb4", + "-Cextra-filename=-b6c5dfd7f886ecb4", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +executable("package-with-bin-v0_1_0-test") { + crate_name = "package_with_bin" + crate_root = "//binary_with_tests/src/lib.rs" + output_name = "package_with_bin-b6c5dfd7f886ecb4-test" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=b6c5dfd7f886ecb4", + "-Cextra-filename=-b6c5dfd7f886ecb4", + "--test", + ] + + visibility = [ ":*" ] + + testonly = true + applicable_licenses = [] +}
diff --git a/tests/binary_with_tests/Cargo.lock b/tests/binary_with_tests/Cargo.lock new file mode 100644 index 0000000..b254950 --- /dev/null +++ b/tests/binary_with_tests/Cargo.lock
@@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "package-with-bin" +version = "0.1.0"
diff --git a/tests/binary_with_tests/Cargo.toml b/tests/binary_with_tests/Cargo.toml new file mode 100644 index 0000000..405a4a9 --- /dev/null +++ b/tests/binary_with_tests/Cargo.toml
@@ -0,0 +1,26 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "package-with-bin" +version = "0.1.0" +authors = ["Bryan Henry <bryanhenry@google.com>"] +edition = "2018" + +[[bin]] +name = "foo-binary" +path = "src/main.rs" + +[gn.package.package-with-bin."0.1.0"] +tests = true + +[gn.package.package-with-bin."0.1.0".binary.foo-binary] +output_name = "foo-output" + +[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(unix)"] +configs = [ "//applied/to/all" ] + +[gn.package.package-with-bin."0.1.0".binary.foo-binary.platform."cfg(target_os = \"fuchsia\")"] +configs = [ "//extra/fuchsia/config" ] +
diff --git a/tests/binary_with_tests/src/lib.rs b/tests/binary_with_tests/src/lib.rs new file mode 100644 index 0000000..5f482a8 --- /dev/null +++ b/tests/binary_with_tests/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +fn some_fn() { + println!("Hello, world!"); +}
diff --git a/tests/binary_with_tests/src/main.rs b/tests/binary_with_tests/src/main.rs new file mode 100644 index 0000000..8821b24 --- /dev/null +++ b/tests/binary_with_tests/src/main.rs
@@ -0,0 +1,5 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +fn main() {}
diff --git a/tests/cargo_features/BUILD-all-features.gn b/tests/cargo_features/BUILD-all-features.gn new file mode 100644 index 0000000..e8476e8 --- /dev/null +++ b/tests/cargo_features/BUILD-all-features.gn
@@ -0,0 +1,129 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("cargo_features") { + public_deps = [ ":cargo_features-v0_1_0" ] +} + +rust_library("cargo_features-v0_1_0") { + crate_name = "cargo_features" + crate_root = "//cargo_features/src/lib.rs" + output_name = "cargo_features-4775242031656858" + + deps = [] + deps += [ ":sub-crate-default-v0_1_0" ] + deps += [ ":sub-crate-non-default-v0_1_0" ] + deps += [ ":sub-crate-with-feature-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=4775242031656858", + "-Cextra-filename=-4775242031656858", + "--cfg=feature=\"default\"", + "--cfg=feature=\"featurefoo\"", + "--cfg=feature=\"sub-crate-default\"", + "--cfg=feature=\"sub-crate-non-default\"", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-default-v0_1_0") { + crate_name = "sub_crate_default" + crate_root = "//cargo_features/sub-crate-default/src/lib.rs" + output_name = "sub_crate_default-907014e298fd4f87" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=907014e298fd4f87", + "-Cextra-filename=-907014e298fd4f87", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-for-feature-v0_1_0") { + crate_name = "sub_crate_for_feature" + crate_root = "//cargo_features/sub-crate-for-feature/src/lib.rs" + output_name = "sub_crate_for_feature-aa2f3f9b02f291a9" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=aa2f3f9b02f291a9", + "-Cextra-filename=-aa2f3f9b02f291a9", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-non-default-v0_1_0") { + crate_name = "sub_crate_non_default" + crate_root = "//cargo_features/sub-crate-non-default/src/lib.rs" + output_name = "sub_crate_non_default-d9f5c9139685fd00" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=d9f5c9139685fd00", + "-Cextra-filename=-d9f5c9139685fd00", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-with-feature-v0_1_0") { + crate_name = "sub_crate_with_feature" + crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs" + output_name = "sub_crate_with_feature-2daa1749812a946a" + + deps = [] + deps += [ ":sub-crate-for-feature-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=2daa1749812a946a", + "-Cextra-filename=-2daa1749812a946a", + "--cfg=feature=\"featurefoo\"", + "--cfg=feature=\"sub-crate-for-feature\"", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/cargo_features/BUILD-default.gn b/tests/cargo_features/BUILD-default.gn new file mode 100644 index 0000000..1990209 --- /dev/null +++ b/tests/cargo_features/BUILD-default.gn
@@ -0,0 +1,81 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("cargo_features") { + public_deps = [ ":cargo_features-v0_1_0" ] +} + +rust_library("cargo_features-v0_1_0") { + crate_name = "cargo_features" + crate_root = "//cargo_features/src/lib.rs" + output_name = "cargo_features-4775242031656858" + + deps = [] + deps += [ ":sub-crate-default-v0_1_0" ] + deps += [ ":sub-crate-with-feature-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=4775242031656858", + "-Cextra-filename=-4775242031656858", + "--cfg=feature=\"default\"", + "--cfg=feature=\"sub-crate-default\"", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-default-v0_1_0") { + crate_name = "sub_crate_default" + crate_root = "//cargo_features/sub-crate-default/src/lib.rs" + output_name = "sub_crate_default-907014e298fd4f87" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=907014e298fd4f87", + "-Cextra-filename=-907014e298fd4f87", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-with-feature-v0_1_0") { + crate_name = "sub_crate_with_feature" + crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs" + output_name = "sub_crate_with_feature-2daa1749812a946a" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=2daa1749812a946a", + "-Cextra-filename=-2daa1749812a946a", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/cargo_features/BUILD-featurefoo.gn b/tests/cargo_features/BUILD-featurefoo.gn new file mode 100644 index 0000000..7681de9 --- /dev/null +++ b/tests/cargo_features/BUILD-featurefoo.gn
@@ -0,0 +1,106 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("cargo_features") { + public_deps = [ ":cargo_features-v0_1_0" ] +} + +rust_library("cargo_features-v0_1_0") { + crate_name = "cargo_features" + crate_root = "//cargo_features/src/lib.rs" + output_name = "cargo_features-4775242031656858" + + deps = [] + deps += [ ":sub-crate-default-v0_1_0" ] + deps += [ ":sub-crate-with-feature-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=4775242031656858", + "-Cextra-filename=-4775242031656858", + "--cfg=feature=\"default\"", + "--cfg=feature=\"featurefoo\"", + "--cfg=feature=\"sub-crate-default\"", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-default-v0_1_0") { + crate_name = "sub_crate_default" + crate_root = "//cargo_features/sub-crate-default/src/lib.rs" + output_name = "sub_crate_default-907014e298fd4f87" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=907014e298fd4f87", + "-Cextra-filename=-907014e298fd4f87", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-for-feature-v0_1_0") { + crate_name = "sub_crate_for_feature" + crate_root = "//cargo_features/sub-crate-for-feature/src/lib.rs" + output_name = "sub_crate_for_feature-aa2f3f9b02f291a9" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=aa2f3f9b02f291a9", + "-Cextra-filename=-aa2f3f9b02f291a9", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-with-feature-v0_1_0") { + crate_name = "sub_crate_with_feature" + crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs" + output_name = "sub_crate_with_feature-2daa1749812a946a" + + deps = [] + deps += [ ":sub-crate-for-feature-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=2daa1749812a946a", + "-Cextra-filename=-2daa1749812a946a", + "--cfg=feature=\"featurefoo\"", + "--cfg=feature=\"sub-crate-for-feature\"", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/cargo_features/BUILD-no-default-features.gn b/tests/cargo_features/BUILD-no-default-features.gn new file mode 100644 index 0000000..118faaf --- /dev/null +++ b/tests/cargo_features/BUILD-no-default-features.gn
@@ -0,0 +1,57 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("cargo_features") { + public_deps = [ ":cargo_features-v0_1_0" ] +} + +rust_library("cargo_features-v0_1_0") { + crate_name = "cargo_features" + crate_root = "//cargo_features/src/lib.rs" + output_name = "cargo_features-4775242031656858" + + deps = [] + deps += [ ":sub-crate-with-feature-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=4775242031656858", + "-Cextra-filename=-4775242031656858", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-with-feature-v0_1_0") { + crate_name = "sub_crate_with_feature" + crate_root = "//cargo_features/sub-crate-with-feature/src/lib.rs" + output_name = "sub_crate_with_feature-2daa1749812a946a" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=2daa1749812a946a", + "-Cextra-filename=-2daa1749812a946a", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/cargo_features/Cargo.lock b/tests/cargo_features/Cargo.lock new file mode 100644 index 0000000..b5075ed --- /dev/null +++ b/tests/cargo_features/Cargo.lock
@@ -0,0 +1,31 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cargo_features" +version = "0.1.0" +dependencies = [ + "sub-crate-default", + "sub-crate-non-default", + "sub-crate-with-feature", +] + +[[package]] +name = "sub-crate-default" +version = "0.1.0" + +[[package]] +name = "sub-crate-for-feature" +version = "0.1.0" + +[[package]] +name = "sub-crate-non-default" +version = "0.1.0" + +[[package]] +name = "sub-crate-with-feature" +version = "0.1.0" +dependencies = [ + "sub-crate-for-feature", +]
diff --git a/tests/cargo_features/Cargo.toml b/tests/cargo_features/Cargo.toml new file mode 100644 index 0000000..47ba252 --- /dev/null +++ b/tests/cargo_features/Cargo.toml
@@ -0,0 +1,19 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "cargo_features" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sub-crate-default = { path = "./sub-crate-default", optional = true } +sub-crate-non-default = { path = "./sub-crate-non-default", optional = true } +sub-crate-with-feature = { path = "./sub-crate-with-feature" } + +[features] +default = ["sub-crate-default"] +featurefoo = ["sub-crate-with-feature/featurefoo"]
diff --git a/tests/cargo_features/src/lib.rs b/tests/cargo_features/src/lib.rs new file mode 100644 index 0000000..a4eccd0 --- /dev/null +++ b/tests/cargo_features/src/lib.rs
@@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +}
diff --git a/tests/cargo_features/sub-crate-default/Cargo.toml b/tests/cargo_features/sub-crate-default/Cargo.toml new file mode 100644 index 0000000..8d9a004 --- /dev/null +++ b/tests/cargo_features/sub-crate-default/Cargo.toml
@@ -0,0 +1,12 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "sub-crate-default" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies]
diff --git a/tests/cargo_features/sub-crate-default/src/lib.rs b/tests/cargo_features/sub-crate-default/src/lib.rs new file mode 100644 index 0000000..a4eccd0 --- /dev/null +++ b/tests/cargo_features/sub-crate-default/src/lib.rs
@@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +}
diff --git a/tests/cargo_features/sub-crate-for-feature/Cargo.toml b/tests/cargo_features/sub-crate-for-feature/Cargo.toml new file mode 100644 index 0000000..9cf8fe7 --- /dev/null +++ b/tests/cargo_features/sub-crate-for-feature/Cargo.toml
@@ -0,0 +1,12 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "sub-crate-for-feature" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies]
diff --git a/tests/cargo_features/sub-crate-for-feature/src/lib.rs b/tests/cargo_features/sub-crate-for-feature/src/lib.rs new file mode 100644 index 0000000..a4eccd0 --- /dev/null +++ b/tests/cargo_features/sub-crate-for-feature/src/lib.rs
@@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +}
diff --git a/tests/cargo_features/sub-crate-non-default/Cargo.toml b/tests/cargo_features/sub-crate-non-default/Cargo.toml new file mode 100644 index 0000000..a8dc53b --- /dev/null +++ b/tests/cargo_features/sub-crate-non-default/Cargo.toml
@@ -0,0 +1,12 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "sub-crate-non-default" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies]
diff --git a/tests/cargo_features/sub-crate-non-default/src/lib.rs b/tests/cargo_features/sub-crate-non-default/src/lib.rs new file mode 100644 index 0000000..a4eccd0 --- /dev/null +++ b/tests/cargo_features/sub-crate-non-default/src/lib.rs
@@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +}
diff --git a/tests/cargo_features/sub-crate-with-feature/Cargo.toml b/tests/cargo_features/sub-crate-with-feature/Cargo.toml new file mode 100644 index 0000000..35eee2b --- /dev/null +++ b/tests/cargo_features/sub-crate-with-feature/Cargo.toml
@@ -0,0 +1,16 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "sub-crate-with-feature" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sub-crate-for-feature = { path = "../sub-crate-for-feature", optional = true } + +[features] +featurefoo = ["sub-crate-for-feature"]
diff --git a/tests/cargo_features/sub-crate-with-feature/src/lib.rs b/tests/cargo_features/sub-crate-with-feature/src/lib.rs new file mode 100644 index 0000000..a4eccd0 --- /dev/null +++ b/tests/cargo_features/sub-crate-with-feature/src/lib.rs
@@ -0,0 +1,12 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +}
diff --git a/tests/feature_review/BUILD.gn b/tests/feature_review/BUILD.gn new file mode 100644 index 0000000..4403a59 --- /dev/null +++ b/tests/feature_review/BUILD.gn
@@ -0,0 +1,80 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("feature_test") { + public_deps = [ ":feature_test-v0_1_0" ] +} + +rust_library("crate_with_features-v0_1_0") { + crate_name = "crate_with_features" + crate_root = "//feature_review/crate_with_features/src/lib.rs" + output_name = "crate_with_features-7091b4706c607e57" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=7091b4706c607e57", + "-Cextra-filename=-7091b4706c607e57", + "--cfg=feature=\"feature1\"", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("feature_test-v0_1_0") { + crate_name = "feature_test" + crate_root = "//feature_review/src/lib.rs" + output_name = "feature_test-75712ce55243d0e0" + + deps = [] + deps += [ ":needs_feature-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=75712ce55243d0e0", + "-Cextra-filename=-75712ce55243d0e0", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("needs_feature-v0_1_0") { + crate_name = "needs_feature" + crate_root = "//feature_review/needs_feature/src/lib.rs" + output_name = "needs_feature-e99a212d2fc1fa1b" + + deps = [] + deps += [ ":crate_with_features-v0_1_0" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=e99a212d2fc1fa1b", + "-Cextra-filename=-e99a212d2fc1fa1b", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/feature_review/Cargo.lock b/tests/feature_review/Cargo.lock new file mode 100644 index 0000000..c9a5795 --- /dev/null +++ b/tests/feature_review/Cargo.lock
@@ -0,0 +1,21 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "crate_with_features" +version = "0.1.0" + +[[package]] +name = "feature_test" +version = "0.1.0" +dependencies = [ + "needs_feature", +] + +[[package]] +name = "needs_feature" +version = "0.1.0" +dependencies = [ + "crate_with_features", +]
diff --git a/tests/feature_review/Cargo.toml b/tests/feature_review/Cargo.toml new file mode 100644 index 0000000..09de031 --- /dev/null +++ b/tests/feature_review/Cargo.toml
@@ -0,0 +1,17 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "feature_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +needs_feature = { path = "needs_feature" } + +[gn] +require_feature_reviews = ["crate_with_features"] + +[gn.package.crate_with_features."0.1.0"] +reviewed_features = ["feature1"]
diff --git a/tests/feature_review/Cargo_extra_review.toml b/tests/feature_review/Cargo_extra_review.toml new file mode 100644 index 0000000..6f4c3e7 --- /dev/null +++ b/tests/feature_review/Cargo_extra_review.toml
@@ -0,0 +1,10 @@ +[package] +name = "feature_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +needs_feature = { path = "needs_feature" } + +[gn.package.crate_with_features."0.1.0"] +reviewed_features = ["feature1"]
diff --git a/tests/feature_review/Cargo_missing_review.toml b/tests/feature_review/Cargo_missing_review.toml new file mode 100644 index 0000000..deb7aad --- /dev/null +++ b/tests/feature_review/Cargo_missing_review.toml
@@ -0,0 +1,12 @@ +[package] +name = "feature_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +needs_feature = { path = "needs_feature" } + +[gn] +require_feature_reviews = ["crate_with_features"] + +[gn.package] \ No newline at end of file
diff --git a/tests/feature_review/Cargo_unreviewed_feature.toml b/tests/feature_review/Cargo_unreviewed_feature.toml new file mode 100644 index 0000000..e046195 --- /dev/null +++ b/tests/feature_review/Cargo_unreviewed_feature.toml
@@ -0,0 +1,13 @@ +[package] +name = "feature_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +needs_feature = { path = "needs_feature" } + +[gn] +require_feature_reviews = ["crate_with_features"] + +[gn.package.crate_with_features."0.1.0"] +reviewed_features = ["feature2"]
diff --git a/tests/feature_review/crate_with_features/Cargo.toml b/tests/feature_review/crate_with_features/Cargo.toml new file mode 100644 index 0000000..c0fa58c --- /dev/null +++ b/tests/feature_review/crate_with_features/Cargo.toml
@@ -0,0 +1,12 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "crate_with_features" +version = "0.1.0" +edition = "2018" + +[features] +feature1 = [] +feature2 = []
diff --git a/tests/feature_review/crate_with_features/src/lib.rs b/tests/feature_review/crate_with_features/src/lib.rs new file mode 100644 index 0000000..70a39d4 --- /dev/null +++ b/tests/feature_review/crate_with_features/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/feature_review/needs_feature/Cargo.toml b/tests/feature_review/needs_feature/Cargo.toml new file mode 100644 index 0000000..cbc217d --- /dev/null +++ b/tests/feature_review/needs_feature/Cargo.toml
@@ -0,0 +1,11 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "needs_feature" +version = "0.1.0" +edition = "2021" + +[dependencies] +crate_with_features = { path = "../crate_with_features", features = ["feature1"] }
diff --git a/tests/feature_review/needs_feature/src/lib.rs b/tests/feature_review/needs_feature/src/lib.rs new file mode 100644 index 0000000..70a39d4 --- /dev/null +++ b/tests/feature_review/needs_feature/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/feature_review/src/lib.rs b/tests/feature_review/src/lib.rs new file mode 100644 index 0000000..70a39d4 --- /dev/null +++ b/tests/feature_review/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/golden.rs b/tests/golden.rs new file mode 100644 index 0000000..99c9c5e --- /dev/null +++ b/tests/golden.rs
@@ -0,0 +1,355 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! This file contains "golden" tests, which compare the output of known sample +//! `Cargo.toml` files with known fixed reference output files. +//! +//! TODO(https://fxbug.dev/42178193) move these golden specs into GN + +use { + anyhow::Context, + argh::FromArgs, + // Without this, the test diffs are impractical to debug. + pretty_assertions::assert_eq, + std::fmt::{Debug, Display}, + std::path::{Path, PathBuf}, +}; + +#[derive(FromArgs, Debug)] +/// Paths to use in test. All paths are relative to where this test is executed. +/// +/// These paths have to be relative when passed to this test on infra bots, so they are mapped +/// correctly, otherwise they won't be available at test runtime. It is safe to convert these to +/// absolute paths later in the test. +struct Paths { + /// path to the directory where golden tests are placed. + #[argh(option)] + test_base_dir: String, + /// path to `rustc` binary to use in test. + #[argh(option)] + rustc_binary_path: String, + /// path to `gn` binary to use in test. + #[argh(option)] + gn_binary_path: String, + /// path to `cargo` binary to use in test. + #[argh(option)] + cargo_binary_path: String, + /// path to shared libraries directory to use in test. + #[argh(option)] + lib_path: String, +} + +#[derive(PartialEq, Eq)] +struct DisplayAsDebug<T: Display>(T); + +impl<T: Display> Debug for DisplayAsDebug<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +fn main() { + let paths: Paths = argh::from_env(); + eprintln!("paths: {:?}", &paths); + + // Shared library setup for Linux and Mac. Systems will ignore the settings + // that don't apply to them. + // + // These values need to be absolute so they work regardless of the current working directory. + std::env::set_var("LD_LIBRARY_PATH", Path::new(&paths.lib_path).canonicalize().unwrap()); + std::env::set_var("DYLD_LIBRARY_PATH", Path::new(&paths.lib_path).canonicalize().unwrap()); + + // Cargo internally invokes rustc; but we must tell it to use the one from + // our sandbox, and this is configured using the env variable "RUSTC". + // + // This value needs to be absolute so it works regardless of the current working directory. + // + // See: + // https://doc.rust-lang.org/cargo/reference/environment-variables.html + std::env::set_var("RUSTC", Path::new(&paths.rustc_binary_path).canonicalize().unwrap()); + + #[derive(Debug, Default)] + struct Options { + /// Fuchsia SDK metadata output path; relative to the base test directory. + sdk_metadata_path: Option<Vec<&'static str>>, + /// Fuchsia SDK metadata golden path; relative to the golden files directory. + sdk_metadata_golden_path: Option<Vec<&'static str>>, + /// Extra arguments to pass to gnaw. + extra_args: Vec<&'static str>, + } + #[derive(Debug)] + struct TestCase { + /// Manifest file path (`Cargo.toml`); relative to the base test directory. + manifest_path: Vec<&'static str>, + /// Expected file (`BUILD.gn`); relative to the base test directory. + golden_expected_filename: Vec<&'static str>, + /// Extra stuff not needed for most tests. + options: Options, + } + + let tests = vec![ + TestCase { + manifest_path: vec!["simple", "Cargo.toml"], + golden_expected_filename: vec!["simple", "BUILD.gn"], + options: Default::default(), + }, + TestCase { + manifest_path: vec!["simple_deps", "Cargo.toml"], + golden_expected_filename: vec!["simple_deps", "BUILD.gn"], + options: Default::default(), + }, + TestCase { + manifest_path: vec!["simple_deps", "Cargo.toml"], + golden_expected_filename: vec!["simple_deps", "BUILD_WITH_NO_ROOT.gn"], + options: Options { extra_args: vec!["--skip-root"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["platform_deps", "Cargo.toml"], + golden_expected_filename: vec!["platform_deps", "BUILD.gn"], + options: Options { extra_args: vec!["--skip-root"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["platform_features", "Cargo.toml"], + golden_expected_filename: vec!["platform_features", "BUILD.gn"], + options: Options { extra_args: vec!["--skip-root"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["binary", "Cargo.toml"], + golden_expected_filename: vec!["binary", "BUILD.gn"], + options: Default::default(), + }, + TestCase { + manifest_path: vec!["binary_with_tests", "Cargo.toml"], + golden_expected_filename: vec!["binary_with_tests", "BUILD.gn"], + options: Default::default(), + }, + TestCase { + manifest_path: vec!["multiple_crate_types", "Cargo.toml"], + golden_expected_filename: vec!["multiple_crate_types", "BUILD.gn"], + options: Default::default(), + }, + TestCase { + manifest_path: vec!["feature_review", "Cargo.toml"], + golden_expected_filename: vec!["feature_review", "BUILD.gn"], + options: Default::default(), + }, + TestCase { + manifest_path: vec!["cargo_features", "Cargo.toml"], + golden_expected_filename: vec!["cargo_features", "BUILD-default.gn"], + options: Default::default(), + }, + TestCase { + manifest_path: vec!["cargo_features", "Cargo.toml"], + golden_expected_filename: vec!["cargo_features", "BUILD-all-features.gn"], + options: Options { extra_args: vec!["--all-features"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["cargo_features", "Cargo.toml"], + golden_expected_filename: vec!["cargo_features", "BUILD-no-default-features.gn"], + options: Options { extra_args: vec!["--no-default-features"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["cargo_features", "Cargo.toml"], + golden_expected_filename: vec!["cargo_features", "BUILD-featurefoo.gn"], + options: Options { extra_args: vec!["--features", "featurefoo"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["visibility", "Cargo.toml"], + golden_expected_filename: vec!["visibility", "BUILD.gn"], + options: Options { extra_args: vec!["--skip-root"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["target_renaming", "Cargo.toml"], + golden_expected_filename: vec!["target_renaming", "BUILD.gn"], + options: Options { extra_args: vec!["--skip-root"], ..Default::default() }, + }, + TestCase { + manifest_path: vec!["sdk_metadata", "Cargo.toml"], + golden_expected_filename: vec!["sdk_metadata", "BUILD.gn"], + options: Options { + sdk_metadata_path: Some(vec!["sdk_metas", "sdk_metadata.sdk.meta.json"]), + sdk_metadata_golden_path: Some(vec![ + "sdk_metadata", + "sdk_metas", + "sdk_metadata.sdk.meta.json", + ]), + ..Default::default() + }, + }, + TestCase { + manifest_path: vec!["testonly", "Cargo.toml"], + golden_expected_filename: vec!["testonly", "BUILD.gn"], + options: Options { extra_args: vec!["--skip-root"], ..Default::default() }, + }, + ]; + + let run_gnaw = |manifest_path: &[&str], + extra_args: &[&str], + sdk_metadata_path: Option<&[&str]>| { + let test_dir = tempfile::TempDir::new().unwrap(); + let mut manifest_path: PathBuf = + test_dir.path().join(manifest_path.iter().collect::<PathBuf>()); + let output = test_dir.path().join("BUILD.gn"); + let output_sdk_metadata = sdk_metadata_path.map(|sdk_metadata_path| { + test_dir.path().join(sdk_metadata_path.iter().collect::<PathBuf>()) + }); + + // we need the emitted file to be under the same path as the gn targets it references + let test_base_dir = PathBuf::from(&paths.test_base_dir); + copy_contents(&test_base_dir, test_dir.path()); + + if manifest_path.file_name().unwrap() != "Cargo.toml" { + // rename manifest so that `cargo metadata` is happy. + let manifest_dest_path = + manifest_path.parent().expect("getting Cargo.toml parent dir").join("Cargo.toml"); + std::fs::copy(&manifest_path, &manifest_dest_path).expect("writing Cargo.toml"); + manifest_path = manifest_dest_path; + } + + let project_root = test_dir.path().to_str().unwrap().to_owned(); + // Note: argh does not support "--flag=value" or "--bool-flag false". + let absolute_cargo_binary_path = + Path::new(&paths.cargo_binary_path).canonicalize().unwrap(); + let mut args: Vec<&str> = vec![ + // args[0] is not used in arg parsing, so this can be any string. + "fake_binary_name", + "--manifest-path", + manifest_path.to_str().unwrap(), + "--project-root", + &project_root, + "--output", + output.to_str().unwrap(), + "--gn-bin", + &paths.gn_binary_path, + "--cargo", + // Cargo is not executed in another working directory by gnaw_lib, so an absolute path + // is necessary here. + absolute_cargo_binary_path.to_str().unwrap(), + ]; + if let Some(output_sdk_metadata) = &output_sdk_metadata { + args.extend(&[ + "--output-fuchsia-sdk-metadata", + output_sdk_metadata.parent().unwrap().to_str().unwrap(), + ]); + } + args.extend(extra_args); + gnaw_lib::run(&args) + .with_context(|| format!("error running gnaw with args: {:?}\n\t", &args))?; + let output = std::fs::read_to_string(&output) + .with_context(|| format!("while reading tempfile: {}", output.display())) + .expect("tempfile read success"); + let output_sdk_metadata = output_sdk_metadata + .as_ref() + .map(std::fs::read_to_string) + .transpose() + .with_context(|| { + format!("while reading sdk metadata: {}", output_sdk_metadata.unwrap().display()) + }) + .expect("sdk metadata read success"); + Result::<_, anyhow::Error>::Ok((output, output_sdk_metadata)) + }; + + for test in tests { + let (output, output_sdk_metadata) = run_gnaw( + &test.manifest_path, + &test.options.extra_args, + test.options.sdk_metadata_path.as_deref(), + ) + .with_context(|| format!("\n\ttest was: {:?}", &test)) + .expect("gnaw_lib::run should succeed"); + + let test_base_dir = PathBuf::from(&paths.test_base_dir); + let expected_path: PathBuf = + test_base_dir.join(test.golden_expected_filename.iter().collect::<PathBuf>()); + let expected = std::fs::read_to_string(expected_path.to_string_lossy().to_string()) + .with_context(|| { + format!("while reading expected: {:?}", &test.golden_expected_filename) + }) + .expect("expected file read success"); + assert_eq!( + DisplayAsDebug(&expected), + DisplayAsDebug(&output), + "left: expected; right: actual: {:?}\n\nGenerated content:\n----------\n{}\n----------\n", + &test, + &output + ); + + if let Some(output_sdk_metadata) = output_sdk_metadata { + let expected_sdk_metadata_path = test_base_dir.join( + test.options.sdk_metadata_golden_path.as_ref().unwrap().iter().collect::<PathBuf>(), + ); + let expected_sdk_metadata = std::fs::read_to_string(&expected_sdk_metadata_path) + .with_context(|| { + format!("while reading sdk metadata: {}", expected_sdk_metadata_path.display()) + }) + .expect("sdk metadata read success"); + assert_eq!( + DisplayAsDebug(&expected_sdk_metadata), + DisplayAsDebug(&output_sdk_metadata), + "left: expected; right: actual: {:?}\n\nGenerated content:\n----------\n{}\n----------\n", + &test, + &output_sdk_metadata, + ); + } + } + + #[derive(Debug)] + struct ExpectFailCase { + /// Manifest file path (`Cargo.toml`); relative to the base test directory. + manifest_path: Vec<&'static str>, + /// Expected string to search for in returned error. + expected_error_substring: &'static str, + /// Extra arguments to pass to gnaw. + extra_args: Vec<&'static str>, + } + let tests = vec![ + ExpectFailCase { + manifest_path: vec!["feature_review", "Cargo_unreviewed_feature.toml"], + expected_error_substring: + "crate_with_features 0.1.0 is included with unreviewed features [\"feature1\"]", + extra_args: vec![], + }, + ExpectFailCase { + manifest_path: vec!["feature_review", "Cargo_missing_review.toml"], + expected_error_substring: + "crate_with_features 0.1.0 requires feature review but reviewed features not found", + extra_args: vec![], + }, + ExpectFailCase { + manifest_path: vec!["feature_review", "Cargo_extra_review.toml"], + expected_error_substring: + "crate_with_features 0.1.0 sets reviewed_features but crate_with_features was not found in require_feature_reviews", + extra_args: vec![], + }, + ]; + for test in tests { + let result = run_gnaw(&test.manifest_path, &test.extra_args, None); + let error = match result { + Ok(_) => panic!("gnaw unexpectedly succeeded for {:?}", test), + Err(e) => e, + }; + if error.chain().find(|e| e.to_string().contains(test.expected_error_substring)).is_none() { + panic!( + "expected error to contain {:?}, was: {:?}", + test.expected_error_substring, error + ); + } + } +} + +fn copy_contents(original_test_dir: &Path, test_dir_path: &Path) { + // copy the contents of original test dir to test_dir + for entry in walkdir::WalkDir::new(&original_test_dir) { + let entry = entry.expect("walking original test directory to copy files to /tmp"); + if !entry.file_type().is_file() { + continue; + } + let to_copy = entry.path(); + let destination = test_dir_path.join(to_copy.strip_prefix(&original_test_dir).unwrap()); + std::fs::create_dir_all(destination.parent().unwrap()) + .expect("making parent of file to copy"); + std::fs::copy(to_copy, destination).expect("copying file"); + } + println!("done copying files"); +}
diff --git a/tests/multiple_crate_types/BUILD.gn b/tests/multiple_crate_types/BUILD.gn new file mode 100644 index 0000000..5203c1a --- /dev/null +++ b/tests/multiple_crate_types/BUILD.gn
@@ -0,0 +1,105 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("multiple_crate_types") { + public_deps = [ ":multiple_crate_types-v1_0_25" ] +} + +rust_library("multiple_crate_types-v1_0_25") { + crate_name = "multiple_crate_types" + crate_root = "//multiple_crate_types/src/lib.rs" + output_name = "multiple_crate_types-8be5e59704cbf5a4" + + deps = [] + deps += [ ":sub-crate-with-cdylib-v1_0_25" ] + deps += [ ":sub-crate-with-dylib-v1_0_25" ] + deps += [ ":sub-crate-with-rlib-v1_0_25" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=8be5e59704cbf5a4", + "-Cextra-filename=-8be5e59704cbf5a4", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-with-cdylib-v1_0_25") { + crate_name = "sub_crate_with_cdylib" + crate_root = "//multiple_crate_types/sub-crate-with-cdylib/src/lib.rs" + output_name = "sub_crate_with_cdylib-2c19a9098651391d" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=2c19a9098651391d", + "-Cextra-filename=-2c19a9098651391d", + ] + + visibility = [] + visibility += [ ":*" ] + visibility += [ "//foo/bar/*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-with-dylib-v1_0_25") { + crate_name = "sub_crate_with_dylib" + crate_root = "//multiple_crate_types/sub-crate-with-dylib/src/lib.rs" + output_name = "sub_crate_with_dylib-cba782ed0389a004" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=cba782ed0389a004", + "-Cextra-filename=-cba782ed0389a004", + ] + + visibility = [] + visibility += [ ":*" ] + visibility += [ "//foo/bar/*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-with-rlib-v1_0_25") { + crate_name = "sub_crate_with_rlib" + crate_root = "//multiple_crate_types/sub-crate-with-rlib/src/lib.rs" + output_name = "sub_crate_with_rlib-5a8ce889cce06275" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=5a8ce889cce06275", + "-Cextra-filename=-5a8ce889cce06275", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/multiple_crate_types/Cargo.lock b/tests/multiple_crate_types/Cargo.lock new file mode 100644 index 0000000..dbea50f --- /dev/null +++ b/tests/multiple_crate_types/Cargo.lock
@@ -0,0 +1,24 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "multiple_crate_types" +version = "1.0.25" +dependencies = [ + "sub-crate-with-cdylib", + "sub-crate-with-dylib", + "sub-crate-with-rlib", +] + +[[package]] +name = "sub-crate-with-cdylib" +version = "1.0.25" + +[[package]] +name = "sub-crate-with-dylib" +version = "1.0.25" + +[[package]] +name = "sub-crate-with-rlib" +version = "1.0.25"
diff --git a/tests/multiple_crate_types/Cargo.toml b/tests/multiple_crate_types/Cargo.toml new file mode 100644 index 0000000..44bed4f --- /dev/null +++ b/tests/multiple_crate_types/Cargo.toml
@@ -0,0 +1,20 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "multiple_crate_types" +version = "1.0.25" +authors = ["Erick Tryzelaar <etryzelaar@google.com>"] +edition = "2018" + +[dependencies] +sub-crate-with-cdylib = { version = "1.0.25", path = "sub-crate-with-cdylib" } +sub-crate-with-dylib = { version = "1.0.25", path = "sub-crate-with-dylib" } +sub-crate-with-rlib = { version = "1.0.25", path = "sub-crate-with-rlib" } + +[gn.package.sub-crate-with-cdylib."1.0.25"] +visibility = [":*", "//foo/bar/*"] + +[gn.package.sub-crate-with-dylib."1.0.25"] +visibility = [":*", "//foo/bar/*"]
diff --git a/tests/multiple_crate_types/src/lib.rs b/tests/multiple_crate_types/src/lib.rs new file mode 100644 index 0000000..917264b --- /dev/null +++ b/tests/multiple_crate_types/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +pub fn some_fn() { + println!("Hello, world!"); +}
diff --git a/tests/multiple_crate_types/sub-crate-with-cdylib/Cargo.toml b/tests/multiple_crate_types/sub-crate-with-cdylib/Cargo.toml new file mode 100644 index 0000000..0083f47 --- /dev/null +++ b/tests/multiple_crate_types/sub-crate-with-cdylib/Cargo.toml
@@ -0,0 +1,8 @@ +[package] +name = "sub-crate-with-cdylib" +version = "1.0.25" +authors = ["Erick Tryzelaar <etryzelaar@google.com>"] +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib", "staticlib"]
diff --git a/tests/multiple_crate_types/sub-crate-with-cdylib/src/lib.rs b/tests/multiple_crate_types/sub-crate-with-cdylib/src/lib.rs new file mode 100644 index 0000000..40b4304 --- /dev/null +++ b/tests/multiple_crate_types/sub-crate-with-cdylib/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// no-op +// hello there +pub const EXPORTED: u32 = 777;
diff --git a/tests/multiple_crate_types/sub-crate-with-dylib/Cargo.toml b/tests/multiple_crate_types/sub-crate-with-dylib/Cargo.toml new file mode 100644 index 0000000..3aeefa6 --- /dev/null +++ b/tests/multiple_crate_types/sub-crate-with-dylib/Cargo.toml
@@ -0,0 +1,8 @@ +[package] +name = "sub-crate-with-dylib" +version = "1.0.25" +authors = ["Erick Tryzelaar <etryzelaar@google.com>"] +edition = "2018" + +[lib] +crate-type = ["dylib", "lib", "staticlib"]
diff --git a/tests/multiple_crate_types/sub-crate-with-dylib/src/lib.rs b/tests/multiple_crate_types/sub-crate-with-dylib/src/lib.rs new file mode 100644 index 0000000..40b4304 --- /dev/null +++ b/tests/multiple_crate_types/sub-crate-with-dylib/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// no-op +// hello there +pub const EXPORTED: u32 = 777;
diff --git a/tests/multiple_crate_types/sub-crate-with-rlib/Cargo.toml b/tests/multiple_crate_types/sub-crate-with-rlib/Cargo.toml new file mode 100644 index 0000000..fa164e1 --- /dev/null +++ b/tests/multiple_crate_types/sub-crate-with-rlib/Cargo.toml
@@ -0,0 +1,8 @@ +[package] +name = "sub-crate-with-rlib" +version = "1.0.25" +authors = ["Erick Tryzelaar <etryzelaar@google.com>"] +edition = "2018" + +[lib] +crate-type = ["rlib", "staticlib"]
diff --git a/tests/multiple_crate_types/sub-crate-with-rlib/src/lib.rs b/tests/multiple_crate_types/sub-crate-with-rlib/src/lib.rs new file mode 100644 index 0000000..40b4304 --- /dev/null +++ b/tests/multiple_crate_types/sub-crate-with-rlib/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// no-op +// hello there +pub const EXPORTED: u32 = 777;
diff --git a/tests/platform_deps/.cargo/config b/tests/platform_deps/.cargo/config new file mode 100644 index 0000000..0236928 --- /dev/null +++ b/tests/platform_deps/.cargo/config
@@ -0,0 +1,5 @@ +[source.crates-io] +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendor"
diff --git a/tests/platform_deps/BUILD.gn b/tests/platform_deps/BUILD.gn new file mode 100644 index 0000000..1196a9d --- /dev/null +++ b/tests/platform_deps/BUILD.gn
@@ -0,0 +1,57 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +if (current_cpu == "arm64" && current_os == "mac") { + group("anyhow") { + public_deps = [ ":anyhow-v1_0_27" ] + } +} + +if (current_os == "fuchsia") { + group("anyhow") { + public_deps = [ ":anyhow-v1_0_27" ] + } +} + +license("anyhow-v1_0_27.license") { + public_package_name = "anyhow" + license_files = [ + "//platform_deps/vendor/anyhow/LICENSE-APACHE", + "//platform_deps/vendor/anyhow/LICENSE-MIT", + ] +} + +rust_library("anyhow-v1_0_27") { + crate_name = "anyhow" + crate_root = "//platform_deps/vendor/anyhow/src/lib.rs" + output_name = "anyhow-d49cfcf8c47646f2" + if (current_os == "fuchsia") { + configs += [ "//build/config/fuchsia:libunwind" ] + } + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=d49cfcf8c47646f2", + "-Cextra-filename=-d49cfcf8c47646f2", + "--cfg=feature=\"default\"", + "--cfg=feature=\"std\"", + "--cfg=backtrace", + ] + + visibility = [ ":*" ] + + applicable_licenses = [ ":anyhow-v1_0_27.license" ] +}
diff --git a/tests/platform_deps/Cargo.lock b/tests/platform_deps/Cargo.lock new file mode 100644 index 0000000..364b284 --- /dev/null +++ b/tests/platform_deps/Cargo.lock
@@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" + +[[package]] +name = "simple_deps" +version = "0.1.0" +dependencies = [ + "anyhow", +]
diff --git a/tests/platform_deps/Cargo.toml b/tests/platform_deps/Cargo.toml new file mode 100644 index 0000000..7b234a5 --- /dev/null +++ b/tests/platform_deps/Cargo.toml
@@ -0,0 +1,22 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "simple_deps" +version = "0.1.0" +authors = ["Benjamin Brittain <bwb@google.com>"] +edition = "2018" + +[target.'cfg(target_os = "fuchsia")'.dependencies] +anyhow = "1.0.27" + +[target.aarch64-apple-darwin.dependencies] +anyhow = "1.0.27" + +# gn config +[gn.package.anyhow."1.0.27"] +rustflags = ["--cfg=backtrace"] + +[gn.package.anyhow."1.0.27".platform."cfg(target_os = \"fuchsia\")"] +configs = [ "//build/config/fuchsia:libunwind" ]
diff --git a/tests/platform_deps/src/lib.rs b/tests/platform_deps/src/lib.rs new file mode 100644 index 0000000..5f482a8 --- /dev/null +++ b/tests/platform_deps/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +fn some_fn() { + println!("Hello, world!"); +}
diff --git a/tests/platform_deps/vendor/anyhow/.cargo-checksum.json b/tests/platform_deps/vendor/anyhow/.cargo-checksum.json new file mode 100644 index 0000000..5da7341 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/.cargo-checksum.json
@@ -0,0 +1 @@ +{"files":{"Cargo.toml":"6a91884ae5c50562445d6a81d989a8ac76fe64e5fd6a6f3ce067e01f03772419","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"6f6d4c826e6be52ba5c29e6b1169b11e3789ab0d426d3e17ad1c19b1f646b631","build.rs":"42e4fff05dc773820ee1843aba8e36389a36386d6e6082e778d2203420bd72ab","src/backtrace.rs":"a82a8ffae2c68ee385dc78d8ec8cb6f3351234f0ad6af7e87df2371593e0f6aa","src/chain.rs":"1627608ce95c3484d26e1742a1b8b533b74c6159916b717bacffae3ae53731f9","src/context.rs":"f2be36af9588c924ed087bcb7bd681d330889e54b8f98cdc2aba93512a28762e","src/error.rs":"38ed38f1d3561685ff7167f3e706d86003952404e63f8ada5c5bf3e9ee32f76c","src/fmt.rs":"079d7b4faaa23f42423e0bb6b4e8a80d7d6d45c38c0d46bebd7d647c8679469f","src/kind.rs":"8481a8b7835eebb3859a8c32c217bf9c73543cfc62e3916b98d39af8b063125c","src/lib.rs":"e10c1dda3d1d40de3606176630885c0eaa0ac78fd58a169fed24004ccd4c37a8","src/macros.rs":"77722190b58a6106b21aefd3b5d4f136a076afcdbc0fae21562d99e2c22912e1","src/wrapper.rs":"1229beca67dbd95ca77c9ecce282272acc55276c267c58cb73a75388b4693dda","tests/common/mod.rs":"f9088c2d7afafa64ff730b629272045b776bfafc2f5957508242da630635f2e1","tests/compiletest.rs":"0a52a44786aea1c299c695bf948b2ed2081e4cc344e5c2cadceab4eb03d0010d","tests/drop/mod.rs":"464bc1ddeae307eac906928286ec3edb77057c5c1302e02150d3649e2b861f1a","tests/test_autotrait.rs":"981e792db353be2f14c7a1cabe43b5f1329c168cb7679077cc2be786a0920d48","tests/test_backtrace.rs":"0e50edbb33b6bd07ba89ff3db72fb7c688ba2a4371fccdbbb20309ab02948b6a","tests/test_boxed.rs":"98a45325b1e86d4c5d3094ab99cd1ada1f771c505d2d7322f0afcbe7bdb71cfa","tests/test_chain.rs":"f28efeae7395d1c395e6f1a647b4199c25a00410ade45248c145c6fcf2fb448a","tests/test_context.rs":"f82c915b182df1a604a4cd558a03b1a821414983d6f6af6822398104cea70676","tests/test_convert.rs":"62840be1ee8022ba5e8c0d3fc1752a1526b2c47d4cceecff2b86790524c3b3ea","tests/test_downcast.rs":"253d6f54e554965023b378b037827ec6289c4779a7a7c12706e19c2731d219fe","tests/test_fmt.rs":"17572596f257aac9aa2ec4620e292ca6a954128b94772bb948399fab53832e70","tests/test_macros.rs":"c7d3d5e0b756f59d4858035025fb341d031369c88486fd9f961ee16bae6c78bf","tests/test_repr.rs":"dbb9b04ddbe1ab31eb5331ea69f05bb3a147299da2275a3d4dcc92947b5591b9","tests/test_source.rs":"b80723cf635a4f8c4df21891b34bfab9ed2b2aa407e7a2f826d24e334cd5f88e","tests/ui/no-impl.rs":"fab6cbf2f6ea510b86f567dfb3b7c31250a9fd71ae5d110dbb9188be569ec593","tests/ui/no-impl.stderr":"7c2c3f46c266a437300591f10be330f937ac6a0a2213ed5030a9fbc895e2d100"},"package":"013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"} \ No newline at end of file
diff --git a/tests/platform_deps/vendor/anyhow/Cargo.toml b/tests/platform_deps/vendor/anyhow/Cargo.toml new file mode 100644 index 0000000..9e2930c --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/Cargo.toml
@@ -0,0 +1,42 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "anyhow" +version = "1.0.27" +authors = ["David Tolnay <dtolnay@gmail.com>"] +description = "Flexible concrete Error type built on std::error::Error" +documentation = "https://docs.rs/anyhow" +readme = "README.md" +categories = ["rust-patterns"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/anyhow" +[dev-dependencies.futures] +version = "0.3" +default-features = false + +[dev-dependencies.rustversion] +version = "1.0" + +[dev-dependencies.thiserror] +version = "1.0" + +[dev-dependencies.trybuild] +version = "1.0.19" +features = ["diff"] + +[features] +default = ["std"] +std = [] +[badges.travis-ci] +repository = "dtolnay/anyhow"
diff --git a/tests/platform_deps/vendor/anyhow/LICENSE-APACHE b/tests/platform_deps/vendor/anyhow/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/LICENSE-APACHE
@@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.
diff --git a/tests/platform_deps/vendor/anyhow/LICENSE-MIT b/tests/platform_deps/vendor/anyhow/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/LICENSE-MIT
@@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
diff --git a/tests/platform_deps/vendor/anyhow/README.md b/tests/platform_deps/vendor/anyhow/README.md new file mode 100644 index 0000000..0c2786b --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/README.md
@@ -0,0 +1,171 @@ +Anyhow ¯\\\_(ツ)\_/¯ +========================= + +[](https://travis-ci.com/dtolnay/anyhow) +[](https://crates.io/crates/anyhow) +[](https://docs.rs/anyhow) + +This library provides [`anyhow::Error`][Error], a trait object based error type +for easy idiomatic error handling in Rust applications. + +[Error]: https://docs.rs/anyhow/1.0/anyhow/struct.Error.html + +```toml +[dependencies] +anyhow = "1.0" +``` + +*Compiler support: requires rustc 1.34+* + +<br> + +## Details + +- Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as the + return type of any fallible function. + + Within the function, use `?` to easily propagate any error that implements the + `std::error::Error` trait. + + ```rust + use anyhow::Result; + + fn get_cluster_info() -> Result<ClusterMap> { + let config = std::fs::read_to_string("cluster.json")?; + let map: ClusterMap = serde_json::from_str(&config)?; + Ok(map) + } + ``` + +- Attach context to help the person troubleshooting the error understand where + things went wrong. A low-level error like "No such file or directory" can be + annoying to debug without more context about what higher level step the + application was in the middle of. + + ```rust + use anyhow::{Context, Result}; + + fn main() -> Result<()> { + ... + it.detach().context("Failed to detach the important thing")?; + + let content = std::fs::read(path) + .with_context(|| format!("Failed to read instrs from {}", path))?; + ... + } + ``` + + ```console + Error: Failed to read instrs from ./path/to/instrs.json + + Caused by: + No such file or directory (os error 2) + ``` + +- Downcasting is supported and can be by value, by shared reference, or by + mutable reference as needed. + + ```rust + // If the error was caused by redaction, then return a + // tombstone instead of the content. + match root_cause.downcast_ref::<DataStoreError>() { + Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), + None => Err(error), + } + ``` + +- A backtrace is captured and printed with the error if the underlying error + type does not already provide its own. In order to see backtraces, they must + be enabled through the environment variables described in [`std::backtrace`]: + + - If you want panics and errors to both have backtraces, set + `RUST_BACKTRACE=1`; + - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; + - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and + `RUST_LIB_BACKTRACE=0`. + + [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables + +- Anyhow works with any error type that has an impl of `std::error::Error`, + including ones defined in your crate. We do not bundle a `derive(Error)` macro + but you can write the impls yourself or use a standalone macro like + [thiserror]. + + ```rust + use thiserror::Error; + + #[derive(Error, Debug)] + pub enum FormatError { + #[error("Invalid header (expected {expected:?}, got {found:?})")] + InvalidHeader { + expected: String, + found: String, + }, + #[error("Missing attribute: {0}")] + MissingAttribute(String), + } + ``` + +- One-off error messages can be constructed using the `anyhow!` macro, which + supports string interpolation and produces an `anyhow::Error`. + + ```rust + return Err(anyhow!("Missing attribute: {}", missing)); + ``` + +<br> + +## No-std support + +In no_std mode, the same API is almost all available and works the same way. To +depend on Anyhow in no_std mode, disable our default enabled "std" feature in +Cargo.toml. A global allocator is required. + +```toml +[dependencies] +anyhow = { version = "1.0", default-features = false } +``` + +Since the `?`-based error conversions would normally rely on the +`std::error::Error` trait which is only available through std, no_std mode will +require an explicit `.map_err(Error::msg)` when working with a non-Anyhow error +type inside a function that returns Anyhow's error type. + +<br> + +## Comparison to failure + +The `anyhow::Error` type works something like `failure::Error`, but unlike +failure ours is built around the standard library's `std::error::Error` trait +rather than a separate trait `failure::Fail`. The standard library has adopted +the necessary improvements for this to be possible as part of [RFC 2504]. + +[RFC 2504]: https://github.com/rust-lang/rfcs/blob/HEAD/text/2504-fix-error.md + +<br> + +## Comparison to thiserror + +Use Anyhow if you don't care what error type your functions return, you just +want it to be easy. This is common in application code. Use [thiserror] if you +are a library that wants to design your own dedicated error type(s) so that on +failures the caller gets exactly the information that you choose. + +[thiserror]: https://github.com/dtolnay/thiserror + +<br> + +#### License + +<sup> +Licensed under either of <a href="LICENSE-APACHE">Apache License, Version +2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. +</sup> + +<br> + +<sub> +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. +</sub>
diff --git a/tests/platform_deps/vendor/anyhow/build.rs b/tests/platform_deps/vendor/anyhow/build.rs new file mode 100644 index 0000000..293167e --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/build.rs
@@ -0,0 +1,61 @@ +use std::path::Path; +use std::process::{Command, ExitStatus}; +use std::{env, fs}; + +// This code exercises the surface area that we expect of the std Backtrace +// type. If the current toolchain is able to compile it, we go ahead and use +// backtrace in anyhow. +const PROBE: &str = r#" + #![feature(backtrace)] + #![allow(dead_code)] + + use std::backtrace::{Backtrace, BacktraceStatus}; + use std::error::Error; + use std::fmt::{self, Display}; + + #[derive(Debug)] + struct E; + + impl Display for E { + fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } + } + + impl Error for E { + fn backtrace(&self) -> Option<&Backtrace> { + let backtrace = Backtrace::capture(); + match backtrace.status() { + BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} + } + unimplemented!() + } + } +"#; + +fn main() { + if !cfg!(feature = "std") { + return; + } + match compile_probe() { + Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"), + _ => {} + } +} + +fn compile_probe() -> Option<ExitStatus> { + let rustc = env::var_os("RUSTC")?; + let out_dir = env::var_os("OUT_DIR")?; + let probefile = Path::new(&out_dir).join("probe.rs"); + fs::write(&probefile, PROBE).ok()?; + Command::new(rustc) + .arg("--edition=2018") + .arg("--crate-name=anyhow_build") + .arg("--crate-type=lib") + .arg("--emit=metadata") + .arg("--out-dir") + .arg(out_dir) + .arg(probefile) + .status() + .ok() +}
diff --git a/tests/platform_deps/vendor/anyhow/src/backtrace.rs b/tests/platform_deps/vendor/anyhow/src/backtrace.rs new file mode 100644 index 0000000..01e33cb --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/backtrace.rs
@@ -0,0 +1,36 @@ +#[cfg(backtrace)] +pub(crate) use std::backtrace::Backtrace; + +#[cfg(not(backtrace))] +pub(crate) enum Backtrace {} + +#[cfg(backtrace)] +macro_rules! backtrace { + () => { + Some(Backtrace::capture()) + }; +} + +#[cfg(not(backtrace))] +macro_rules! backtrace { + () => { + None + }; +} + +#[cfg(backtrace)] +macro_rules! backtrace_if_absent { + ($err:expr) => { + match $err.backtrace() { + Some(_) => None, + None => Some(Backtrace::capture()), + } + }; +} + +#[cfg(all(feature = "std", not(backtrace)))] +macro_rules! backtrace_if_absent { + ($err:expr) => { + None + }; +}
diff --git a/tests/platform_deps/vendor/anyhow/src/chain.rs b/tests/platform_deps/vendor/anyhow/src/chain.rs new file mode 100644 index 0000000..97fd0e3 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/chain.rs
@@ -0,0 +1,95 @@ +use self::ChainState::*; +use crate::StdError; + +#[cfg(feature = "std")] +use std::vec; + +#[cfg(feature = "std")] +pub(crate) use crate::Chain; + +#[cfg(not(feature = "std"))] +pub(crate) struct Chain<'a> { + state: ChainState<'a>, +} + +#[derive(Clone)] +pub(crate) enum ChainState<'a> { + Linked { + next: Option<&'a (dyn StdError + 'static)>, + }, + #[cfg(feature = "std")] + Buffered { + rest: vec::IntoIter<&'a (dyn StdError + 'static)>, + }, +} + +impl<'a> Chain<'a> { + pub fn new(head: &'a (dyn StdError + 'static)) -> Self { + Chain { state: ChainState::Linked { next: Some(head) } } + } +} + +impl<'a> Iterator for Chain<'a> { + type Item = &'a (dyn StdError + 'static); + + fn next(&mut self) -> Option<Self::Item> { + match &mut self.state { + Linked { next } => { + let error = (*next)?; + *next = error.source(); + Some(error) + } + #[cfg(feature = "std")] + Buffered { rest } => rest.next(), + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let len = self.len(); + (len, Some(len)) + } +} + +#[cfg(feature = "std")] +impl DoubleEndedIterator for Chain<'_> { + fn next_back(&mut self) -> Option<Self::Item> { + match &mut self.state { + Linked { mut next } => { + let mut rest = Vec::new(); + while let Some(cause) = next { + next = cause.source(); + rest.push(cause); + } + let mut rest = rest.into_iter(); + let last = rest.next_back(); + self.state = Buffered { rest }; + last + } + Buffered { rest } => rest.next_back(), + } + } +} + +impl ExactSizeIterator for Chain<'_> { + fn len(&self) -> usize { + match &self.state { + Linked { mut next } => { + let mut len = 0; + while let Some(cause) = next { + next = cause.source(); + len += 1; + } + len + } + #[cfg(feature = "std")] + Buffered { rest } => rest.len(), + } + } +} + +#[cfg(feature = "std")] +impl Default for Chain<'_> { + fn default() -> Self { + Chain { state: ChainState::Buffered { rest: Vec::new().into_iter() } } + } +}
diff --git a/tests/platform_deps/vendor/anyhow/src/context.rs b/tests/platform_deps/vendor/anyhow/src/context.rs new file mode 100644 index 0000000..25d3411 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/context.rs
@@ -0,0 +1,177 @@ +use crate::error::ContextError; +use crate::{Context, Error, StdError}; +use core::convert::Infallible; +use core::fmt::{self, Debug, Display, Write}; + +#[cfg(backtrace)] +use std::backtrace::Backtrace; + +mod ext { + use super::*; + + pub trait StdError { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static; + } + + #[cfg(feature = "std")] + impl<E> StdError for E + where + E: std::error::Error + Send + Sync + 'static, + { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static, + { + let backtrace = backtrace_if_absent!(self); + Error::from_context(context, self, backtrace) + } + } + + impl StdError for Error { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static, + { + self.context(context) + } + } +} + +impl<T, E> Context<T, E> for Result<T, E> +where + E: ext::StdError + Send + Sync + 'static, +{ + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + { + self.map_err(|error| error.ext_context(context)) + } + + fn with_context<C, F>(self, context: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + self.map_err(|error| error.ext_context(context())) + } +} + +/// ``` +/// # type T = (); +/// # +/// use anyhow::{Context, Result}; +/// +/// fn maybe_get() -> Option<T> { +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// +/// fn demo() -> Result<()> { +/// let t = maybe_get().context("there is no T")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// ``` +impl<T> Context<T, Infallible> for Option<T> { + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + { + self.ok_or_else(|| Error::from_display(context, backtrace!())) + } + + fn with_context<C, F>(self, context: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + self.ok_or_else(|| Error::from_display(context(), backtrace!())) + } +} + +impl<C, E> Debug for ContextError<C, E> +where + C: Display, + E: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Error") + .field("context", &Quoted(&self.context)) + .field("source", &self.error) + .finish() + } +} + +impl<C, E> Display for ContextError<C, E> +where + C: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.context, f) + } +} + +impl<C, E> StdError for ContextError<C, E> +where + C: Display, + E: StdError + 'static, +{ + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> { + self.error.backtrace() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.error) + } +} + +impl<C> StdError for ContextError<C, Error> +where + C: Display, +{ + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> { + Some(self.error.backtrace()) + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(self.error.inner.error()) + } +} + +struct Quoted<C>(C); + +impl<C> Debug for Quoted<C> +where + C: Display, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_char('"')?; + Quoted(&mut *formatter).write_fmt(format_args!("{}", self.0))?; + formatter.write_char('"')?; + Ok(()) + } +} + +impl Write for Quoted<&mut fmt::Formatter<'_>> { + fn write_str(&mut self, s: &str) -> fmt::Result { + Display::fmt(&s.escape_debug(), self.0) + } +} + +pub(crate) mod private { + use super::*; + + pub trait Sealed {} + + impl<T, E> Sealed for Result<T, E> where E: ext::StdError {} + impl<T> Sealed for Option<T> {} +}
diff --git a/tests/platform_deps/vendor/anyhow/src/error.rs b/tests/platform_deps/vendor/anyhow/src/error.rs new file mode 100644 index 0000000..410a8f4 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/error.rs
@@ -0,0 +1,794 @@ +use crate::alloc::Box; +use crate::backtrace::Backtrace; +use crate::chain::Chain; +use crate::{Error, StdError}; +use core::any::TypeId; +use core::fmt::{self, Debug, Display}; +use core::mem::{self, ManuallyDrop}; +use core::ptr::{self, NonNull}; + +#[cfg(feature = "std")] +use core::ops::{Deref, DerefMut}; + +impl Error { + /// Create a new error object from any error type. + /// + /// The error type must be threadsafe and `'static`, so that the `Error` + /// will be as well. + /// + /// If the error type does not provide a backtrace, a backtrace will be + /// created here to ensure that a backtrace exists. + #[cfg(feature = "std")] + pub fn new<E>(error: E) -> Self + where + E: StdError + Send + Sync + 'static, + { + let backtrace = backtrace_if_absent!(error); + Error::from_std(error, backtrace) + } + + /// Create a new error object from a printable error message. + /// + /// If the argument implements std::error::Error, prefer `Error::new` + /// instead which preserves the underlying error's cause chain and + /// backtrace. If the argument may or may not implement std::error::Error + /// now or in the future, use `anyhow!(err)` which handles either way + /// correctly. + /// + /// `Error::msg("...")` is equivalent to `anyhow!("...")` but occasionally + /// convenient in places where a function is preferable over a macro, such + /// as iterator or stream combinators: + /// + /// ``` + /// # mod ffi { + /// # pub struct Input; + /// # pub struct Output; + /// # pub async fn do_some_work(_: Input) -> Result<Output, &'static str> { + /// # unimplemented!() + /// # } + /// # } + /// # + /// # use ffi::{Input, Output}; + /// # + /// use anyhow::{Error, Result}; + /// use futures::stream::{Stream, StreamExt, TryStreamExt}; + /// + /// async fn demo<S>(stream: S) -> Result<Vec<Output>> + /// where + /// S: Stream<Item = Input>, + /// { + /// stream + /// .then(ffi::do_some_work) // returns Result<Output, &str> + /// .map_err(Error::msg) + /// .try_collect() + /// .await + /// } + /// ``` + pub fn msg<M>(message: M) -> Self + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } + + #[cfg(feature = "std")] + pub(crate) fn from_std<E>(error: E, backtrace: Option<Backtrace>) -> Self + where + E: StdError + Send + Sync + 'static, + { + let vtable = &ErrorVTable { + object_drop: object_drop::<E>, + object_ref: object_ref::<E>, + #[cfg(feature = "std")] + object_mut: object_mut::<E>, + object_boxed: object_boxed::<E>, + object_downcast: object_downcast::<E>, + object_drop_rest: object_drop_front::<E>, + }; + + // Safety: passing vtable that operates on the right type E. + unsafe { Error::construct(error, vtable, backtrace) } + } + + pub(crate) fn from_adhoc<M>(message: M, backtrace: Option<Backtrace>) -> Self + where + M: Display + Debug + Send + Sync + 'static, + { + use crate::wrapper::MessageError; + let error: MessageError<M> = MessageError(message); + let vtable = &ErrorVTable { + object_drop: object_drop::<MessageError<M>>, + object_ref: object_ref::<MessageError<M>>, + #[cfg(feature = "std")] + object_mut: object_mut::<MessageError<M>>, + object_boxed: object_boxed::<MessageError<M>>, + object_downcast: object_downcast::<M>, + object_drop_rest: object_drop_front::<M>, + }; + + // Safety: MessageError is repr(transparent) so it is okay for the + // vtable to allow casting the MessageError<M> to M. + unsafe { Error::construct(error, vtable, backtrace) } + } + + pub(crate) fn from_display<M>(message: M, backtrace: Option<Backtrace>) -> Self + where + M: Display + Send + Sync + 'static, + { + use crate::wrapper::DisplayError; + let error: DisplayError<M> = DisplayError(message); + let vtable = &ErrorVTable { + object_drop: object_drop::<DisplayError<M>>, + object_ref: object_ref::<DisplayError<M>>, + #[cfg(feature = "std")] + object_mut: object_mut::<DisplayError<M>>, + object_boxed: object_boxed::<DisplayError<M>>, + object_downcast: object_downcast::<M>, + object_drop_rest: object_drop_front::<M>, + }; + + // Safety: DisplayError is repr(transparent) so it is okay for the + // vtable to allow casting the DisplayError<M> to M. + unsafe { Error::construct(error, vtable, backtrace) } + } + + #[cfg(feature = "std")] + pub(crate) fn from_context<C, E>(context: C, error: E, backtrace: Option<Backtrace>) -> Self + where + C: Display + Send + Sync + 'static, + E: StdError + Send + Sync + 'static, + { + let error: ContextError<C, E> = ContextError { context, error }; + + let vtable = &ErrorVTable { + object_drop: object_drop::<ContextError<C, E>>, + object_ref: object_ref::<ContextError<C, E>>, + #[cfg(feature = "std")] + object_mut: object_mut::<ContextError<C, E>>, + object_boxed: object_boxed::<ContextError<C, E>>, + object_downcast: context_downcast::<C, E>, + object_drop_rest: context_drop_rest::<C, E>, + }; + + // Safety: passing vtable that operates on the right type. + unsafe { Error::construct(error, vtable, backtrace) } + } + + #[cfg(feature = "std")] + pub(crate) fn from_boxed( + error: Box<dyn StdError + Send + Sync>, + backtrace: Option<Backtrace>, + ) -> Self { + use crate::wrapper::BoxedError; + let error = BoxedError(error); + let vtable = &ErrorVTable { + object_drop: object_drop::<BoxedError>, + object_ref: object_ref::<BoxedError>, + #[cfg(feature = "std")] + object_mut: object_mut::<BoxedError>, + object_boxed: object_boxed::<BoxedError>, + object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>, + object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>, + }; + + // Safety: BoxedError is repr(transparent) so it is okay for the vtable + // to allow casting to Box<dyn StdError + Send + Sync>. + unsafe { Error::construct(error, vtable, backtrace) } + } + + // Takes backtrace as argument rather than capturing it here so that the + // user sees one fewer layer of wrapping noise in the backtrace. + // + // Unsafe because the given vtable must have sensible behavior on the error + // value of type E. + unsafe fn construct<E>( + error: E, + vtable: &'static ErrorVTable, + backtrace: Option<Backtrace>, + ) -> Self + where + E: StdError + Send + Sync + 'static, + { + let inner = Box::new(ErrorImpl { vtable, backtrace, _object: error }); + // Erase the concrete type of E from the compile-time type system. This + // is equivalent to the safe unsize coersion from Box<ErrorImpl<E>> to + // Box<ErrorImpl<dyn StdError + Send + Sync + 'static>> except that the + // result is a thin pointer. The necessary behavior for manipulating the + // underlying ErrorImpl<E> is preserved in the vtable provided by the + // caller rather than a builtin fat pointer vtable. + let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner); + let inner = ManuallyDrop::new(erased); + Error { inner } + } + + /// Wrap the error value with additional context. + /// + /// For attaching context to a `Result` as it is propagated, the + /// [`Context`][crate::Context] extension trait may be more convenient than + /// this function. + /// + /// The primary reason to use `error.context(...)` instead of + /// `result.context(...)` via the `Context` trait would be if the context + /// needs to depend on some data held by the underlying error: + /// + /// ``` + /// # use std::fmt::{self, Debug, Display}; + /// # + /// # type T = (); + /// # + /// # impl std::error::Error for ParseError {} + /// # impl Debug for ParseError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # impl Display for ParseError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # + /// use anyhow::Result; + /// use std::fs::File; + /// use std::path::Path; + /// + /// struct ParseError { + /// line: usize, + /// column: usize, + /// } + /// + /// fn parse_impl(file: File) -> Result<T, ParseError> { + /// # const IGNORE: &str = stringify! { + /// ... + /// # }; + /// # unimplemented!() + /// } + /// + /// pub fn parse(path: impl AsRef<Path>) -> Result<T> { + /// let file = File::open(&path)?; + /// parse_impl(file).map_err(|error| { + /// let context = format!( + /// "only the first {} lines of {} are valid", + /// error.line, path.as_ref().display(), + /// ); + /// anyhow::Error::new(error).context(context) + /// }) + /// } + /// ``` + pub fn context<C>(self, context: C) -> Self + where + C: Display + Send + Sync + 'static, + { + let error: ContextError<C, Error> = ContextError { context, error: self }; + + let vtable = &ErrorVTable { + object_drop: object_drop::<ContextError<C, Error>>, + object_ref: object_ref::<ContextError<C, Error>>, + #[cfg(feature = "std")] + object_mut: object_mut::<ContextError<C, Error>>, + object_boxed: object_boxed::<ContextError<C, Error>>, + object_downcast: context_chain_downcast::<C>, + object_drop_rest: context_chain_drop_rest::<C>, + }; + + // As the cause is anyhow::Error, we already have a backtrace for it. + let backtrace = None; + + // Safety: passing vtable that operates on the right type. + unsafe { Error::construct(error, vtable, backtrace) } + } + + /// Get the backtrace for this Error. + /// + /// Backtraces are only available on the nightly channel. Tracking issue: + /// [rust-lang/rust#53487][tracking]. + /// + /// In order for the backtrace to be meaningful, one of the two environment + /// variables `RUST_LIB_BACKTRACE=1` or `RUST_BACKTRACE=1` must be defined + /// and `RUST_LIB_BACKTRACE` must not be `0`. Backtraces are somewhat + /// expensive to capture in Rust, so we don't necessarily want to be + /// capturing them all over the place all the time. + /// + /// - If you want panics and errors to both have backtraces, set + /// `RUST_BACKTRACE=1`; + /// - If you want only errors to have backtraces, set + /// `RUST_LIB_BACKTRACE=1`; + /// - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and + /// `RUST_LIB_BACKTRACE=0`. + /// + /// [tracking]: https://github.com/rust-lang/rust/issues/53487 + #[cfg(backtrace)] + pub fn backtrace(&self) -> &Backtrace { + self.inner.backtrace() + } + + /// An iterator of the chain of source errors contained by this Error. + /// + /// This iterator will visit every error in the cause chain of this error + /// object, beginning with the error that this error object was created + /// from. + /// + /// # Example + /// + /// ``` + /// use anyhow::Error; + /// use std::io; + /// + /// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> { + /// for cause in error.chain() { + /// if let Some(io_error) = cause.downcast_ref::<io::Error>() { + /// return Some(io_error.kind()); + /// } + /// } + /// None + /// } + /// ``` + #[cfg(feature = "std")] + pub fn chain(&self) -> Chain { + self.inner.chain() + } + + /// The lowest level cause of this error — this error's cause's + /// cause's cause etc. + /// + /// The root cause is the last error in the iterator produced by + /// [`chain()`][Error::chain]. + #[cfg(feature = "std")] + pub fn root_cause(&self) -> &(dyn StdError + 'static) { + let mut chain = self.chain(); + let mut root_cause = chain.next().unwrap(); + for cause in chain { + root_cause = cause; + } + root_cause + } + + /// Returns true if `E` is the type held by this error object. + /// + /// For errors with context, this method returns true if `E` matches the + /// type of the context `C` **or** the type of the error on which the + /// context has been attached. For details about the interaction between + /// context and downcasting, [see here]. + /// + /// [see here]: trait.Context.html#effect-on-downcasting + pub fn is<E>(&self) -> bool + where + E: Display + Debug + Send + Sync + 'static, + { + self.downcast_ref::<E>().is_some() + } + + /// Attempt to downcast the error object to a concrete type. + pub fn downcast<E>(self) -> Result<E, Self> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = match (self.inner.vtable.object_downcast)(&self.inner, target) { + Some(addr) => addr, + None => return Err(self), + }; + + // Prepare to read E out of the data structure. We'll drop the rest + // of the data structure separately so that E is not dropped. + let outer = ManuallyDrop::new(self); + + // Read E from where the vtable found it. + let error = ptr::read(addr.cast::<E>().as_ptr()); + + // Read Box<ErrorImpl<()>> from self. Can't move it out because + // Error has a Drop impl which we want to not run. + let inner = ptr::read(&outer.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Drop rest of the data structure outside of E. + (erased.vtable.object_drop_rest)(erased, target); + + Ok(error) + } + } + + /// Downcast this error object by reference. + /// + /// # Example + /// + /// ``` + /// # use anyhow::anyhow; + /// # use std::fmt::{self, Display}; + /// # use std::task::Poll; + /// # + /// # #[derive(Debug)] + /// # enum DataStoreError { + /// # Censored(()), + /// # } + /// # + /// # impl Display for DataStoreError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # + /// # impl std::error::Error for DataStoreError {} + /// # + /// # const REDACTED_CONTENT: () = (); + /// # + /// # let error = anyhow!("..."); + /// # let root_cause = &error; + /// # + /// # let ret = + /// // If the error was caused by redaction, then return a tombstone instead + /// // of the content. + /// match root_cause.downcast_ref::<DataStoreError>() { + /// Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), + /// None => Err(error), + /// } + /// # ; + /// ``` + pub fn downcast_ref<E>(&self) -> Option<&E> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&*addr.cast::<E>().as_ptr()) + } + } + + /// Downcast this error object by mutable reference. + pub fn downcast_mut<E>(&mut self) -> Option<&mut E> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&mut *addr.cast::<E>().as_ptr()) + } + } +} + +#[cfg(feature = "std")] +impl<E> From<E> for Error +where + E: StdError + Send + Sync + 'static, +{ + fn from(error: E) -> Self { + let backtrace = backtrace_if_absent!(error); + Error::from_std(error, backtrace) + } +} + +#[cfg(feature = "std")] +impl Deref for Error { + type Target = dyn StdError + Send + Sync + 'static; + + fn deref(&self) -> &Self::Target { + self.inner.error() + } +} + +#[cfg(feature = "std")] +impl DerefMut for Error { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.error_mut() + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.inner.display(formatter) + } +} + +impl Debug for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.inner.debug(formatter) + } +} + +impl Drop for Error { + fn drop(&mut self) { + unsafe { + // Read Box<ErrorImpl<()>> from self. + let inner = ptr::read(&self.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Invoke the vtable's drop behavior. + (erased.vtable.object_drop)(erased); + } + } +} + +struct ErrorVTable { + object_drop: unsafe fn(Box<ErrorImpl<()>>), + object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static), + #[cfg(feature = "std")] + object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static), + object_boxed: unsafe fn(Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>, + object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option<NonNull<()>>, + object_drop_rest: unsafe fn(Box<ErrorImpl<()>>, TypeId), +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) { + // Cast back to ErrorImpl<E> so that the allocator receives the correct + // Layout to deallocate the Box's memory. + let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e); + drop(unerased); +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) { + // Drop the fields of ErrorImpl other than E as well as the Box allocation, + // without dropping E itself. This is used by downcast after doing a + // ptr::read to take ownership of the E. + let _ = target; + let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<ManuallyDrop<E>>>>(e); + drop(unerased); +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_ref<E>(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static) +where + E: StdError + Send + Sync + 'static, +{ + // Attach E's native StdError vtable onto a pointer to self._object. + &(*(e as *const ErrorImpl<()> as *const ErrorImpl<E>))._object +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +#[cfg(feature = "std")] +unsafe fn object_mut<E>(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static) +where + E: StdError + Send + Sync + 'static, +{ + // Attach E's native StdError vtable onto a pointer to self._object. + &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl<E>))._object +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_boxed<E>(e: Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static> +where + E: StdError + Send + Sync + 'static, +{ + // Attach ErrorImpl<E>'s native StdError vtable. The StdError impl is below. + mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e) +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_downcast<E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + E: 'static, +{ + if TypeId::of::<E>() == target { + // Caller is looking for an E pointer and e is ErrorImpl<E>, take a + // pointer to its E field. + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<E>; + let addr = &(*unerased)._object as *const E as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + None + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>. +#[cfg(feature = "std")] +unsafe fn context_downcast<C, E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + C: 'static, + E: 'static, +{ + if TypeId::of::<C>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>; + let addr = &(*unerased)._object.context as *const C as *mut (); + Some(NonNull::new_unchecked(addr)) + } else if TypeId::of::<E>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>; + let addr = &(*unerased)._object.error as *const E as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + None + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>. +#[cfg(feature = "std")] +unsafe fn context_drop_rest<C, E>(e: Box<ErrorImpl<()>>, target: TypeId) +where + C: 'static, + E: 'static, +{ + // Called after downcasting by value to either the C or the E and doing a + // ptr::read to take ownership of that value. + if TypeId::of::<C>() == target { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<ManuallyDrop<C>, E>>>, + >(e); + drop(unerased); + } else { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<C, ManuallyDrop<E>>>>, + >(e); + drop(unerased); + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>. +unsafe fn context_chain_downcast<C>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + C: 'static, +{ + if TypeId::of::<C>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>; + let addr = &(*unerased)._object.context as *const C as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + // Recurse down the context chain per the inner error's vtable. + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>; + let source = &(*unerased)._object.error; + (source.inner.vtable.object_downcast)(&source.inner, target) + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>. +unsafe fn context_chain_drop_rest<C>(e: Box<ErrorImpl<()>>, target: TypeId) +where + C: 'static, +{ + // Called after downcasting by value to either the C or one of the causes + // and doing a ptr::read to take ownership of that value. + if TypeId::of::<C>() == target { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<ManuallyDrop<C>, Error>>>, + >(e); + // Drop the entire rest of the data structure rooted in the next Error. + drop(unerased); + } else { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<C, ManuallyDrop<Error>>>>, + >(e); + // Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error. + let inner = ptr::read(&unerased._object.error.inner); + drop(unerased); + let erased = ManuallyDrop::into_inner(inner); + // Recursively drop the next error using the same target typeid. + (erased.vtable.object_drop_rest)(erased, target); + } +} + +// repr C to ensure that E remains in the final position. +#[repr(C)] +pub(crate) struct ErrorImpl<E> { + vtable: &'static ErrorVTable, + backtrace: Option<Backtrace>, + // NOTE: Don't use directly. Use only through vtable. Erased type may have + // different alignment. + _object: E, +} + +// repr C to ensure that ContextError<C, E> has the same layout as +// ContextError<ManuallyDrop<C>, E> and ContextError<C, ManuallyDrop<E>>. +#[repr(C)] +pub(crate) struct ContextError<C, E> { + pub context: C, + pub error: E, +} + +impl<E> ErrorImpl<E> { + fn erase(&self) -> &ErrorImpl<()> { + // Erase the concrete type of E but preserve the vtable in self.vtable + // for manipulating the resulting thin pointer. This is analogous to an + // unsize coersion. + unsafe { &*(self as *const ErrorImpl<E> as *const ErrorImpl<()>) } + } +} + +impl ErrorImpl<()> { + pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) { + // Use vtable to attach E's native StdError vtable for the right + // original type E. + unsafe { &*(self.vtable.object_ref)(self) } + } + + #[cfg(feature = "std")] + pub(crate) fn error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) { + // Use vtable to attach E's native StdError vtable for the right + // original type E. + unsafe { &mut *(self.vtable.object_mut)(self) } + } + + #[cfg(backtrace)] + pub(crate) fn backtrace(&self) -> &Backtrace { + // This unwrap can only panic if the underlying error's backtrace method + // is nondeterministic, which would only happen in maliciously + // constructed code. + self.backtrace + .as_ref() + .or_else(|| self.error().backtrace()) + .expect("backtrace capture failed") + } + + pub(crate) fn chain(&self) -> Chain { + Chain::new(self.error()) + } +} + +impl<E> StdError for ErrorImpl<E> +where + E: StdError, +{ + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> { + Some(self.erase().backtrace()) + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.erase().error().source() + } +} + +impl<E> Debug for ErrorImpl<E> +where + E: Debug, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.erase().debug(formatter) + } +} + +impl<E> Display for ErrorImpl<E> +where + E: Display, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.erase().error(), formatter) + } +} + +impl From<Error> for Box<dyn StdError + Send + Sync + 'static> { + fn from(error: Error) -> Self { + let outer = ManuallyDrop::new(error); + unsafe { + // Read Box<ErrorImpl<()>> from error. Can't move it out because + // Error has a Drop impl which we want to not run. + let inner = ptr::read(&outer.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Use vtable to attach ErrorImpl<E>'s native StdError vtable for + // the right original type E. + (erased.vtable.object_boxed)(erased) + } + } +} + +impl From<Error> for Box<dyn StdError + 'static> { + fn from(error: Error) -> Self { + Box::<dyn StdError + Send + Sync>::from(error) + } +} + +#[cfg(feature = "std")] +impl AsRef<dyn StdError + Send + Sync> for Error { + fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { + &**self + } +} + +#[cfg(feature = "std")] +impl AsRef<dyn StdError> for Error { + fn as_ref(&self) -> &(dyn StdError + 'static) { + &**self + } +}
diff --git a/tests/platform_deps/vendor/anyhow/src/fmt.rs b/tests/platform_deps/vendor/anyhow/src/fmt.rs new file mode 100644 index 0000000..9579478 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/fmt.rs
@@ -0,0 +1,131 @@ +use crate::chain::Chain; +use crate::error::ErrorImpl; +use core::fmt::{self, Debug, Write}; + +impl ErrorImpl<()> { + pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.error())?; + + if f.alternate() { + for cause in self.chain().skip(1) { + write!(f, ": {}", cause)?; + } + } + + Ok(()) + } + + pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { + let error = self.error(); + + if f.alternate() { + return Debug::fmt(error, f); + } + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + let multiple = cause.source().is_some(); + for (n, error) in Chain::new(cause).enumerate() { + writeln!(f)?; + let mut indented = Indented { + inner: f, + number: if multiple { Some(n) } else { None }, + started: false, + }; + write!(indented, "{}", error)?; + } + } + + #[cfg(backtrace)] + { + use std::backtrace::BacktraceStatus; + + let backtrace = self.backtrace(); + if let BacktraceStatus::Captured = backtrace.status() { + let mut backtrace = backtrace.to_string(); + if backtrace.starts_with("stack backtrace:") { + // Capitalize to match "Caused by:" + backtrace.replace_range(0..1, "S"); + } + backtrace.truncate(backtrace.trim_end().len()); + write!(f, "\n\n{}", backtrace)?; + } + } + + Ok(()) + } +} + +struct Indented<'a, D> { + inner: &'a mut D, + number: Option<usize>, + started: bool, +} + +impl<T> Write for Indented<'_, T> +where + T: Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if !self.started { + self.started = true; + match self.number { + Some(number) => write!(self.inner, "{: >5}: ", number)?, + None => self.inner.write_str(" ")?, + } + } else if i > 0 { + self.inner.write_char('\n')?; + if self.number.is_some() { + self.inner.write_str(" ")?; + } else { + self.inner.write_str(" ")?; + } + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn one_digit() { + let input = "verify\nthis"; + let expected = " 2: verify\n this"; + let mut output = String::new(); + + Indented { inner: &mut output, number: Some(2), started: false }.write_str(input).unwrap(); + + assert_eq!(expected, output); + } + + #[test] + fn two_digits() { + let input = "verify\nthis"; + let expected = " 12: verify\n this"; + let mut output = String::new(); + + Indented { inner: &mut output, number: Some(12), started: false }.write_str(input).unwrap(); + + assert_eq!(expected, output); + } + + #[test] + fn no_digits() { + let input = "verify\nthis"; + let expected = " verify\n this"; + let mut output = String::new(); + + Indented { inner: &mut output, number: None, started: false }.write_str(input).unwrap(); + + assert_eq!(expected, output); + } +}
diff --git a/tests/platform_deps/vendor/anyhow/src/kind.rs b/tests/platform_deps/vendor/anyhow/src/kind.rs new file mode 100644 index 0000000..fdeb060 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/kind.rs
@@ -0,0 +1,116 @@ +// Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`. +// +// When anyhow! is given a single expr argument to turn into anyhow::Error, we +// want the resulting Error to pick up the input's implementation of source() +// and backtrace() if it has a std::error::Error impl, otherwise require nothing +// more than Display and Debug. +// +// Expressed in terms of specialization, we want something like: +// +// trait AnyhowNew { +// fn new(self) -> Error; +// } +// +// impl<T> AnyhowNew for T +// where +// T: Display + Debug + Send + Sync + 'static, +// { +// default fn new(self) -> Error { +// /* no std error impl */ +// } +// } +// +// impl<T> AnyhowNew for T +// where +// T: std::error::Error + Send + Sync + 'static, +// { +// fn new(self) -> Error { +// /* use std error's source() and backtrace() */ +// } +// } +// +// Since specialization is not stable yet, instead we rely on autoref behavior +// of method resolution to perform tagged dispatch. Here we have two traits +// AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is +// implemented whether or not the caller's type has a std error impl, while +// TraitKind is implemented only when a std error impl does exist. The ambiguity +// is resolved by AdhocKind requiring an extra autoref so that it has lower +// precedence. +// +// The anyhow! macro will set up the call in this form: +// +// #[allow(unused_imports)] +// use $crate::private::{AdhocKind, TraitKind}; +// let error = $msg; +// (&error).anyhow_kind().new(error) + +use crate::Error; +use core::fmt::{Debug, Display}; + +#[cfg(feature = "std")] +use crate::StdError; + +#[cfg(backtrace)] +use std::backtrace::Backtrace; + +pub struct Adhoc; + +pub trait AdhocKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Adhoc { + Adhoc + } +} + +impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} + +impl Adhoc { + pub fn new<M>(self, message: M) -> Error + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } +} + +pub struct Trait; + +pub trait TraitKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Trait { + Trait + } +} + +impl<E> TraitKind for E where E: Into<Error> {} + +impl Trait { + pub fn new<E>(self, error: E) -> Error + where + E: Into<Error>, + { + error.into() + } +} + +#[cfg(feature = "std")] +pub struct Boxed; + +#[cfg(feature = "std")] +pub trait BoxedKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Boxed { + Boxed + } +} + +#[cfg(feature = "std")] +impl BoxedKind for Box<dyn StdError + Send + Sync> {} + +#[cfg(feature = "std")] +impl Boxed { + pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error { + let backtrace = backtrace_if_absent!(error); + Error::from_boxed(error, backtrace) + } +}
diff --git a/tests/platform_deps/vendor/anyhow/src/lib.rs b/tests/platform_deps/vendor/anyhow/src/lib.rs new file mode 100644 index 0000000..3e49658 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/lib.rs
@@ -0,0 +1,587 @@ +//! This library provides [`anyhow::Error`][Error], a trait object based error +//! type for easy idiomatic error handling in Rust applications. +//! +//! <br> +//! +//! # Details +//! +//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as +//! the return type of any fallible function. +//! +//! Within the function, use `?` to easily propagate any error that implements +//! the `std::error::Error` trait. +//! +//! ``` +//! # pub trait Deserialize {} +//! # +//! # mod serde_json { +//! # use super::Deserialize; +//! # use std::io; +//! # +//! # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { +//! # unimplemented!() +//! # } +//! # } +//! # +//! # struct ClusterMap; +//! # +//! # impl Deserialize for ClusterMap {} +//! # +//! use anyhow::Result; +//! +//! fn get_cluster_info() -> Result<ClusterMap> { +//! let config = std::fs::read_to_string("cluster.json")?; +//! let map: ClusterMap = serde_json::from_str(&config)?; +//! Ok(map) +//! } +//! # +//! # fn main() {} +//! ``` +//! +//! - Attach context to help the person troubleshooting the error understand +//! where things went wrong. A low-level error like "No such file or +//! directory" can be annoying to debug without more context about what higher +//! level step the application was in the middle of. +//! +//! ``` +//! # struct It; +//! # +//! # impl It { +//! # fn detach(&self) -> Result<()> { +//! # unimplemented!() +//! # } +//! # } +//! # +//! use anyhow::{Context, Result}; +//! +//! fn main() -> Result<()> { +//! # return Ok(()); +//! # +//! # const _: &str = stringify! { +//! ... +//! # }; +//! # +//! # let it = It; +//! # let path = "./path/to/instrs.json"; +//! # +//! it.detach().context("Failed to detach the important thing")?; +//! +//! let content = std::fs::read(path) +//! .with_context(|| format!("Failed to read instrs from {}", path))?; +//! # +//! # const _: &str = stringify! { +//! ... +//! # }; +//! # +//! # Ok(()) +//! } +//! ``` +//! +//! ```console +//! Error: Failed to read instrs from ./path/to/instrs.json +//! +//! Caused by: +//! No such file or directory (os error 2) +//! ``` +//! +//! - Downcasting is supported and can be by value, by shared reference, or by +//! mutable reference as needed. +//! +//! ``` +//! # use anyhow::anyhow; +//! # use std::fmt::{self, Display}; +//! # use std::task::Poll; +//! # +//! # #[derive(Debug)] +//! # enum DataStoreError { +//! # Censored(()), +//! # } +//! # +//! # impl Display for DataStoreError { +//! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +//! # unimplemented!() +//! # } +//! # } +//! # +//! # impl std::error::Error for DataStoreError {} +//! # +//! # const REDACTED_CONTENT: () = (); +//! # +//! # let error = anyhow!("..."); +//! # let root_cause = &error; +//! # +//! # let ret = +//! // If the error was caused by redaction, then return a +//! // tombstone instead of the content. +//! match root_cause.downcast_ref::<DataStoreError>() { +//! Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), +//! None => Err(error), +//! } +//! # ; +//! ``` +//! +//! - A backtrace is captured and printed with the error if the underlying error +//! type does not already provide its own. In order to see backtraces, they +//! must be enabled through the environment variables described in +//! [`std::backtrace`]: +//! +//! - If you want panics and errors to both have backtraces, set +//! `RUST_BACKTRACE=1`; +//! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; +//! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and +//! `RUST_LIB_BACKTRACE=0`. +//! +//! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables +//! +//! - Anyhow works with any error type that has an impl of `std::error::Error`, +//! including ones defined in your crate. We do not bundle a `derive(Error)` +//! macro but you can write the impls yourself or use a standalone macro like +//! [thiserror]. +//! +//! [thiserror]: https://github.com/dtolnay/thiserror +//! +//! ``` +//! use thiserror::Error; +//! +//! #[derive(Error, Debug)] +//! pub enum FormatError { +//! #[error("Invalid header (expected {expected:?}, got {found:?})")] +//! InvalidHeader { +//! expected: String, +//! found: String, +//! }, +//! #[error("Missing attribute: {0}")] +//! MissingAttribute(String), +//! } +//! ``` +//! +//! - One-off error messages can be constructed using the `anyhow!` macro, which +//! supports string interpolation and produces an `anyhow::Error`. +//! +//! ``` +//! # use anyhow::{anyhow, Result}; +//! # +//! # fn demo() -> Result<()> { +//! # let missing = "..."; +//! return Err(anyhow!("Missing attribute: {}", missing)); +//! # Ok(()) +//! # } +//! ``` +//! +//! <br> +//! +//! # No-std support +//! +//! In no_std mode, the same API is almost all available and works the same way. +//! To depend on Anyhow in no_std mode, disable our default enabled "std" +//! feature in Cargo.toml. A global allocator is required. +//! +//! ```toml +//! [dependencies] +//! anyhow = { version = "1.0", default-features = false } +//! ``` +//! +//! Since the `?`-based error conversions would normally rely on the +//! `std::error::Error` trait which is only available through std, no_std mode +//! will require an explicit `.map_err(Error::msg)` when working with a +//! non-Anyhow error type inside a function that returns Anyhow's error type. + +#![doc(html_root_url = "https://docs.rs/anyhow/1.0.27")] +#![cfg_attr(backtrace, feature(backtrace))] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::needless_doctest_main, clippy::new_ret_no_self, clippy::wrong_self_convention)] + +mod alloc { + #[cfg(not(feature = "std"))] + extern crate alloc; + + #[cfg(not(feature = "std"))] + pub use alloc::boxed::Box; + + #[cfg(feature = "std")] + pub use std::boxed::Box; +} + +#[macro_use] +mod backtrace; +mod chain; +mod context; +mod error; +mod fmt; +mod kind; +mod macros; +mod wrapper; + +use crate::alloc::Box; +use crate::error::ErrorImpl; +use core::fmt::Display; +use core::mem::ManuallyDrop; + +#[cfg(not(feature = "std"))] +use core::fmt::Debug; + +#[cfg(feature = "std")] +use std::error::Error as StdError; + +#[cfg(not(feature = "std"))] +trait StdError: Debug + Display { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + None + } +} + +pub use anyhow as format_err; + +/// The `Error` type, a wrapper around a dynamic error type. +/// +/// `Error` works a lot like `Box<dyn std::error::Error>`, but with these +/// differences: +/// +/// - `Error` requires that the error is `Send`, `Sync`, and `'static`. +/// - `Error` guarantees that a backtrace is available, even if the underlying +/// error type does not provide one. +/// - `Error` is represented as a narrow pointer — exactly one word in +/// size instead of two. +/// +/// <br> +/// +/// # Display representations +/// +/// When you print an error object using "{}" or to_string(), only the outermost +/// underlying error or context is printed, not any of the lower level causes. +/// This is exactly as if you had called the Display impl of the error from +/// which you constructed your anyhow::Error. +/// +/// ```console +/// Failed to read instrs from ./path/to/instrs.json +/// ``` +/// +/// To print causes as well using anyhow's default formatting of causes, use the +/// alternate selector "{:#}". +/// +/// ```console +/// Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2) +/// ``` +/// +/// The Debug format "{:?}" includes your backtrace if one was captured. Note +/// that this is the representation you get by default if you return an error +/// from `fn main` instead of printing it explicitly yourself. +/// +/// ```console +/// Error: Failed to read instrs from ./path/to/instrs.json +/// +/// Caused by: +/// No such file or directory (os error 2) +/// +/// Stack backtrace: +/// 0: <E as anyhow::context::ext::StdError>::ext_context +/// at /git/anyhow/src/backtrace.rs:26 +/// 1: core::result::Result<T,E>::map_err +/// at /git/rustc/src/libcore/result.rs:596 +/// 2: anyhow::context::<impl anyhow::Context<T,E> for core::result::Result<T,E>>::with_context +/// at /git/anyhow/src/context.rs:58 +/// 3: testing::main +/// at src/main.rs:5 +/// 4: std::rt::lang_start +/// at /git/rustc/src/libstd/rt.rs:61 +/// 5: main +/// 6: __libc_start_main +/// 7: _start +/// ``` +/// +/// To see a conventional struct-style Debug representation, use "{:#?}". +/// +/// ```console +/// Error { +/// context: "Failed to read instrs from ./path/to/instrs.json", +/// source: Os { +/// code: 2, +/// kind: NotFound, +/// message: "No such file or directory", +/// }, +/// } +/// ``` +/// +/// If none of the built-in representations are appropriate and you would prefer +/// to render the error and its cause chain yourself, it can be done something +/// like this: +/// +/// ``` +/// use anyhow::{Context, Result}; +/// +/// fn main() { +/// if let Err(err) = try_main() { +/// eprintln!("ERROR: {}", err); +/// err.chain().skip(1).for_each(|cause| eprintln!("because: {}", cause)); +/// std::process::exit(1); +/// } +/// } +/// +/// fn try_main() -> Result<()> { +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # Ok(()) +/// } +/// ``` +pub struct Error { + inner: ManuallyDrop<Box<ErrorImpl<()>>>, +} + +/// Iterator of a chain of source errors. +/// +/// This type is the iterator returned by [`Error::chain`]. +/// +/// # Example +/// +/// ``` +/// use anyhow::Error; +/// use std::io; +/// +/// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> { +/// for cause in error.chain() { +/// if let Some(io_error) = cause.downcast_ref::<io::Error>() { +/// return Some(io_error.kind()); +/// } +/// } +/// None +/// } +/// ``` +#[cfg(feature = "std")] +#[derive(Clone)] +pub struct Chain<'a> { + state: crate::chain::ChainState<'a>, +} + +/// `Result<T, Error>` +/// +/// This is a reasonable return type to use throughout your application but also +/// for `fn main`; if you do, failures will be printed along with any +/// [context][Context] and a backtrace if one was captured. +/// +/// `anyhow::Result` may be used with one *or* two type parameters. +/// +/// ```rust +/// use anyhow::Result; +/// +/// # const IGNORE: &str = stringify! { +/// fn demo1() -> Result<T> {...} +/// // ^ equivalent to std::result::Result<T, anyhow::Error> +/// +/// fn demo2() -> Result<T, OtherError> {...} +/// // ^ equivalent to std::result::Result<T, OtherError> +/// # }; +/// ``` +/// +/// # Example +/// +/// ``` +/// # pub trait Deserialize {} +/// # +/// # mod serde_json { +/// # use super::Deserialize; +/// # use std::io; +/// # +/// # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { +/// # unimplemented!() +/// # } +/// # } +/// # +/// # #[derive(Debug)] +/// # struct ClusterMap; +/// # +/// # impl Deserialize for ClusterMap {} +/// # +/// use anyhow::Result; +/// +/// fn main() -> Result<()> { +/// # return Ok(()); +/// let config = std::fs::read_to_string("cluster.json")?; +/// let map: ClusterMap = serde_json::from_str(&config)?; +/// println!("cluster info: {:#?}", map); +/// Ok(()) +/// } +/// ``` +pub type Result<T, E = Error> = core::result::Result<T, E>; + +/// Provides the `context` method for `Result`. +/// +/// This trait is sealed and cannot be implemented for types outside of +/// `anyhow`. +/// +/// <br> +/// +/// # Example +/// +/// ``` +/// use anyhow::{Context, Result}; +/// use std::fs; +/// use std::path::PathBuf; +/// +/// pub struct ImportantThing { +/// path: PathBuf, +/// } +/// +/// impl ImportantThing { +/// # const IGNORE: &'static str = stringify! { +/// pub fn detach(&mut self) -> Result<()> {...} +/// # }; +/// # fn detach(&mut self) -> Result<()> { +/// # unimplemented!() +/// # } +/// } +/// +/// pub fn do_it(mut it: ImportantThing) -> Result<Vec<u8>> { +/// it.detach().context("Failed to detach the important thing")?; +/// +/// let path = &it.path; +/// let content = fs::read(path) +/// .with_context(|| format!("Failed to read instrs from {}", path.display()))?; +/// +/// Ok(content) +/// } +/// ``` +/// +/// When printed, the outermost context would be printed first and the lower +/// level underlying causes would be enumerated below. +/// +/// ```console +/// Error: Failed to read instrs from ./path/to/instrs.json +/// +/// Caused by: +/// No such file or directory (os error 2) +/// ``` +/// +/// <br> +/// +/// # Effect on downcasting +/// +/// After attaching context of type `C` onto an error of type `E`, the resulting +/// `anyhow::Error` may be downcast to `C` **or** to `E`. +/// +/// That is, in codebases that rely on downcasting, Anyhow's context supports +/// both of the following use cases: +/// +/// - **Attaching context whose type is insignificant onto errors whose type +/// is used in downcasts.** +/// +/// In other error libraries whose context is not designed this way, it can +/// be risky to introduce context to existing code because new context might +/// break existing working downcasts. In Anyhow, any downcast that worked +/// before adding context will continue to work after you add a context, so +/// you should freely add human-readable context to errors wherever it would +/// be helpful. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct SuspiciousError; +/// # +/// # fn helper() -> Result<()> { +/// # bail!(SuspiciousError); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context("Failed to complete the work")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::<SuspiciousError>() { +/// // If helper() returned SuspiciousError, this downcast will +/// // correctly succeed even with the context in between. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` +/// +/// - **Attaching context whose type is used in downcasts onto errors whose +/// type is insignificant.** +/// +/// Some codebases prefer to use machine-readable context to categorize +/// lower level errors in a way that will be actionable to higher levels of +/// the application. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct HelperFailed; +/// # +/// # fn helper() -> Result<()> { +/// # bail!("no such file or directory"); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context(HelperFailed)?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::<HelperFailed>() { +/// // If helper failed, this downcast will succeed because +/// // HelperFailed is the context that has been attached to +/// // that error. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` +pub trait Context<T, E>: context::private::Sealed { + /// Wrap the error value with additional context. + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static; + + /// Wrap the error value with additional context that is evaluated lazily + /// only once an error does occur. + fn with_context<C, F>(self, f: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C; +} + +// Not public API. Referenced by macro-generated code. +#[doc(hidden)] +pub mod private { + use crate::Error; + use core::fmt::{Debug, Display}; + + #[cfg(backtrace)] + use std::backtrace::Backtrace; + + pub use core::result::Result::Err; + + #[doc(hidden)] + pub mod kind { + pub use crate::kind::{AdhocKind, TraitKind}; + + #[cfg(feature = "std")] + pub use crate::kind::BoxedKind; + } + + pub fn new_adhoc<M>(message: M) -> Error + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } +}
diff --git a/tests/platform_deps/vendor/anyhow/src/macros.rs b/tests/platform_deps/vendor/anyhow/src/macros.rs new file mode 100644 index 0000000..15a9208 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/macros.rs
@@ -0,0 +1,163 @@ +/// Return early with an error. +/// +/// This macro is equivalent to `return Err(From::from($err))`. +/// +/// # Example +/// +/// ``` +/// # use anyhow::{bail, Result}; +/// # +/// # fn has_permission(user: usize, resource: usize) -> bool { +/// # true +/// # } +/// # +/// # fn main() -> Result<()> { +/// # let user = 0; +/// # let resource = 0; +/// # +/// if !has_permission(user, resource) { +/// bail!("permission denied for accessing {}", resource); +/// } +/// # Ok(()) +/// # } +/// ``` +/// +/// ``` +/// # use anyhow::{bail, Result}; +/// # use thiserror::Error; +/// # +/// # const MAX_DEPTH: usize = 1; +/// # +/// #[derive(Error, Debug)] +/// enum ScienceError { +/// #[error("recursion limit exceeded")] +/// RecursionLimitExceeded, +/// # #[error("...")] +/// # More = (stringify! { +/// ... +/// # }, 1).1, +/// } +/// +/// # fn main() -> Result<()> { +/// # let depth = 0; +/// # +/// if depth > MAX_DEPTH { +/// bail!(ScienceError::RecursionLimitExceeded); +/// } +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! bail { + ($msg:literal $(,)?) => { + return $crate::private::Err($crate::anyhow!($msg)); + }; + ($err:expr $(,)?) => { + return $crate::private::Err($crate::anyhow!($err)); + }; + ($fmt:expr, $($arg:tt)*) => { + return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + }; +} + +/// Return early with an error if a condition is not satisfied. +/// +/// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`. +/// +/// Analogously to `assert!`, `ensure!` takes a condition and exits the function +/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error` +/// rather than panicking. +/// +/// # Example +/// +/// ``` +/// # use anyhow::{ensure, Result}; +/// # +/// # fn main() -> Result<()> { +/// # let user = 0; +/// # +/// ensure!(user == 0, "only user 0 is allowed"); +/// # Ok(()) +/// # } +/// ``` +/// +/// ``` +/// # use anyhow::{ensure, Result}; +/// # use thiserror::Error; +/// # +/// # const MAX_DEPTH: usize = 1; +/// # +/// #[derive(Error, Debug)] +/// enum ScienceError { +/// #[error("recursion limit exceeded")] +/// RecursionLimitExceeded, +/// # #[error("...")] +/// # More = (stringify! { +/// ... +/// # }, 1).1, +/// } +/// +/// # fn main() -> Result<()> { +/// # let depth = 0; +/// # +/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded); +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! ensure { + ($cond:expr, $msg:literal $(,)?) => { + if !$cond { + return $crate::private::Err($crate::anyhow!($msg)); + } + }; + ($cond:expr, $err:expr $(,)?) => { + if !$cond { + return $crate::private::Err($crate::anyhow!($err)); + } + }; + ($cond:expr, $fmt:expr, $($arg:tt)*) => { + if !$cond { + return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + } + }; +} + +/// Construct an ad-hoc error from a string. +/// +/// This evaluates to an `Error`. It can take either just a string, or a format +/// string with arguments. It also can take any custom type which implements +/// `Debug` and `Display`. +/// +/// # Example +/// +/// ``` +/// # type V = (); +/// # +/// use anyhow::{anyhow, Result}; +/// +/// fn lookup(key: &str) -> Result<V> { +/// if key.len() != 16 { +/// return Err(anyhow!("key length must be 16 characters, got {:?}", key)); +/// } +/// +/// // ... +/// # Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! anyhow { + ($msg:literal $(,)?) => { + // Handle $:literal as a special case to make cargo-expanded code more + // concise in the common case. + $crate::private::new_adhoc($msg) + }; + ($err:expr $(,)?) => ({ + use $crate::private::kind::*; + let error = $err; + (&error).anyhow_kind().new(error) + }); + ($fmt:expr, $($arg:tt)*) => { + $crate::private::new_adhoc(format!($fmt, $($arg)*)) + }; +}
diff --git a/tests/platform_deps/vendor/anyhow/src/wrapper.rs b/tests/platform_deps/vendor/anyhow/src/wrapper.rs new file mode 100644 index 0000000..3ebe51a --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/src/wrapper.rs
@@ -0,0 +1,78 @@ +use crate::StdError; +use core::fmt::{self, Debug, Display}; + +#[repr(transparent)] +pub struct MessageError<M>(pub M); + +impl<M> Debug for MessageError<M> +where + M: Display + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl<M> Display for MessageError<M> +where + M: Display + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {} + +#[repr(transparent)] +pub struct DisplayError<M>(pub M); + +impl<M> Debug for DisplayError<M> +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> Display for DisplayError<M> +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> StdError for DisplayError<M> where M: Display + 'static {} + +#[cfg(feature = "std")] +#[repr(transparent)] +pub struct BoxedError(pub Box<dyn StdError + Send + Sync>); + +#[cfg(feature = "std")] +impl Debug for BoxedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +#[cfg(feature = "std")] +impl Display for BoxedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +#[cfg(feature = "std")] +impl StdError for BoxedError { + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> { + self.0.backtrace() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.0.source() + } +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/common/mod.rs b/tests/platform_deps/vendor/anyhow/tests/common/mod.rs new file mode 100644 index 0000000..fc165a5 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/common/mod.rs
@@ -0,0 +1,14 @@ +use anyhow::{bail, Result}; +use std::io; + +pub fn bail_literal() -> Result<()> { + bail!("oh no!"); +} + +pub fn bail_fmt() -> Result<()> { + bail!("{} {}!", "oh", "no"); +} + +pub fn bail_error() -> Result<()> { + bail!(io::Error::new(io::ErrorKind::Other, "oh no!")); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/compiletest.rs b/tests/platform_deps/vendor/anyhow/tests/compiletest.rs new file mode 100644 index 0000000..f9aea23 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/compiletest.rs
@@ -0,0 +1,6 @@ +#[rustversion::attr(not(nightly), ignore)] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/drop/mod.rs b/tests/platform_deps/vendor/anyhow/tests/drop/mod.rs new file mode 100644 index 0000000..1204011 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/drop/mod.rs
@@ -0,0 +1,46 @@ +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::Arc; + +#[derive(Debug)] +pub struct Flag { + atomic: Arc<AtomicBool>, +} + +impl Flag { + pub fn new() -> Self { + Flag { atomic: Arc::new(AtomicBool::new(false)) } + } + + pub fn get(&self) -> bool { + self.atomic.load(SeqCst) + } +} + +#[derive(Debug)] +pub struct DetectDrop { + has_dropped: Flag, +} + +impl DetectDrop { + pub fn new(has_dropped: &Flag) -> Self { + DetectDrop { has_dropped: Flag { atomic: Arc::clone(&has_dropped.atomic) } } + } +} + +impl StdError for DetectDrop {} + +impl Display for DetectDrop { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "oh no!") + } +} + +impl Drop for DetectDrop { + fn drop(&mut self) { + let already_dropped = self.has_dropped.atomic.swap(true, SeqCst); + assert!(!already_dropped); + } +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_autotrait.rs b/tests/platform_deps/vendor/anyhow/tests/test_autotrait.rs new file mode 100644 index 0000000..0c9326d --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_autotrait.rs
@@ -0,0 +1,13 @@ +use anyhow::Error; + +#[test] +fn test_send() { + fn assert_send<T: Send>() {} + assert_send::<Error>(); +} + +#[test] +fn test_sync() { + fn assert_sync<T: Sync>() {} + assert_sync::<Error>(); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_backtrace.rs b/tests/platform_deps/vendor/anyhow/tests/test_backtrace.rs new file mode 100644 index 0000000..ce385f5 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_backtrace.rs
@@ -0,0 +1,13 @@ +#[rustversion::not(nightly)] +#[ignore] +#[test] +fn test_backtrace() {} + +#[rustversion::nightly] +#[test] +fn test_backtrace() { + use anyhow::anyhow; + + let error = anyhow!("oh no!"); + let _ = error.backtrace(); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_boxed.rs b/tests/platform_deps/vendor/anyhow/tests/test_boxed.rs new file mode 100644 index 0000000..851dcbb --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_boxed.rs
@@ -0,0 +1,35 @@ +use anyhow::anyhow; +use std::error::Error as StdError; +use std::io; +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("outer")] +struct MyError { + source: io::Error, +} + +#[test] +fn test_boxed_str() { + let error = Box::<dyn StdError + Send + Sync>::from("oh no!"); + let error = anyhow!(error); + assert_eq!("oh no!", error.to_string()); + assert_eq!( + "oh no!", + error.downcast_ref::<Box<dyn StdError + Send + Sync>>().unwrap().to_string() + ); +} + +#[test] +fn test_boxed_thiserror() { + let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!") }; + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} + +#[test] +fn test_boxed_anyhow() { + let error = anyhow!("oh no!").context("it failed"); + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_chain.rs b/tests/platform_deps/vendor/anyhow/tests/test_chain.rs new file mode 100644 index 0000000..b1c5a3d --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_chain.rs
@@ -0,0 +1,45 @@ +use anyhow::{anyhow, Error}; + +fn error() -> Error { + anyhow!(0).context(1).context(2).context(3) +} + +#[test] +fn test_iter() { + let e = error(); + let mut chain = e.chain(); + assert_eq!("3", chain.next().unwrap().to_string()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!("1", chain.next().unwrap().to_string()); + assert_eq!("0", chain.next().unwrap().to_string()); + assert!(chain.next().is_none()); + assert!(chain.next_back().is_none()); +} + +#[test] +fn test_rev() { + let e = error(); + let mut chain = e.chain().rev(); + assert_eq!("0", chain.next().unwrap().to_string()); + assert_eq!("1", chain.next().unwrap().to_string()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!("3", chain.next().unwrap().to_string()); + assert!(chain.next().is_none()); + assert!(chain.next_back().is_none()); +} + +#[test] +fn test_len() { + let e = error(); + let mut chain = e.chain(); + assert_eq!(4, chain.len()); + assert_eq!("3", chain.next().unwrap().to_string()); + assert_eq!(3, chain.len()); + assert_eq!("0", chain.next_back().unwrap().to_string()); + assert_eq!(2, chain.len()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!(1, chain.len()); + assert_eq!("1", chain.next_back().unwrap().to_string()); + assert_eq!(0, chain.len()); + assert!(chain.next().is_none()); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_context.rs b/tests/platform_deps/vendor/anyhow/tests/test_context.rs new file mode 100644 index 0000000..4bd1ffe --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_context.rs
@@ -0,0 +1,150 @@ +mod drop; + +use crate::drop::{DetectDrop, Flag}; +use anyhow::{Context, Error, Result}; +use std::fmt::{self, Display}; +use thiserror::Error; + +// https://github.com/dtolnay/anyhow/issues/18 +#[test] +fn test_inference() -> Result<()> { + let x = "1"; + let y: u32 = x.parse().context("...")?; + assert_eq!(y, 1); + Ok(()) +} + +macro_rules! context_type { + ($name:ident) => { + #[derive(Debug)] + struct $name { + message: &'static str, + drop: DetectDrop, + } + + impl Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.message) + } + } + }; +} + +context_type!(HighLevel); +context_type!(MidLevel); + +#[derive(Error, Debug)] +#[error("{message}")] +struct LowLevel { + message: &'static str, + drop: DetectDrop, +} + +struct Dropped { + low: Flag, + mid: Flag, + high: Flag, +} + +impl Dropped { + fn none(&self) -> bool { + !self.low.get() && !self.mid.get() && !self.high.get() + } + + fn all(&self) -> bool { + self.low.get() && self.mid.get() && self.high.get() + } +} + +fn make_chain() -> (Error, Dropped) { + let dropped = Dropped { low: Flag::new(), mid: Flag::new(), high: Flag::new() }; + + let low = + LowLevel { message: "no such file or directory", drop: DetectDrop::new(&dropped.low) }; + + // impl Context for Result<T, E> + let mid = Err::<(), LowLevel>(low) + .context(MidLevel { message: "failed to load config", drop: DetectDrop::new(&dropped.mid) }) + .unwrap_err(); + + // impl Context for Result<T, Error> + let high = Err::<(), Error>(mid) + .context(HighLevel { + message: "failed to start server", + drop: DetectDrop::new(&dropped.high), + }) + .unwrap_err(); + + (high, dropped) +} + +#[test] +fn test_downcast_ref() { + let (err, dropped) = make_chain(); + + assert!(!err.is::<String>()); + assert!(err.downcast_ref::<String>().is_none()); + + assert!(err.is::<HighLevel>()); + let high = err.downcast_ref::<HighLevel>().unwrap(); + assert_eq!(high.to_string(), "failed to start server"); + + assert!(err.is::<MidLevel>()); + let mid = err.downcast_ref::<MidLevel>().unwrap(); + assert_eq!(mid.to_string(), "failed to load config"); + + assert!(err.is::<LowLevel>()); + let low = err.downcast_ref::<LowLevel>().unwrap(); + assert_eq!(low.to_string(), "no such file or directory"); + + assert!(dropped.none()); + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_high() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<HighLevel>().unwrap(); + assert!(!dropped.high.get()); + assert!(dropped.low.get() && dropped.mid.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_mid() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<MidLevel>().unwrap(); + assert!(!dropped.mid.get()); + assert!(dropped.low.get() && dropped.high.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_low() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<LowLevel>().unwrap(); + assert!(!dropped.low.get()); + assert!(dropped.mid.get() && dropped.high.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_unsuccessful_downcast() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<String>().unwrap_err(); + assert!(dropped.none()); + + drop(err); + assert!(dropped.all()); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_convert.rs b/tests/platform_deps/vendor/anyhow/tests/test_convert.rs new file mode 100644 index 0000000..72da020 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_convert.rs
@@ -0,0 +1,24 @@ +mod drop; + +use self::drop::{DetectDrop, Flag}; +use anyhow::{Error, Result}; +use std::error::Error as StdError; + +#[test] +fn test_convert() { + let has_dropped = Flag::new(); + let error = Error::new(DetectDrop::new(&has_dropped)); + let box_dyn = Box::<dyn StdError + Send + Sync>::from(error); + assert_eq!("oh no!", box_dyn.to_string()); + drop(box_dyn); + assert!(has_dropped.get()); +} + +#[test] +fn test_question_mark() -> Result<(), Box<dyn StdError>> { + fn f() -> Result<()> { + Ok(()) + } + f()?; + Ok(()) +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_downcast.rs b/tests/platform_deps/vendor/anyhow/tests/test_downcast.rs new file mode 100644 index 0000000..c4393e8 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_downcast.rs
@@ -0,0 +1,70 @@ +mod common; +mod drop; + +use self::common::*; +use self::drop::{DetectDrop, Flag}; +use anyhow::Error; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +#[test] +fn test_downcast() { + assert_eq!("oh no!", bail_literal().unwrap_err().downcast::<&str>().unwrap(),); + assert_eq!("oh no!", bail_fmt().unwrap_err().downcast::<String>().unwrap(),); + assert_eq!("oh no!", bail_error().unwrap_err().downcast::<io::Error>().unwrap().to_string(),); +} + +#[test] +fn test_downcast_ref() { + assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(),); + assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_ref::<String>().unwrap(),); + assert_eq!( + "oh no!", + bail_error().unwrap_err().downcast_ref::<io::Error>().unwrap().to_string(), + ); +} + +#[test] +fn test_downcast_mut() { + assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(),); + assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_mut::<String>().unwrap(),); + assert_eq!( + "oh no!", + bail_error().unwrap_err().downcast_mut::<io::Error>().unwrap().to_string(), + ); +} + +#[test] +fn test_drop() { + let has_dropped = Flag::new(); + let error = Error::new(DetectDrop::new(&has_dropped)); + drop(error.downcast::<DetectDrop>().unwrap()); + assert!(has_dropped.get()); +} + +#[test] +fn test_large_alignment() { + #[repr(align(64))] + #[derive(Debug)] + struct LargeAlignedError(&'static str); + + impl Display for LargeAlignedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.0) + } + } + + impl StdError for LargeAlignedError {} + + let error = Error::new(LargeAlignedError("oh no!")); + assert_eq!("oh no!", error.downcast_ref::<LargeAlignedError>().unwrap().0); +} + +#[test] +fn test_unsuccessful_downcast() { + let mut error = bail_error().unwrap_err(); + assert!(error.downcast_ref::<&str>().is_none()); + assert!(error.downcast_mut::<&str>().is_none()); + assert!(error.downcast::<&str>().is_err()); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_fmt.rs b/tests/platform_deps/vendor/anyhow/tests/test_fmt.rs new file mode 100644 index 0000000..cc49291 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_fmt.rs
@@ -0,0 +1,94 @@ +use anyhow::{bail, Context, Result}; +use std::io; + +fn f() -> Result<()> { + bail!(io::Error::new(io::ErrorKind::PermissionDenied, "oh no!")); +} + +fn g() -> Result<()> { + f().context("f failed") +} + +fn h() -> Result<()> { + g().context("g failed") +} + +const EXPECTED_ALTDISPLAY_F: &str = "oh no!"; + +const EXPECTED_ALTDISPLAY_G: &str = "f failed: oh no!"; + +const EXPECTED_ALTDISPLAY_H: &str = "g failed: f failed: oh no!"; + +const EXPECTED_DEBUG_F: &str = "oh no!"; + +const EXPECTED_DEBUG_G: &str = "\ +f failed + +Caused by: + oh no!\ +"; + +const EXPECTED_DEBUG_H: &str = "\ +g failed + +Caused by: + 0: f failed + 1: oh no!\ +"; + +const EXPECTED_ALTDEBUG_F: &str = "\ +Custom { + kind: PermissionDenied, + error: \"oh no!\", +}\ +"; + +const EXPECTED_ALTDEBUG_G: &str = "\ +Error { + context: \"f failed\", + source: Custom { + kind: PermissionDenied, + error: \"oh no!\", + }, +}\ +"; + +const EXPECTED_ALTDEBUG_H: &str = "\ +Error { + context: \"g failed\", + source: Error { + context: \"f failed\", + source: Custom { + kind: PermissionDenied, + error: \"oh no!\", + }, + }, +}\ +"; + +#[test] +fn test_display() { + assert_eq!("g failed", h().unwrap_err().to_string()); +} + +#[test] +fn test_altdisplay() { + assert_eq!(EXPECTED_ALTDISPLAY_F, format!("{:#}", f().unwrap_err())); + assert_eq!(EXPECTED_ALTDISPLAY_G, format!("{:#}", g().unwrap_err())); + assert_eq!(EXPECTED_ALTDISPLAY_H, format!("{:#}", h().unwrap_err())); +} + +#[test] +#[cfg_attr(not(backtrace), ignore)] +fn test_debug() { + assert_eq!(EXPECTED_DEBUG_F, format!("{:?}", f().unwrap_err())); + assert_eq!(EXPECTED_DEBUG_G, format!("{:?}", g().unwrap_err())); + assert_eq!(EXPECTED_DEBUG_H, format!("{:?}", h().unwrap_err())); +} + +#[test] +fn test_altdebug() { + assert_eq!(EXPECTED_ALTDEBUG_F, format!("{:#?}", f().unwrap_err())); + assert_eq!(EXPECTED_ALTDEBUG_G, format!("{:#?}", g().unwrap_err())); + assert_eq!(EXPECTED_ALTDEBUG_H, format!("{:#?}", h().unwrap_err())); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_macros.rs b/tests/platform_deps/vendor/anyhow/tests/test_macros.rs new file mode 100644 index 0000000..c6888b6 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_macros.rs
@@ -0,0 +1,33 @@ +mod common; + +use self::common::*; +use anyhow::ensure; + +#[test] +fn test_messages() { + assert_eq!("oh no!", bail_literal().unwrap_err().to_string()); + assert_eq!("oh no!", bail_fmt().unwrap_err().to_string()); + assert_eq!("oh no!", bail_error().unwrap_err().to_string()); +} + +#[test] +fn test_ensure() { + let f = || { + ensure!(1 + 1 == 2, "This is correct"); + Ok(()) + }; + assert!(f().is_ok()); + + let v = 1; + let f = || { + ensure!(v + v == 2, "This is correct, v: {}", v); + Ok(()) + }; + assert!(f().is_ok()); + + let f = || { + ensure!(v + v == 1, "This is not correct, v: {}", v); + Ok(()) + }; + assert!(f().is_err()); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_repr.rs b/tests/platform_deps/vendor/anyhow/tests/test_repr.rs new file mode 100644 index 0000000..72f5002 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_repr.rs
@@ -0,0 +1,29 @@ +mod drop; + +use self::drop::{DetectDrop, Flag}; +use anyhow::Error; +use std::marker::Unpin; +use std::mem; + +#[test] +fn test_error_size() { + assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>()); +} + +#[test] +fn test_null_pointer_optimization() { + assert_eq!(mem::size_of::<Result<(), Error>>(), mem::size_of::<usize>()); +} + +#[test] +fn test_autotraits() { + fn assert<E: Unpin + Send + Sync + 'static>() {} + assert::<Error>(); +} + +#[test] +fn test_drop() { + let has_dropped = Flag::new(); + drop(Error::new(DetectDrop::new(&has_dropped))); + assert!(has_dropped.get()); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/test_source.rs b/tests/platform_deps/vendor/anyhow/tests/test_source.rs new file mode 100644 index 0000000..018267d --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/test_source.rs
@@ -0,0 +1,62 @@ +use anyhow::anyhow; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +#[derive(Debug)] +enum TestError { + Io(io::Error), +} + +impl Display for TestError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + TestError::Io(e) => Display::fmt(e, formatter), + } + } +} + +impl StdError for TestError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + TestError::Io(io) => Some(io), + } + } +} + +#[test] +fn test_literal_source() { + let error = anyhow!("oh no!"); + assert!(error.source().is_none()); +} + +#[test] +fn test_variable_source() { + let msg = "oh no!"; + let error = anyhow!(msg); + assert!(error.source().is_none()); + + let msg = msg.to_owned(); + let error = anyhow!(msg); + assert!(error.source().is_none()); +} + +#[test] +fn test_fmt_source() { + let error = anyhow!("{} {}!", "oh", "no"); + assert!(error.source().is_none()); +} + +#[test] +fn test_io_source() { + let io = io::Error::new(io::ErrorKind::Other, "oh no!"); + let error = anyhow!(TestError::Io(io)); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} + +#[test] +fn test_anyhow_from_anyhow() { + let error = anyhow!("oh no!").context("context"); + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.rs b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.rs new file mode 100644 index 0000000..d2e89af --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.rs
@@ -0,0 +1,8 @@ +use anyhow::anyhow; + +#[derive(Debug)] +struct Error; + +fn main() { + let _ = anyhow!(Error); +}
diff --git a/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.stderr b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.stderr new file mode 100644 index 0000000..be95737 --- /dev/null +++ b/tests/platform_deps/vendor/anyhow/tests/ui/no-impl.stderr
@@ -0,0 +1,21 @@ +error[E0599]: no method named `anyhow_kind` found for reference `&Error` in the current scope + --> $DIR/no-impl.rs:7:13 + | +4 | struct Error; + | ------------- + | | + | doesn't satisfy `Error: anyhow::kind::TraitKind` + | doesn't satisfy `Error: std::convert::Into<anyhow::Error>` + | doesn't satisfy `Error: std::fmt::Display` +... +7 | let _ = anyhow!(Error); + | ^^^^^^^^^^^^^^ method not found in `&Error` + | + = note: the method `anyhow_kind` exists but the following trait bounds were not satisfied: + `Error: std::convert::Into<anyhow::Error>` + which is required by `Error: anyhow::kind::TraitKind` + `Error: std::fmt::Display` + which is required by `&Error: anyhow::kind::AdhocKind` + `&Error: std::convert::Into<anyhow::Error>` + which is required by `&Error: anyhow::kind::TraitKind` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/platform_features/BUILD.gn b/tests/platform_features/BUILD.gn new file mode 100644 index 0000000..2d697e9 --- /dev/null +++ b/tests/platform_features/BUILD.gn
@@ -0,0 +1,68 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("a") { + public_deps = [ ":a-v1_0_0" ] +} + +if (!(current_os == "fuchsia")) { + group("b") { + public_deps = [ ":b-v1_0_0" ] + } +} + +rust_library("a-v1_0_0") { + crate_name = "a" + crate_root = "//platform_features/a/src/lib.rs" + output_name = "a-537022e729cc9ca0" + + deps = [] + if (!(current_os == "fuchsia")) { + deps += [ ":b-v1_0_0" ] + } + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=537022e729cc9ca0", + "-Cextra-filename=-537022e729cc9ca0", + ] + if (!(current_os == "fuchsia")) { + rustflags += [ "--cfg=feature=\"b\"" ] + } + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("b-v1_0_0") { + crate_name = "b" + crate_root = "//platform_features/b/src/lib.rs" + output_name = "b-72d4e469bdf48536" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=72d4e469bdf48536", + "-Cextra-filename=-72d4e469bdf48536", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/platform_features/Cargo.lock b/tests/platform_features/Cargo.lock new file mode 100644 index 0000000..20913cf --- /dev/null +++ b/tests/platform_features/Cargo.lock
@@ -0,0 +1,19 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "a" +version = "1.0.0" + +[[package]] +name = "b" +version = "1.0.0" + +[[package]] +name = "platform_features" +version = "0.1.0" +dependencies = [ + "a", + "b", +]
diff --git a/tests/platform_features/Cargo.toml b/tests/platform_features/Cargo.toml new file mode 100644 index 0000000..94b1e6e --- /dev/null +++ b/tests/platform_features/Cargo.toml
@@ -0,0 +1,20 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "platform_features" +version = "0.1.0" +authors = ["David Koloski <dkoloski@google.com>"] +edition = "2021" + +[dependencies] +a = { version = "1.0", path = "a" } + +[target."cfg(not(target_os = \"fuchsia\"))".dependencies] +b = { version = "1.0", path = "b" } + +# Manually enable the 'b' feature on a and add a dependency on it +[gn.package.a."1.0.0".platform."cfg(not(target_os = \"fuchsia\"))"] +deps = [":b-v1_0_0"] +rustflags = ["--cfg=feature=\\\"b\\\""]
diff --git a/tests/platform_features/a/Cargo.toml b/tests/platform_features/a/Cargo.toml new file mode 100644 index 0000000..48a1677 --- /dev/null +++ b/tests/platform_features/a/Cargo.toml
@@ -0,0 +1,13 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "a" +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +b = { path = "../b", optional = true }
diff --git a/tests/platform_features/a/src/lib.rs b/tests/platform_features/a/src/lib.rs new file mode 100644 index 0000000..ff5b3e0 --- /dev/null +++ b/tests/platform_features/a/src/lib.rs
@@ -0,0 +1,6 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#[cfg(all(target_os = "fuchsia", feature = "b"))] +compile_error!("the 'b' feature is not supported on Fuchsia");
diff --git a/tests/platform_features/b/Cargo.toml b/tests/platform_features/b/Cargo.toml new file mode 100644 index 0000000..d10394e --- /dev/null +++ b/tests/platform_features/b/Cargo.toml
@@ -0,0 +1,12 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "b" +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies]
diff --git a/tests/platform_features/b/src/lib.rs b/tests/platform_features/b/src/lib.rs new file mode 100644 index 0000000..5c9d3b6 --- /dev/null +++ b/tests/platform_features/b/src/lib.rs
@@ -0,0 +1,3 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file.
diff --git a/tests/platform_features/src/lib.rs b/tests/platform_features/src/lib.rs new file mode 100644 index 0000000..5c9d3b6 --- /dev/null +++ b/tests/platform_features/src/lib.rs
@@ -0,0 +1,3 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file.
diff --git a/tests/sdk_metadata/BUILD.gn b/tests/sdk_metadata/BUILD.gn new file mode 100644 index 0000000..eb9b55f --- /dev/null +++ b/tests/sdk_metadata/BUILD.gn
@@ -0,0 +1,106 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +import("//build/sdk/sdk_atom.gni") + +# True if the current GN toolchain instance can be used to create +# Rust sdk_atom() targets. +_generating_sdk = false + +# The SDK prefix for the current toolchain. +if (current_toolchain == default_toolchain) { + _generating_sdk = true + _sdk_prefix = "" +} else if (is_host) { + _generating_sdk = true + _sdk_prefix = "rust_proc_macros/" +} + +group("sdk_metadata") { + public_deps = [ ":sdk_metadata-v1_0_0" ] +} + +if (_generating_sdk) { + sdk_atom("sdk_metadata_sdk") { + id = "sdk://${_sdk_prefix}third_party/rust_crates/sdk_metadata" + category = "internal" + meta = { + source = "sdk_metas/sdk_metadata.sdk.meta.json" + dest = "${_sdk_prefix}third_party/rust_crates/sdk_metadata/meta.json" + schema = "3p_rust_library" + } + } +} + +rust_library("sdk_metadata-v1_0_0") { + crate_name = "sdk_metadata" + crate_root = "//sdk_metadata/src/lib.rs" + output_name = "sdk_metadata-e14a5f3dc8d9ca6" + + deps = [] + deps += [ ":sub-crate-v1_0_0" ] + deps += [ ":sub-crate-proc-macro-v1_0_0($host_toolchain)" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=e14a5f3dc8d9ca6", + "-Cextra-filename=-e14a5f3dc8d9ca6", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_library("sub-crate-v1_0_0") { + crate_name = "sub_crate" + crate_root = "//sdk_metadata/sub-crate/src/lib.rs" + output_name = "sub_crate-1eba52a868057013" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=1eba52a868057013", + "-Cextra-filename=-1eba52a868057013", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +} + +rust_proc_macro("sub-crate-proc-macro-v1_0_0") { + crate_name = "sub_crate_proc_macro" + crate_root = "//sdk_metadata/sub-crate-proc-macro/src/lib.rs" + output_name = "sub_crate_proc_macro-bf52f82d4654f78c" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=bf52f82d4654f78c", + "-Cextra-filename=-bf52f82d4654f78c", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/sdk_metadata/Cargo.lock b/tests/sdk_metadata/Cargo.lock new file mode 100644 index 0000000..923c006 --- /dev/null +++ b/tests/sdk_metadata/Cargo.lock
@@ -0,0 +1,19 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "sdk_metadata" +version = "1.0.0" +dependencies = [ + "sub-crate", + "sub-crate-proc-macro", +] + +[[package]] +name = "sub-crate" +version = "1.0.0" + +[[package]] +name = "sub-crate-proc-macro" +version = "1.0.0"
diff --git a/tests/sdk_metadata/Cargo.toml b/tests/sdk_metadata/Cargo.toml new file mode 100644 index 0000000..f306dc1 --- /dev/null +++ b/tests/sdk_metadata/Cargo.toml
@@ -0,0 +1,13 @@ +# Copyright 2023 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "sdk_metadata" +version = "1.0.0" +authors = ["David Koloski <dkoloski@google.com>"] +edition = "2018" + +[dependencies] +sub-crate = { version = "1.0.0", path = "sub-crate" } +sub-crate-proc-macro = { version = "1.0.0", path = "sub-crate-proc-macro" }
diff --git a/tests/sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json b/tests/sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json new file mode 100644 index 0000000..64d3f95 --- /dev/null +++ b/tests/sdk_metadata/sdk_metas/sdk_metadata.sdk.meta.json
@@ -0,0 +1,5 @@ +{ + "type": "rust_3p_library", + "name": "sdk_metadata", + "version": "1.0.0" +}
diff --git a/tests/sdk_metadata/src/lib.rs b/tests/sdk_metadata/src/lib.rs new file mode 100644 index 0000000..4cc0cd7 --- /dev/null +++ b/tests/sdk_metadata/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2023 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +pub const EXPORTED: u32 = 777;
diff --git a/tests/sdk_metadata/sub-crate-proc-macro/Cargo.toml b/tests/sdk_metadata/sub-crate-proc-macro/Cargo.toml new file mode 100644 index 0000000..c99e6bc --- /dev/null +++ b/tests/sdk_metadata/sub-crate-proc-macro/Cargo.toml
@@ -0,0 +1,12 @@ +# Copyright 2023 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "sub-crate-proc-macro" +version = "1.0.0" +authors = ["David Koloski <dkoloski@google.com>"] +edition = "2018" + +[lib] +proc-macro = true
diff --git a/tests/sdk_metadata/sub-crate-proc-macro/src/lib.rs b/tests/sdk_metadata/sub-crate-proc-macro/src/lib.rs new file mode 100644 index 0000000..7874a8a --- /dev/null +++ b/tests/sdk_metadata/sub-crate-proc-macro/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2023 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +const PROC_MACRO: u32 = 777;
diff --git a/tests/sdk_metadata/sub-crate/Cargo.toml b/tests/sdk_metadata/sub-crate/Cargo.toml new file mode 100644 index 0000000..9f35821 --- /dev/null +++ b/tests/sdk_metadata/sub-crate/Cargo.toml
@@ -0,0 +1,9 @@ +# Copyright 2023 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "sub-crate" +version = "1.0.0" +authors = ["David Koloski <dkoloski@google.com>"] +edition = "2018"
diff --git a/tests/sdk_metadata/sub-crate/src/lib.rs b/tests/sdk_metadata/sub-crate/src/lib.rs new file mode 100644 index 0000000..7025df7 --- /dev/null +++ b/tests/sdk_metadata/sub-crate/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2023 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +pub const SUB_CRATE: u32 = 777;
diff --git a/tests/simple/BUILD.gn b/tests/simple/BUILD.gn new file mode 100644 index 0000000..6462d18 --- /dev/null +++ b/tests/simple/BUILD.gn
@@ -0,0 +1,35 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("simple") { + public_deps = [ ":simple-v1_0_25" ] +} + +rust_library("simple-v1_0_25") { + crate_name = "simple" + crate_root = "//simple/src/lib.rs" + output_name = "simple-3b5ec47e038ac967" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=3b5ec47e038ac967", + "-Cextra-filename=-3b5ec47e038ac967", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/simple/Cargo.lock b/tests/simple/Cargo.lock new file mode 100644 index 0000000..6111785 --- /dev/null +++ b/tests/simple/Cargo.lock
@@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "simple" +version = "1.0.25" +
diff --git a/tests/simple/Cargo.toml b/tests/simple/Cargo.toml new file mode 100644 index 0000000..2160613 --- /dev/null +++ b/tests/simple/Cargo.toml
@@ -0,0 +1,11 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "simple" +version = "1.0.25" +authors = ["Benjamin Brittain <bwb@google.com>"] +edition = "2018" + +[dependencies]
diff --git a/tests/simple/src/lib.rs b/tests/simple/src/lib.rs new file mode 100644 index 0000000..40b4304 --- /dev/null +++ b/tests/simple/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// no-op +// hello there +pub const EXPORTED: u32 = 777;
diff --git a/tests/simple_deps/.cargo/config b/tests/simple_deps/.cargo/config new file mode 100644 index 0000000..0236928 --- /dev/null +++ b/tests/simple_deps/.cargo/config
@@ -0,0 +1,5 @@ +[source.crates-io] +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendor"
diff --git a/tests/simple_deps/BUILD.gn b/tests/simple_deps/BUILD.gn new file mode 100644 index 0000000..5aac33d --- /dev/null +++ b/tests/simple_deps/BUILD.gn
@@ -0,0 +1,79 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("simple_deps") { + public_deps = [ ":simple_deps-v0_1_0" ] +} + +license("anyhow-v1_0_27.license") { + public_package_name = "anyhow" + license_files = [ + "//simple_deps/vendor/anyhow/LICENSE-APACHE", + "//simple_deps/vendor/anyhow/LICENSE-MIT", + "//simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING", + ] +} + +rust_library("anyhow-v1_0_27") { + crate_name = "anyhow" + crate_root = "//simple_deps/vendor/anyhow/src/lib.rs" + output_name = "anyhow-d49cfcf8c47646f2" + configs += [ "//build/config:some_config" ] + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=d49cfcf8c47646f2", + "-Cextra-filename=-d49cfcf8c47646f2", + "--cfg=feature=\"default\"", + "--cfg=feature=\"std\"", + "--cfg=backtrace", + ] + + visibility = [ ":*" ] + + applicable_licenses = [ ":anyhow-v1_0_27.license" ] +} + +license("simple_deps-v0_1_0.license") { + public_package_name = "simple_deps" + license_files = [ + "//simple_deps/vendor/anyhow/LICENSE-APACHE", + "//simple_deps/vendor/anyhow/LICENSE-MIT", + "//simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING", + ] +} + +rust_library("simple_deps-v0_1_0") { + crate_name = "simple_deps" + crate_root = "//simple_deps/src/lib.rs" + output_name = "simple_deps-176a6857401f170f" + + deps = [] + deps += [ ":anyhow-v1_0_27" ] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=176a6857401f170f", + "-Cextra-filename=-176a6857401f170f", + ] + + visibility = [ ":*" ] + + applicable_licenses = [ ":simple_deps-v0_1_0.license" ] +}
diff --git a/tests/simple_deps/BUILD_WITH_NO_ROOT.gn b/tests/simple_deps/BUILD_WITH_NO_ROOT.gn new file mode 100644 index 0000000..7580145 --- /dev/null +++ b/tests/simple_deps/BUILD_WITH_NO_ROOT.gn
@@ -0,0 +1,48 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("anyhow") { + public_deps = [ ":anyhow-v1_0_27" ] +} + +license("anyhow-v1_0_27.license") { + public_package_name = "anyhow" + license_files = [ + "//simple_deps/vendor/anyhow/LICENSE-APACHE", + "//simple_deps/vendor/anyhow/LICENSE-MIT", + "//simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING", + ] +} + +rust_library("anyhow-v1_0_27") { + crate_name = "anyhow" + crate_root = "//simple_deps/vendor/anyhow/src/lib.rs" + output_name = "anyhow-d49cfcf8c47646f2" + configs += [ "//build/config:some_config" ] + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2018", + "-Cmetadata=d49cfcf8c47646f2", + "-Cextra-filename=-d49cfcf8c47646f2", + "--cfg=feature=\"default\"", + "--cfg=feature=\"std\"", + "--cfg=backtrace", + ] + + visibility = [ ":*" ] + + applicable_licenses = [ ":anyhow-v1_0_27.license" ] +}
diff --git a/tests/simple_deps/Cargo.lock b/tests/simple_deps/Cargo.lock new file mode 100644 index 0000000..364b284 --- /dev/null +++ b/tests/simple_deps/Cargo.lock
@@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" + +[[package]] +name = "simple_deps" +version = "0.1.0" +dependencies = [ + "anyhow", +]
diff --git a/tests/simple_deps/Cargo.toml b/tests/simple_deps/Cargo.toml new file mode 100644 index 0000000..9497227 --- /dev/null +++ b/tests/simple_deps/Cargo.toml
@@ -0,0 +1,17 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "simple_deps" +version = "0.1.0" +authors = ["Benjamin Brittain <bwb@google.com>"] +edition = "2018" + +[dependencies] +anyhow = "1.0.27" + +# gn config +[gn.package.anyhow."1.0.27"] +rustflags = ["--cfg=backtrace"] +configs = ["//build/config:some_config"]
diff --git a/tests/simple_deps/src/lib.rs b/tests/simple_deps/src/lib.rs new file mode 100644 index 0000000..5f482a8 --- /dev/null +++ b/tests/simple_deps/src/lib.rs
@@ -0,0 +1,7 @@ +// Copyright 2020 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +fn some_fn() { + println!("Hello, world!"); +}
diff --git a/tests/simple_deps/vendor/anyhow/.cargo-checksum.json b/tests/simple_deps/vendor/anyhow/.cargo-checksum.json new file mode 100644 index 0000000..5da7341 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/.cargo-checksum.json
@@ -0,0 +1 @@ +{"files":{"Cargo.toml":"6a91884ae5c50562445d6a81d989a8ac76fe64e5fd6a6f3ce067e01f03772419","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"6f6d4c826e6be52ba5c29e6b1169b11e3789ab0d426d3e17ad1c19b1f646b631","build.rs":"42e4fff05dc773820ee1843aba8e36389a36386d6e6082e778d2203420bd72ab","src/backtrace.rs":"a82a8ffae2c68ee385dc78d8ec8cb6f3351234f0ad6af7e87df2371593e0f6aa","src/chain.rs":"1627608ce95c3484d26e1742a1b8b533b74c6159916b717bacffae3ae53731f9","src/context.rs":"f2be36af9588c924ed087bcb7bd681d330889e54b8f98cdc2aba93512a28762e","src/error.rs":"38ed38f1d3561685ff7167f3e706d86003952404e63f8ada5c5bf3e9ee32f76c","src/fmt.rs":"079d7b4faaa23f42423e0bb6b4e8a80d7d6d45c38c0d46bebd7d647c8679469f","src/kind.rs":"8481a8b7835eebb3859a8c32c217bf9c73543cfc62e3916b98d39af8b063125c","src/lib.rs":"e10c1dda3d1d40de3606176630885c0eaa0ac78fd58a169fed24004ccd4c37a8","src/macros.rs":"77722190b58a6106b21aefd3b5d4f136a076afcdbc0fae21562d99e2c22912e1","src/wrapper.rs":"1229beca67dbd95ca77c9ecce282272acc55276c267c58cb73a75388b4693dda","tests/common/mod.rs":"f9088c2d7afafa64ff730b629272045b776bfafc2f5957508242da630635f2e1","tests/compiletest.rs":"0a52a44786aea1c299c695bf948b2ed2081e4cc344e5c2cadceab4eb03d0010d","tests/drop/mod.rs":"464bc1ddeae307eac906928286ec3edb77057c5c1302e02150d3649e2b861f1a","tests/test_autotrait.rs":"981e792db353be2f14c7a1cabe43b5f1329c168cb7679077cc2be786a0920d48","tests/test_backtrace.rs":"0e50edbb33b6bd07ba89ff3db72fb7c688ba2a4371fccdbbb20309ab02948b6a","tests/test_boxed.rs":"98a45325b1e86d4c5d3094ab99cd1ada1f771c505d2d7322f0afcbe7bdb71cfa","tests/test_chain.rs":"f28efeae7395d1c395e6f1a647b4199c25a00410ade45248c145c6fcf2fb448a","tests/test_context.rs":"f82c915b182df1a604a4cd558a03b1a821414983d6f6af6822398104cea70676","tests/test_convert.rs":"62840be1ee8022ba5e8c0d3fc1752a1526b2c47d4cceecff2b86790524c3b3ea","tests/test_downcast.rs":"253d6f54e554965023b378b037827ec6289c4779a7a7c12706e19c2731d219fe","tests/test_fmt.rs":"17572596f257aac9aa2ec4620e292ca6a954128b94772bb948399fab53832e70","tests/test_macros.rs":"c7d3d5e0b756f59d4858035025fb341d031369c88486fd9f961ee16bae6c78bf","tests/test_repr.rs":"dbb9b04ddbe1ab31eb5331ea69f05bb3a147299da2275a3d4dcc92947b5591b9","tests/test_source.rs":"b80723cf635a4f8c4df21891b34bfab9ed2b2aa407e7a2f826d24e334cd5f88e","tests/ui/no-impl.rs":"fab6cbf2f6ea510b86f567dfb3b7c31250a9fd71ae5d110dbb9188be569ec593","tests/ui/no-impl.stderr":"7c2c3f46c266a437300591f10be330f937ac6a0a2213ed5030a9fbc895e2d100"},"package":"013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"} \ No newline at end of file
diff --git a/tests/simple_deps/vendor/anyhow/Cargo.toml b/tests/simple_deps/vendor/anyhow/Cargo.toml new file mode 100644 index 0000000..9e2930c --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/Cargo.toml
@@ -0,0 +1,42 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "anyhow" +version = "1.0.27" +authors = ["David Tolnay <dtolnay@gmail.com>"] +description = "Flexible concrete Error type built on std::error::Error" +documentation = "https://docs.rs/anyhow" +readme = "README.md" +categories = ["rust-patterns"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/anyhow" +[dev-dependencies.futures] +version = "0.3" +default-features = false + +[dev-dependencies.rustversion] +version = "1.0" + +[dev-dependencies.thiserror] +version = "1.0" + +[dev-dependencies.trybuild] +version = "1.0.19" +features = ["diff"] + +[features] +default = ["std"] +std = [] +[badges.travis-ci] +repository = "dtolnay/anyhow"
diff --git a/tests/simple_deps/vendor/anyhow/LICENSE-APACHE b/tests/simple_deps/vendor/anyhow/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/LICENSE-APACHE
@@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.
diff --git a/tests/simple_deps/vendor/anyhow/LICENSE-MIT b/tests/simple_deps/vendor/anyhow/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/LICENSE-MIT
@@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
diff --git a/tests/simple_deps/vendor/anyhow/README.md b/tests/simple_deps/vendor/anyhow/README.md new file mode 100644 index 0000000..0c2786b --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/README.md
@@ -0,0 +1,171 @@ +Anyhow ¯\\\_(ツ)\_/¯ +========================= + +[](https://travis-ci.com/dtolnay/anyhow) +[](https://crates.io/crates/anyhow) +[](https://docs.rs/anyhow) + +This library provides [`anyhow::Error`][Error], a trait object based error type +for easy idiomatic error handling in Rust applications. + +[Error]: https://docs.rs/anyhow/1.0/anyhow/struct.Error.html + +```toml +[dependencies] +anyhow = "1.0" +``` + +*Compiler support: requires rustc 1.34+* + +<br> + +## Details + +- Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as the + return type of any fallible function. + + Within the function, use `?` to easily propagate any error that implements the + `std::error::Error` trait. + + ```rust + use anyhow::Result; + + fn get_cluster_info() -> Result<ClusterMap> { + let config = std::fs::read_to_string("cluster.json")?; + let map: ClusterMap = serde_json::from_str(&config)?; + Ok(map) + } + ``` + +- Attach context to help the person troubleshooting the error understand where + things went wrong. A low-level error like "No such file or directory" can be + annoying to debug without more context about what higher level step the + application was in the middle of. + + ```rust + use anyhow::{Context, Result}; + + fn main() -> Result<()> { + ... + it.detach().context("Failed to detach the important thing")?; + + let content = std::fs::read(path) + .with_context(|| format!("Failed to read instrs from {}", path))?; + ... + } + ``` + + ```console + Error: Failed to read instrs from ./path/to/instrs.json + + Caused by: + No such file or directory (os error 2) + ``` + +- Downcasting is supported and can be by value, by shared reference, or by + mutable reference as needed. + + ```rust + // If the error was caused by redaction, then return a + // tombstone instead of the content. + match root_cause.downcast_ref::<DataStoreError>() { + Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), + None => Err(error), + } + ``` + +- A backtrace is captured and printed with the error if the underlying error + type does not already provide its own. In order to see backtraces, they must + be enabled through the environment variables described in [`std::backtrace`]: + + - If you want panics and errors to both have backtraces, set + `RUST_BACKTRACE=1`; + - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; + - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and + `RUST_LIB_BACKTRACE=0`. + + [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables + +- Anyhow works with any error type that has an impl of `std::error::Error`, + including ones defined in your crate. We do not bundle a `derive(Error)` macro + but you can write the impls yourself or use a standalone macro like + [thiserror]. + + ```rust + use thiserror::Error; + + #[derive(Error, Debug)] + pub enum FormatError { + #[error("Invalid header (expected {expected:?}, got {found:?})")] + InvalidHeader { + expected: String, + found: String, + }, + #[error("Missing attribute: {0}")] + MissingAttribute(String), + } + ``` + +- One-off error messages can be constructed using the `anyhow!` macro, which + supports string interpolation and produces an `anyhow::Error`. + + ```rust + return Err(anyhow!("Missing attribute: {}", missing)); + ``` + +<br> + +## No-std support + +In no_std mode, the same API is almost all available and works the same way. To +depend on Anyhow in no_std mode, disable our default enabled "std" feature in +Cargo.toml. A global allocator is required. + +```toml +[dependencies] +anyhow = { version = "1.0", default-features = false } +``` + +Since the `?`-based error conversions would normally rely on the +`std::error::Error` trait which is only available through std, no_std mode will +require an explicit `.map_err(Error::msg)` when working with a non-Anyhow error +type inside a function that returns Anyhow's error type. + +<br> + +## Comparison to failure + +The `anyhow::Error` type works something like `failure::Error`, but unlike +failure ours is built around the standard library's `std::error::Error` trait +rather than a separate trait `failure::Fail`. The standard library has adopted +the necessary improvements for this to be possible as part of [RFC 2504]. + +[RFC 2504]: https://github.com/rust-lang/rfcs/blob/HEAD/text/2504-fix-error.md + +<br> + +## Comparison to thiserror + +Use Anyhow if you don't care what error type your functions return, you just +want it to be easy. This is common in application code. Use [thiserror] if you +are a library that wants to design your own dedicated error type(s) so that on +failures the caller gets exactly the information that you choose. + +[thiserror]: https://github.com/dtolnay/thiserror + +<br> + +#### License + +<sup> +Licensed under either of <a href="LICENSE-APACHE">Apache License, Version +2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. +</sup> + +<br> + +<sub> +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. +</sub>
diff --git a/tests/simple_deps/vendor/anyhow/build.rs b/tests/simple_deps/vendor/anyhow/build.rs new file mode 100644 index 0000000..293167e --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/build.rs
@@ -0,0 +1,61 @@ +use std::path::Path; +use std::process::{Command, ExitStatus}; +use std::{env, fs}; + +// This code exercises the surface area that we expect of the std Backtrace +// type. If the current toolchain is able to compile it, we go ahead and use +// backtrace in anyhow. +const PROBE: &str = r#" + #![feature(backtrace)] + #![allow(dead_code)] + + use std::backtrace::{Backtrace, BacktraceStatus}; + use std::error::Error; + use std::fmt::{self, Display}; + + #[derive(Debug)] + struct E; + + impl Display for E { + fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } + } + + impl Error for E { + fn backtrace(&self) -> Option<&Backtrace> { + let backtrace = Backtrace::capture(); + match backtrace.status() { + BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} + } + unimplemented!() + } + } +"#; + +fn main() { + if !cfg!(feature = "std") { + return; + } + match compile_probe() { + Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"), + _ => {} + } +} + +fn compile_probe() -> Option<ExitStatus> { + let rustc = env::var_os("RUSTC")?; + let out_dir = env::var_os("OUT_DIR")?; + let probefile = Path::new(&out_dir).join("probe.rs"); + fs::write(&probefile, PROBE).ok()?; + Command::new(rustc) + .arg("--edition=2018") + .arg("--crate-name=anyhow_build") + .arg("--crate-type=lib") + .arg("--emit=metadata") + .arg("--out-dir") + .arg(out_dir) + .arg(probefile) + .status() + .ok() +}
diff --git a/tests/simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING b/tests/simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING new file mode 100644 index 0000000..f103256 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/LICENSE-FOR-TESTING
@@ -0,0 +1 @@ +Stub license for testing applicable_licenses. \ No newline at end of file
diff --git a/tests/simple_deps/vendor/anyhow/src/backtrace.rs b/tests/simple_deps/vendor/anyhow/src/backtrace.rs new file mode 100644 index 0000000..01e33cb --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/backtrace.rs
@@ -0,0 +1,36 @@ +#[cfg(backtrace)] +pub(crate) use std::backtrace::Backtrace; + +#[cfg(not(backtrace))] +pub(crate) enum Backtrace {} + +#[cfg(backtrace)] +macro_rules! backtrace { + () => { + Some(Backtrace::capture()) + }; +} + +#[cfg(not(backtrace))] +macro_rules! backtrace { + () => { + None + }; +} + +#[cfg(backtrace)] +macro_rules! backtrace_if_absent { + ($err:expr) => { + match $err.backtrace() { + Some(_) => None, + None => Some(Backtrace::capture()), + } + }; +} + +#[cfg(all(feature = "std", not(backtrace)))] +macro_rules! backtrace_if_absent { + ($err:expr) => { + None + }; +}
diff --git a/tests/simple_deps/vendor/anyhow/src/chain.rs b/tests/simple_deps/vendor/anyhow/src/chain.rs new file mode 100644 index 0000000..97fd0e3 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/chain.rs
@@ -0,0 +1,95 @@ +use self::ChainState::*; +use crate::StdError; + +#[cfg(feature = "std")] +use std::vec; + +#[cfg(feature = "std")] +pub(crate) use crate::Chain; + +#[cfg(not(feature = "std"))] +pub(crate) struct Chain<'a> { + state: ChainState<'a>, +} + +#[derive(Clone)] +pub(crate) enum ChainState<'a> { + Linked { + next: Option<&'a (dyn StdError + 'static)>, + }, + #[cfg(feature = "std")] + Buffered { + rest: vec::IntoIter<&'a (dyn StdError + 'static)>, + }, +} + +impl<'a> Chain<'a> { + pub fn new(head: &'a (dyn StdError + 'static)) -> Self { + Chain { state: ChainState::Linked { next: Some(head) } } + } +} + +impl<'a> Iterator for Chain<'a> { + type Item = &'a (dyn StdError + 'static); + + fn next(&mut self) -> Option<Self::Item> { + match &mut self.state { + Linked { next } => { + let error = (*next)?; + *next = error.source(); + Some(error) + } + #[cfg(feature = "std")] + Buffered { rest } => rest.next(), + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let len = self.len(); + (len, Some(len)) + } +} + +#[cfg(feature = "std")] +impl DoubleEndedIterator for Chain<'_> { + fn next_back(&mut self) -> Option<Self::Item> { + match &mut self.state { + Linked { mut next } => { + let mut rest = Vec::new(); + while let Some(cause) = next { + next = cause.source(); + rest.push(cause); + } + let mut rest = rest.into_iter(); + let last = rest.next_back(); + self.state = Buffered { rest }; + last + } + Buffered { rest } => rest.next_back(), + } + } +} + +impl ExactSizeIterator for Chain<'_> { + fn len(&self) -> usize { + match &self.state { + Linked { mut next } => { + let mut len = 0; + while let Some(cause) = next { + next = cause.source(); + len += 1; + } + len + } + #[cfg(feature = "std")] + Buffered { rest } => rest.len(), + } + } +} + +#[cfg(feature = "std")] +impl Default for Chain<'_> { + fn default() -> Self { + Chain { state: ChainState::Buffered { rest: Vec::new().into_iter() } } + } +}
diff --git a/tests/simple_deps/vendor/anyhow/src/context.rs b/tests/simple_deps/vendor/anyhow/src/context.rs new file mode 100644 index 0000000..25d3411 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/context.rs
@@ -0,0 +1,177 @@ +use crate::error::ContextError; +use crate::{Context, Error, StdError}; +use core::convert::Infallible; +use core::fmt::{self, Debug, Display, Write}; + +#[cfg(backtrace)] +use std::backtrace::Backtrace; + +mod ext { + use super::*; + + pub trait StdError { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static; + } + + #[cfg(feature = "std")] + impl<E> StdError for E + where + E: std::error::Error + Send + Sync + 'static, + { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static, + { + let backtrace = backtrace_if_absent!(self); + Error::from_context(context, self, backtrace) + } + } + + impl StdError for Error { + fn ext_context<C>(self, context: C) -> Error + where + C: Display + Send + Sync + 'static, + { + self.context(context) + } + } +} + +impl<T, E> Context<T, E> for Result<T, E> +where + E: ext::StdError + Send + Sync + 'static, +{ + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + { + self.map_err(|error| error.ext_context(context)) + } + + fn with_context<C, F>(self, context: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + self.map_err(|error| error.ext_context(context())) + } +} + +/// ``` +/// # type T = (); +/// # +/// use anyhow::{Context, Result}; +/// +/// fn maybe_get() -> Option<T> { +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// +/// fn demo() -> Result<()> { +/// let t = maybe_get().context("there is no T")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// ``` +impl<T> Context<T, Infallible> for Option<T> { + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + { + self.ok_or_else(|| Error::from_display(context, backtrace!())) + } + + fn with_context<C, F>(self, context: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + self.ok_or_else(|| Error::from_display(context(), backtrace!())) + } +} + +impl<C, E> Debug for ContextError<C, E> +where + C: Display, + E: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Error") + .field("context", &Quoted(&self.context)) + .field("source", &self.error) + .finish() + } +} + +impl<C, E> Display for ContextError<C, E> +where + C: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.context, f) + } +} + +impl<C, E> StdError for ContextError<C, E> +where + C: Display, + E: StdError + 'static, +{ + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> { + self.error.backtrace() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.error) + } +} + +impl<C> StdError for ContextError<C, Error> +where + C: Display, +{ + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> { + Some(self.error.backtrace()) + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(self.error.inner.error()) + } +} + +struct Quoted<C>(C); + +impl<C> Debug for Quoted<C> +where + C: Display, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_char('"')?; + Quoted(&mut *formatter).write_fmt(format_args!("{}", self.0))?; + formatter.write_char('"')?; + Ok(()) + } +} + +impl Write for Quoted<&mut fmt::Formatter<'_>> { + fn write_str(&mut self, s: &str) -> fmt::Result { + Display::fmt(&s.escape_debug(), self.0) + } +} + +pub(crate) mod private { + use super::*; + + pub trait Sealed {} + + impl<T, E> Sealed for Result<T, E> where E: ext::StdError {} + impl<T> Sealed for Option<T> {} +}
diff --git a/tests/simple_deps/vendor/anyhow/src/error.rs b/tests/simple_deps/vendor/anyhow/src/error.rs new file mode 100644 index 0000000..410a8f4 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/error.rs
@@ -0,0 +1,794 @@ +use crate::alloc::Box; +use crate::backtrace::Backtrace; +use crate::chain::Chain; +use crate::{Error, StdError}; +use core::any::TypeId; +use core::fmt::{self, Debug, Display}; +use core::mem::{self, ManuallyDrop}; +use core::ptr::{self, NonNull}; + +#[cfg(feature = "std")] +use core::ops::{Deref, DerefMut}; + +impl Error { + /// Create a new error object from any error type. + /// + /// The error type must be threadsafe and `'static`, so that the `Error` + /// will be as well. + /// + /// If the error type does not provide a backtrace, a backtrace will be + /// created here to ensure that a backtrace exists. + #[cfg(feature = "std")] + pub fn new<E>(error: E) -> Self + where + E: StdError + Send + Sync + 'static, + { + let backtrace = backtrace_if_absent!(error); + Error::from_std(error, backtrace) + } + + /// Create a new error object from a printable error message. + /// + /// If the argument implements std::error::Error, prefer `Error::new` + /// instead which preserves the underlying error's cause chain and + /// backtrace. If the argument may or may not implement std::error::Error + /// now or in the future, use `anyhow!(err)` which handles either way + /// correctly. + /// + /// `Error::msg("...")` is equivalent to `anyhow!("...")` but occasionally + /// convenient in places where a function is preferable over a macro, such + /// as iterator or stream combinators: + /// + /// ``` + /// # mod ffi { + /// # pub struct Input; + /// # pub struct Output; + /// # pub async fn do_some_work(_: Input) -> Result<Output, &'static str> { + /// # unimplemented!() + /// # } + /// # } + /// # + /// # use ffi::{Input, Output}; + /// # + /// use anyhow::{Error, Result}; + /// use futures::stream::{Stream, StreamExt, TryStreamExt}; + /// + /// async fn demo<S>(stream: S) -> Result<Vec<Output>> + /// where + /// S: Stream<Item = Input>, + /// { + /// stream + /// .then(ffi::do_some_work) // returns Result<Output, &str> + /// .map_err(Error::msg) + /// .try_collect() + /// .await + /// } + /// ``` + pub fn msg<M>(message: M) -> Self + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } + + #[cfg(feature = "std")] + pub(crate) fn from_std<E>(error: E, backtrace: Option<Backtrace>) -> Self + where + E: StdError + Send + Sync + 'static, + { + let vtable = &ErrorVTable { + object_drop: object_drop::<E>, + object_ref: object_ref::<E>, + #[cfg(feature = "std")] + object_mut: object_mut::<E>, + object_boxed: object_boxed::<E>, + object_downcast: object_downcast::<E>, + object_drop_rest: object_drop_front::<E>, + }; + + // Safety: passing vtable that operates on the right type E. + unsafe { Error::construct(error, vtable, backtrace) } + } + + pub(crate) fn from_adhoc<M>(message: M, backtrace: Option<Backtrace>) -> Self + where + M: Display + Debug + Send + Sync + 'static, + { + use crate::wrapper::MessageError; + let error: MessageError<M> = MessageError(message); + let vtable = &ErrorVTable { + object_drop: object_drop::<MessageError<M>>, + object_ref: object_ref::<MessageError<M>>, + #[cfg(feature = "std")] + object_mut: object_mut::<MessageError<M>>, + object_boxed: object_boxed::<MessageError<M>>, + object_downcast: object_downcast::<M>, + object_drop_rest: object_drop_front::<M>, + }; + + // Safety: MessageError is repr(transparent) so it is okay for the + // vtable to allow casting the MessageError<M> to M. + unsafe { Error::construct(error, vtable, backtrace) } + } + + pub(crate) fn from_display<M>(message: M, backtrace: Option<Backtrace>) -> Self + where + M: Display + Send + Sync + 'static, + { + use crate::wrapper::DisplayError; + let error: DisplayError<M> = DisplayError(message); + let vtable = &ErrorVTable { + object_drop: object_drop::<DisplayError<M>>, + object_ref: object_ref::<DisplayError<M>>, + #[cfg(feature = "std")] + object_mut: object_mut::<DisplayError<M>>, + object_boxed: object_boxed::<DisplayError<M>>, + object_downcast: object_downcast::<M>, + object_drop_rest: object_drop_front::<M>, + }; + + // Safety: DisplayError is repr(transparent) so it is okay for the + // vtable to allow casting the DisplayError<M> to M. + unsafe { Error::construct(error, vtable, backtrace) } + } + + #[cfg(feature = "std")] + pub(crate) fn from_context<C, E>(context: C, error: E, backtrace: Option<Backtrace>) -> Self + where + C: Display + Send + Sync + 'static, + E: StdError + Send + Sync + 'static, + { + let error: ContextError<C, E> = ContextError { context, error }; + + let vtable = &ErrorVTable { + object_drop: object_drop::<ContextError<C, E>>, + object_ref: object_ref::<ContextError<C, E>>, + #[cfg(feature = "std")] + object_mut: object_mut::<ContextError<C, E>>, + object_boxed: object_boxed::<ContextError<C, E>>, + object_downcast: context_downcast::<C, E>, + object_drop_rest: context_drop_rest::<C, E>, + }; + + // Safety: passing vtable that operates on the right type. + unsafe { Error::construct(error, vtable, backtrace) } + } + + #[cfg(feature = "std")] + pub(crate) fn from_boxed( + error: Box<dyn StdError + Send + Sync>, + backtrace: Option<Backtrace>, + ) -> Self { + use crate::wrapper::BoxedError; + let error = BoxedError(error); + let vtable = &ErrorVTable { + object_drop: object_drop::<BoxedError>, + object_ref: object_ref::<BoxedError>, + #[cfg(feature = "std")] + object_mut: object_mut::<BoxedError>, + object_boxed: object_boxed::<BoxedError>, + object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>, + object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>, + }; + + // Safety: BoxedError is repr(transparent) so it is okay for the vtable + // to allow casting to Box<dyn StdError + Send + Sync>. + unsafe { Error::construct(error, vtable, backtrace) } + } + + // Takes backtrace as argument rather than capturing it here so that the + // user sees one fewer layer of wrapping noise in the backtrace. + // + // Unsafe because the given vtable must have sensible behavior on the error + // value of type E. + unsafe fn construct<E>( + error: E, + vtable: &'static ErrorVTable, + backtrace: Option<Backtrace>, + ) -> Self + where + E: StdError + Send + Sync + 'static, + { + let inner = Box::new(ErrorImpl { vtable, backtrace, _object: error }); + // Erase the concrete type of E from the compile-time type system. This + // is equivalent to the safe unsize coersion from Box<ErrorImpl<E>> to + // Box<ErrorImpl<dyn StdError + Send + Sync + 'static>> except that the + // result is a thin pointer. The necessary behavior for manipulating the + // underlying ErrorImpl<E> is preserved in the vtable provided by the + // caller rather than a builtin fat pointer vtable. + let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner); + let inner = ManuallyDrop::new(erased); + Error { inner } + } + + /// Wrap the error value with additional context. + /// + /// For attaching context to a `Result` as it is propagated, the + /// [`Context`][crate::Context] extension trait may be more convenient than + /// this function. + /// + /// The primary reason to use `error.context(...)` instead of + /// `result.context(...)` via the `Context` trait would be if the context + /// needs to depend on some data held by the underlying error: + /// + /// ``` + /// # use std::fmt::{self, Debug, Display}; + /// # + /// # type T = (); + /// # + /// # impl std::error::Error for ParseError {} + /// # impl Debug for ParseError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # impl Display for ParseError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # + /// use anyhow::Result; + /// use std::fs::File; + /// use std::path::Path; + /// + /// struct ParseError { + /// line: usize, + /// column: usize, + /// } + /// + /// fn parse_impl(file: File) -> Result<T, ParseError> { + /// # const IGNORE: &str = stringify! { + /// ... + /// # }; + /// # unimplemented!() + /// } + /// + /// pub fn parse(path: impl AsRef<Path>) -> Result<T> { + /// let file = File::open(&path)?; + /// parse_impl(file).map_err(|error| { + /// let context = format!( + /// "only the first {} lines of {} are valid", + /// error.line, path.as_ref().display(), + /// ); + /// anyhow::Error::new(error).context(context) + /// }) + /// } + /// ``` + pub fn context<C>(self, context: C) -> Self + where + C: Display + Send + Sync + 'static, + { + let error: ContextError<C, Error> = ContextError { context, error: self }; + + let vtable = &ErrorVTable { + object_drop: object_drop::<ContextError<C, Error>>, + object_ref: object_ref::<ContextError<C, Error>>, + #[cfg(feature = "std")] + object_mut: object_mut::<ContextError<C, Error>>, + object_boxed: object_boxed::<ContextError<C, Error>>, + object_downcast: context_chain_downcast::<C>, + object_drop_rest: context_chain_drop_rest::<C>, + }; + + // As the cause is anyhow::Error, we already have a backtrace for it. + let backtrace = None; + + // Safety: passing vtable that operates on the right type. + unsafe { Error::construct(error, vtable, backtrace) } + } + + /// Get the backtrace for this Error. + /// + /// Backtraces are only available on the nightly channel. Tracking issue: + /// [rust-lang/rust#53487][tracking]. + /// + /// In order for the backtrace to be meaningful, one of the two environment + /// variables `RUST_LIB_BACKTRACE=1` or `RUST_BACKTRACE=1` must be defined + /// and `RUST_LIB_BACKTRACE` must not be `0`. Backtraces are somewhat + /// expensive to capture in Rust, so we don't necessarily want to be + /// capturing them all over the place all the time. + /// + /// - If you want panics and errors to both have backtraces, set + /// `RUST_BACKTRACE=1`; + /// - If you want only errors to have backtraces, set + /// `RUST_LIB_BACKTRACE=1`; + /// - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and + /// `RUST_LIB_BACKTRACE=0`. + /// + /// [tracking]: https://github.com/rust-lang/rust/issues/53487 + #[cfg(backtrace)] + pub fn backtrace(&self) -> &Backtrace { + self.inner.backtrace() + } + + /// An iterator of the chain of source errors contained by this Error. + /// + /// This iterator will visit every error in the cause chain of this error + /// object, beginning with the error that this error object was created + /// from. + /// + /// # Example + /// + /// ``` + /// use anyhow::Error; + /// use std::io; + /// + /// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> { + /// for cause in error.chain() { + /// if let Some(io_error) = cause.downcast_ref::<io::Error>() { + /// return Some(io_error.kind()); + /// } + /// } + /// None + /// } + /// ``` + #[cfg(feature = "std")] + pub fn chain(&self) -> Chain { + self.inner.chain() + } + + /// The lowest level cause of this error — this error's cause's + /// cause's cause etc. + /// + /// The root cause is the last error in the iterator produced by + /// [`chain()`][Error::chain]. + #[cfg(feature = "std")] + pub fn root_cause(&self) -> &(dyn StdError + 'static) { + let mut chain = self.chain(); + let mut root_cause = chain.next().unwrap(); + for cause in chain { + root_cause = cause; + } + root_cause + } + + /// Returns true if `E` is the type held by this error object. + /// + /// For errors with context, this method returns true if `E` matches the + /// type of the context `C` **or** the type of the error on which the + /// context has been attached. For details about the interaction between + /// context and downcasting, [see here]. + /// + /// [see here]: trait.Context.html#effect-on-downcasting + pub fn is<E>(&self) -> bool + where + E: Display + Debug + Send + Sync + 'static, + { + self.downcast_ref::<E>().is_some() + } + + /// Attempt to downcast the error object to a concrete type. + pub fn downcast<E>(self) -> Result<E, Self> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = match (self.inner.vtable.object_downcast)(&self.inner, target) { + Some(addr) => addr, + None => return Err(self), + }; + + // Prepare to read E out of the data structure. We'll drop the rest + // of the data structure separately so that E is not dropped. + let outer = ManuallyDrop::new(self); + + // Read E from where the vtable found it. + let error = ptr::read(addr.cast::<E>().as_ptr()); + + // Read Box<ErrorImpl<()>> from self. Can't move it out because + // Error has a Drop impl which we want to not run. + let inner = ptr::read(&outer.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Drop rest of the data structure outside of E. + (erased.vtable.object_drop_rest)(erased, target); + + Ok(error) + } + } + + /// Downcast this error object by reference. + /// + /// # Example + /// + /// ``` + /// # use anyhow::anyhow; + /// # use std::fmt::{self, Display}; + /// # use std::task::Poll; + /// # + /// # #[derive(Debug)] + /// # enum DataStoreError { + /// # Censored(()), + /// # } + /// # + /// # impl Display for DataStoreError { + /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + /// # unimplemented!() + /// # } + /// # } + /// # + /// # impl std::error::Error for DataStoreError {} + /// # + /// # const REDACTED_CONTENT: () = (); + /// # + /// # let error = anyhow!("..."); + /// # let root_cause = &error; + /// # + /// # let ret = + /// // If the error was caused by redaction, then return a tombstone instead + /// // of the content. + /// match root_cause.downcast_ref::<DataStoreError>() { + /// Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), + /// None => Err(error), + /// } + /// # ; + /// ``` + pub fn downcast_ref<E>(&self) -> Option<&E> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&*addr.cast::<E>().as_ptr()) + } + } + + /// Downcast this error object by mutable reference. + pub fn downcast_mut<E>(&mut self) -> Option<&mut E> + where + E: Display + Debug + Send + Sync + 'static, + { + let target = TypeId::of::<E>(); + unsafe { + // Use vtable to find NonNull<()> which points to a value of type E + // somewhere inside the data structure. + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&mut *addr.cast::<E>().as_ptr()) + } + } +} + +#[cfg(feature = "std")] +impl<E> From<E> for Error +where + E: StdError + Send + Sync + 'static, +{ + fn from(error: E) -> Self { + let backtrace = backtrace_if_absent!(error); + Error::from_std(error, backtrace) + } +} + +#[cfg(feature = "std")] +impl Deref for Error { + type Target = dyn StdError + Send + Sync + 'static; + + fn deref(&self) -> &Self::Target { + self.inner.error() + } +} + +#[cfg(feature = "std")] +impl DerefMut for Error { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.error_mut() + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.inner.display(formatter) + } +} + +impl Debug for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.inner.debug(formatter) + } +} + +impl Drop for Error { + fn drop(&mut self) { + unsafe { + // Read Box<ErrorImpl<()>> from self. + let inner = ptr::read(&self.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Invoke the vtable's drop behavior. + (erased.vtable.object_drop)(erased); + } + } +} + +struct ErrorVTable { + object_drop: unsafe fn(Box<ErrorImpl<()>>), + object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static), + #[cfg(feature = "std")] + object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static), + object_boxed: unsafe fn(Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>, + object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option<NonNull<()>>, + object_drop_rest: unsafe fn(Box<ErrorImpl<()>>, TypeId), +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) { + // Cast back to ErrorImpl<E> so that the allocator receives the correct + // Layout to deallocate the Box's memory. + let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e); + drop(unerased); +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) { + // Drop the fields of ErrorImpl other than E as well as the Box allocation, + // without dropping E itself. This is used by downcast after doing a + // ptr::read to take ownership of the E. + let _ = target; + let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<ManuallyDrop<E>>>>(e); + drop(unerased); +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_ref<E>(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static) +where + E: StdError + Send + Sync + 'static, +{ + // Attach E's native StdError vtable onto a pointer to self._object. + &(*(e as *const ErrorImpl<()> as *const ErrorImpl<E>))._object +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +#[cfg(feature = "std")] +unsafe fn object_mut<E>(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static) +where + E: StdError + Send + Sync + 'static, +{ + // Attach E's native StdError vtable onto a pointer to self._object. + &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl<E>))._object +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_boxed<E>(e: Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static> +where + E: StdError + Send + Sync + 'static, +{ + // Attach ErrorImpl<E>'s native StdError vtable. The StdError impl is below. + mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e) +} + +// Safety: requires layout of *e to match ErrorImpl<E>. +unsafe fn object_downcast<E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + E: 'static, +{ + if TypeId::of::<E>() == target { + // Caller is looking for an E pointer and e is ErrorImpl<E>, take a + // pointer to its E field. + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<E>; + let addr = &(*unerased)._object as *const E as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + None + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>. +#[cfg(feature = "std")] +unsafe fn context_downcast<C, E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + C: 'static, + E: 'static, +{ + if TypeId::of::<C>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>; + let addr = &(*unerased)._object.context as *const C as *mut (); + Some(NonNull::new_unchecked(addr)) + } else if TypeId::of::<E>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, E>>; + let addr = &(*unerased)._object.error as *const E as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + None + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>. +#[cfg(feature = "std")] +unsafe fn context_drop_rest<C, E>(e: Box<ErrorImpl<()>>, target: TypeId) +where + C: 'static, + E: 'static, +{ + // Called after downcasting by value to either the C or the E and doing a + // ptr::read to take ownership of that value. + if TypeId::of::<C>() == target { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<ManuallyDrop<C>, E>>>, + >(e); + drop(unerased); + } else { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<C, ManuallyDrop<E>>>>, + >(e); + drop(unerased); + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>. +unsafe fn context_chain_downcast<C>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>> +where + C: 'static, +{ + if TypeId::of::<C>() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>; + let addr = &(*unerased)._object.context as *const C as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + // Recurse down the context chain per the inner error's vtable. + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>; + let source = &(*unerased)._object.error; + (source.inner.vtable.object_downcast)(&source.inner, target) + } +} + +// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>. +unsafe fn context_chain_drop_rest<C>(e: Box<ErrorImpl<()>>, target: TypeId) +where + C: 'static, +{ + // Called after downcasting by value to either the C or one of the causes + // and doing a ptr::read to take ownership of that value. + if TypeId::of::<C>() == target { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<ManuallyDrop<C>, Error>>>, + >(e); + // Drop the entire rest of the data structure rooted in the next Error. + drop(unerased); + } else { + let unerased = mem::transmute::< + Box<ErrorImpl<()>>, + Box<ErrorImpl<ContextError<C, ManuallyDrop<Error>>>>, + >(e); + // Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error. + let inner = ptr::read(&unerased._object.error.inner); + drop(unerased); + let erased = ManuallyDrop::into_inner(inner); + // Recursively drop the next error using the same target typeid. + (erased.vtable.object_drop_rest)(erased, target); + } +} + +// repr C to ensure that E remains in the final position. +#[repr(C)] +pub(crate) struct ErrorImpl<E> { + vtable: &'static ErrorVTable, + backtrace: Option<Backtrace>, + // NOTE: Don't use directly. Use only through vtable. Erased type may have + // different alignment. + _object: E, +} + +// repr C to ensure that ContextError<C, E> has the same layout as +// ContextError<ManuallyDrop<C>, E> and ContextError<C, ManuallyDrop<E>>. +#[repr(C)] +pub(crate) struct ContextError<C, E> { + pub context: C, + pub error: E, +} + +impl<E> ErrorImpl<E> { + fn erase(&self) -> &ErrorImpl<()> { + // Erase the concrete type of E but preserve the vtable in self.vtable + // for manipulating the resulting thin pointer. This is analogous to an + // unsize coersion. + unsafe { &*(self as *const ErrorImpl<E> as *const ErrorImpl<()>) } + } +} + +impl ErrorImpl<()> { + pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) { + // Use vtable to attach E's native StdError vtable for the right + // original type E. + unsafe { &*(self.vtable.object_ref)(self) } + } + + #[cfg(feature = "std")] + pub(crate) fn error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) { + // Use vtable to attach E's native StdError vtable for the right + // original type E. + unsafe { &mut *(self.vtable.object_mut)(self) } + } + + #[cfg(backtrace)] + pub(crate) fn backtrace(&self) -> &Backtrace { + // This unwrap can only panic if the underlying error's backtrace method + // is nondeterministic, which would only happen in maliciously + // constructed code. + self.backtrace + .as_ref() + .or_else(|| self.error().backtrace()) + .expect("backtrace capture failed") + } + + pub(crate) fn chain(&self) -> Chain { + Chain::new(self.error()) + } +} + +impl<E> StdError for ErrorImpl<E> +where + E: StdError, +{ + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&Backtrace> { + Some(self.erase().backtrace()) + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.erase().error().source() + } +} + +impl<E> Debug for ErrorImpl<E> +where + E: Debug, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.erase().debug(formatter) + } +} + +impl<E> Display for ErrorImpl<E> +where + E: Display, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.erase().error(), formatter) + } +} + +impl From<Error> for Box<dyn StdError + Send + Sync + 'static> { + fn from(error: Error) -> Self { + let outer = ManuallyDrop::new(error); + unsafe { + // Read Box<ErrorImpl<()>> from error. Can't move it out because + // Error has a Drop impl which we want to not run. + let inner = ptr::read(&outer.inner); + let erased = ManuallyDrop::into_inner(inner); + + // Use vtable to attach ErrorImpl<E>'s native StdError vtable for + // the right original type E. + (erased.vtable.object_boxed)(erased) + } + } +} + +impl From<Error> for Box<dyn StdError + 'static> { + fn from(error: Error) -> Self { + Box::<dyn StdError + Send + Sync>::from(error) + } +} + +#[cfg(feature = "std")] +impl AsRef<dyn StdError + Send + Sync> for Error { + fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { + &**self + } +} + +#[cfg(feature = "std")] +impl AsRef<dyn StdError> for Error { + fn as_ref(&self) -> &(dyn StdError + 'static) { + &**self + } +}
diff --git a/tests/simple_deps/vendor/anyhow/src/fmt.rs b/tests/simple_deps/vendor/anyhow/src/fmt.rs new file mode 100644 index 0000000..9579478 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/fmt.rs
@@ -0,0 +1,131 @@ +use crate::chain::Chain; +use crate::error::ErrorImpl; +use core::fmt::{self, Debug, Write}; + +impl ErrorImpl<()> { + pub(crate) fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.error())?; + + if f.alternate() { + for cause in self.chain().skip(1) { + write!(f, ": {}", cause)?; + } + } + + Ok(()) + } + + pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { + let error = self.error(); + + if f.alternate() { + return Debug::fmt(error, f); + } + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + let multiple = cause.source().is_some(); + for (n, error) in Chain::new(cause).enumerate() { + writeln!(f)?; + let mut indented = Indented { + inner: f, + number: if multiple { Some(n) } else { None }, + started: false, + }; + write!(indented, "{}", error)?; + } + } + + #[cfg(backtrace)] + { + use std::backtrace::BacktraceStatus; + + let backtrace = self.backtrace(); + if let BacktraceStatus::Captured = backtrace.status() { + let mut backtrace = backtrace.to_string(); + if backtrace.starts_with("stack backtrace:") { + // Capitalize to match "Caused by:" + backtrace.replace_range(0..1, "S"); + } + backtrace.truncate(backtrace.trim_end().len()); + write!(f, "\n\n{}", backtrace)?; + } + } + + Ok(()) + } +} + +struct Indented<'a, D> { + inner: &'a mut D, + number: Option<usize>, + started: bool, +} + +impl<T> Write for Indented<'_, T> +where + T: Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if !self.started { + self.started = true; + match self.number { + Some(number) => write!(self.inner, "{: >5}: ", number)?, + None => self.inner.write_str(" ")?, + } + } else if i > 0 { + self.inner.write_char('\n')?; + if self.number.is_some() { + self.inner.write_str(" ")?; + } else { + self.inner.write_str(" ")?; + } + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn one_digit() { + let input = "verify\nthis"; + let expected = " 2: verify\n this"; + let mut output = String::new(); + + Indented { inner: &mut output, number: Some(2), started: false }.write_str(input).unwrap(); + + assert_eq!(expected, output); + } + + #[test] + fn two_digits() { + let input = "verify\nthis"; + let expected = " 12: verify\n this"; + let mut output = String::new(); + + Indented { inner: &mut output, number: Some(12), started: false }.write_str(input).unwrap(); + + assert_eq!(expected, output); + } + + #[test] + fn no_digits() { + let input = "verify\nthis"; + let expected = " verify\n this"; + let mut output = String::new(); + + Indented { inner: &mut output, number: None, started: false }.write_str(input).unwrap(); + + assert_eq!(expected, output); + } +}
diff --git a/tests/simple_deps/vendor/anyhow/src/kind.rs b/tests/simple_deps/vendor/anyhow/src/kind.rs new file mode 100644 index 0000000..fdeb060 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/kind.rs
@@ -0,0 +1,116 @@ +// Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`. +// +// When anyhow! is given a single expr argument to turn into anyhow::Error, we +// want the resulting Error to pick up the input's implementation of source() +// and backtrace() if it has a std::error::Error impl, otherwise require nothing +// more than Display and Debug. +// +// Expressed in terms of specialization, we want something like: +// +// trait AnyhowNew { +// fn new(self) -> Error; +// } +// +// impl<T> AnyhowNew for T +// where +// T: Display + Debug + Send + Sync + 'static, +// { +// default fn new(self) -> Error { +// /* no std error impl */ +// } +// } +// +// impl<T> AnyhowNew for T +// where +// T: std::error::Error + Send + Sync + 'static, +// { +// fn new(self) -> Error { +// /* use std error's source() and backtrace() */ +// } +// } +// +// Since specialization is not stable yet, instead we rely on autoref behavior +// of method resolution to perform tagged dispatch. Here we have two traits +// AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is +// implemented whether or not the caller's type has a std error impl, while +// TraitKind is implemented only when a std error impl does exist. The ambiguity +// is resolved by AdhocKind requiring an extra autoref so that it has lower +// precedence. +// +// The anyhow! macro will set up the call in this form: +// +// #[allow(unused_imports)] +// use $crate::private::{AdhocKind, TraitKind}; +// let error = $msg; +// (&error).anyhow_kind().new(error) + +use crate::Error; +use core::fmt::{Debug, Display}; + +#[cfg(feature = "std")] +use crate::StdError; + +#[cfg(backtrace)] +use std::backtrace::Backtrace; + +pub struct Adhoc; + +pub trait AdhocKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Adhoc { + Adhoc + } +} + +impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} + +impl Adhoc { + pub fn new<M>(self, message: M) -> Error + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } +} + +pub struct Trait; + +pub trait TraitKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Trait { + Trait + } +} + +impl<E> TraitKind for E where E: Into<Error> {} + +impl Trait { + pub fn new<E>(self, error: E) -> Error + where + E: Into<Error>, + { + error.into() + } +} + +#[cfg(feature = "std")] +pub struct Boxed; + +#[cfg(feature = "std")] +pub trait BoxedKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Boxed { + Boxed + } +} + +#[cfg(feature = "std")] +impl BoxedKind for Box<dyn StdError + Send + Sync> {} + +#[cfg(feature = "std")] +impl Boxed { + pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error { + let backtrace = backtrace_if_absent!(error); + Error::from_boxed(error, backtrace) + } +}
diff --git a/tests/simple_deps/vendor/anyhow/src/lib.rs b/tests/simple_deps/vendor/anyhow/src/lib.rs new file mode 100644 index 0000000..3e49658 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/lib.rs
@@ -0,0 +1,587 @@ +//! This library provides [`anyhow::Error`][Error], a trait object based error +//! type for easy idiomatic error handling in Rust applications. +//! +//! <br> +//! +//! # Details +//! +//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as +//! the return type of any fallible function. +//! +//! Within the function, use `?` to easily propagate any error that implements +//! the `std::error::Error` trait. +//! +//! ``` +//! # pub trait Deserialize {} +//! # +//! # mod serde_json { +//! # use super::Deserialize; +//! # use std::io; +//! # +//! # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { +//! # unimplemented!() +//! # } +//! # } +//! # +//! # struct ClusterMap; +//! # +//! # impl Deserialize for ClusterMap {} +//! # +//! use anyhow::Result; +//! +//! fn get_cluster_info() -> Result<ClusterMap> { +//! let config = std::fs::read_to_string("cluster.json")?; +//! let map: ClusterMap = serde_json::from_str(&config)?; +//! Ok(map) +//! } +//! # +//! # fn main() {} +//! ``` +//! +//! - Attach context to help the person troubleshooting the error understand +//! where things went wrong. A low-level error like "No such file or +//! directory" can be annoying to debug without more context about what higher +//! level step the application was in the middle of. +//! +//! ``` +//! # struct It; +//! # +//! # impl It { +//! # fn detach(&self) -> Result<()> { +//! # unimplemented!() +//! # } +//! # } +//! # +//! use anyhow::{Context, Result}; +//! +//! fn main() -> Result<()> { +//! # return Ok(()); +//! # +//! # const _: &str = stringify! { +//! ... +//! # }; +//! # +//! # let it = It; +//! # let path = "./path/to/instrs.json"; +//! # +//! it.detach().context("Failed to detach the important thing")?; +//! +//! let content = std::fs::read(path) +//! .with_context(|| format!("Failed to read instrs from {}", path))?; +//! # +//! # const _: &str = stringify! { +//! ... +//! # }; +//! # +//! # Ok(()) +//! } +//! ``` +//! +//! ```console +//! Error: Failed to read instrs from ./path/to/instrs.json +//! +//! Caused by: +//! No such file or directory (os error 2) +//! ``` +//! +//! - Downcasting is supported and can be by value, by shared reference, or by +//! mutable reference as needed. +//! +//! ``` +//! # use anyhow::anyhow; +//! # use std::fmt::{self, Display}; +//! # use std::task::Poll; +//! # +//! # #[derive(Debug)] +//! # enum DataStoreError { +//! # Censored(()), +//! # } +//! # +//! # impl Display for DataStoreError { +//! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +//! # unimplemented!() +//! # } +//! # } +//! # +//! # impl std::error::Error for DataStoreError {} +//! # +//! # const REDACTED_CONTENT: () = (); +//! # +//! # let error = anyhow!("..."); +//! # let root_cause = &error; +//! # +//! # let ret = +//! // If the error was caused by redaction, then return a +//! // tombstone instead of the content. +//! match root_cause.downcast_ref::<DataStoreError>() { +//! Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), +//! None => Err(error), +//! } +//! # ; +//! ``` +//! +//! - A backtrace is captured and printed with the error if the underlying error +//! type does not already provide its own. In order to see backtraces, they +//! must be enabled through the environment variables described in +//! [`std::backtrace`]: +//! +//! - If you want panics and errors to both have backtraces, set +//! `RUST_BACKTRACE=1`; +//! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; +//! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and +//! `RUST_LIB_BACKTRACE=0`. +//! +//! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables +//! +//! - Anyhow works with any error type that has an impl of `std::error::Error`, +//! including ones defined in your crate. We do not bundle a `derive(Error)` +//! macro but you can write the impls yourself or use a standalone macro like +//! [thiserror]. +//! +//! [thiserror]: https://github.com/dtolnay/thiserror +//! +//! ``` +//! use thiserror::Error; +//! +//! #[derive(Error, Debug)] +//! pub enum FormatError { +//! #[error("Invalid header (expected {expected:?}, got {found:?})")] +//! InvalidHeader { +//! expected: String, +//! found: String, +//! }, +//! #[error("Missing attribute: {0}")] +//! MissingAttribute(String), +//! } +//! ``` +//! +//! - One-off error messages can be constructed using the `anyhow!` macro, which +//! supports string interpolation and produces an `anyhow::Error`. +//! +//! ``` +//! # use anyhow::{anyhow, Result}; +//! # +//! # fn demo() -> Result<()> { +//! # let missing = "..."; +//! return Err(anyhow!("Missing attribute: {}", missing)); +//! # Ok(()) +//! # } +//! ``` +//! +//! <br> +//! +//! # No-std support +//! +//! In no_std mode, the same API is almost all available and works the same way. +//! To depend on Anyhow in no_std mode, disable our default enabled "std" +//! feature in Cargo.toml. A global allocator is required. +//! +//! ```toml +//! [dependencies] +//! anyhow = { version = "1.0", default-features = false } +//! ``` +//! +//! Since the `?`-based error conversions would normally rely on the +//! `std::error::Error` trait which is only available through std, no_std mode +//! will require an explicit `.map_err(Error::msg)` when working with a +//! non-Anyhow error type inside a function that returns Anyhow's error type. + +#![doc(html_root_url = "https://docs.rs/anyhow/1.0.27")] +#![cfg_attr(backtrace, feature(backtrace))] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::needless_doctest_main, clippy::new_ret_no_self, clippy::wrong_self_convention)] + +mod alloc { + #[cfg(not(feature = "std"))] + extern crate alloc; + + #[cfg(not(feature = "std"))] + pub use alloc::boxed::Box; + + #[cfg(feature = "std")] + pub use std::boxed::Box; +} + +#[macro_use] +mod backtrace; +mod chain; +mod context; +mod error; +mod fmt; +mod kind; +mod macros; +mod wrapper; + +use crate::alloc::Box; +use crate::error::ErrorImpl; +use core::fmt::Display; +use core::mem::ManuallyDrop; + +#[cfg(not(feature = "std"))] +use core::fmt::Debug; + +#[cfg(feature = "std")] +use std::error::Error as StdError; + +#[cfg(not(feature = "std"))] +trait StdError: Debug + Display { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + None + } +} + +pub use anyhow as format_err; + +/// The `Error` type, a wrapper around a dynamic error type. +/// +/// `Error` works a lot like `Box<dyn std::error::Error>`, but with these +/// differences: +/// +/// - `Error` requires that the error is `Send`, `Sync`, and `'static`. +/// - `Error` guarantees that a backtrace is available, even if the underlying +/// error type does not provide one. +/// - `Error` is represented as a narrow pointer — exactly one word in +/// size instead of two. +/// +/// <br> +/// +/// # Display representations +/// +/// When you print an error object using "{}" or to_string(), only the outermost +/// underlying error or context is printed, not any of the lower level causes. +/// This is exactly as if you had called the Display impl of the error from +/// which you constructed your anyhow::Error. +/// +/// ```console +/// Failed to read instrs from ./path/to/instrs.json +/// ``` +/// +/// To print causes as well using anyhow's default formatting of causes, use the +/// alternate selector "{:#}". +/// +/// ```console +/// Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2) +/// ``` +/// +/// The Debug format "{:?}" includes your backtrace if one was captured. Note +/// that this is the representation you get by default if you return an error +/// from `fn main` instead of printing it explicitly yourself. +/// +/// ```console +/// Error: Failed to read instrs from ./path/to/instrs.json +/// +/// Caused by: +/// No such file or directory (os error 2) +/// +/// Stack backtrace: +/// 0: <E as anyhow::context::ext::StdError>::ext_context +/// at /git/anyhow/src/backtrace.rs:26 +/// 1: core::result::Result<T,E>::map_err +/// at /git/rustc/src/libcore/result.rs:596 +/// 2: anyhow::context::<impl anyhow::Context<T,E> for core::result::Result<T,E>>::with_context +/// at /git/anyhow/src/context.rs:58 +/// 3: testing::main +/// at src/main.rs:5 +/// 4: std::rt::lang_start +/// at /git/rustc/src/libstd/rt.rs:61 +/// 5: main +/// 6: __libc_start_main +/// 7: _start +/// ``` +/// +/// To see a conventional struct-style Debug representation, use "{:#?}". +/// +/// ```console +/// Error { +/// context: "Failed to read instrs from ./path/to/instrs.json", +/// source: Os { +/// code: 2, +/// kind: NotFound, +/// message: "No such file or directory", +/// }, +/// } +/// ``` +/// +/// If none of the built-in representations are appropriate and you would prefer +/// to render the error and its cause chain yourself, it can be done something +/// like this: +/// +/// ``` +/// use anyhow::{Context, Result}; +/// +/// fn main() { +/// if let Err(err) = try_main() { +/// eprintln!("ERROR: {}", err); +/// err.chain().skip(1).for_each(|cause| eprintln!("because: {}", cause)); +/// std::process::exit(1); +/// } +/// } +/// +/// fn try_main() -> Result<()> { +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # Ok(()) +/// } +/// ``` +pub struct Error { + inner: ManuallyDrop<Box<ErrorImpl<()>>>, +} + +/// Iterator of a chain of source errors. +/// +/// This type is the iterator returned by [`Error::chain`]. +/// +/// # Example +/// +/// ``` +/// use anyhow::Error; +/// use std::io; +/// +/// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> { +/// for cause in error.chain() { +/// if let Some(io_error) = cause.downcast_ref::<io::Error>() { +/// return Some(io_error.kind()); +/// } +/// } +/// None +/// } +/// ``` +#[cfg(feature = "std")] +#[derive(Clone)] +pub struct Chain<'a> { + state: crate::chain::ChainState<'a>, +} + +/// `Result<T, Error>` +/// +/// This is a reasonable return type to use throughout your application but also +/// for `fn main`; if you do, failures will be printed along with any +/// [context][Context] and a backtrace if one was captured. +/// +/// `anyhow::Result` may be used with one *or* two type parameters. +/// +/// ```rust +/// use anyhow::Result; +/// +/// # const IGNORE: &str = stringify! { +/// fn demo1() -> Result<T> {...} +/// // ^ equivalent to std::result::Result<T, anyhow::Error> +/// +/// fn demo2() -> Result<T, OtherError> {...} +/// // ^ equivalent to std::result::Result<T, OtherError> +/// # }; +/// ``` +/// +/// # Example +/// +/// ``` +/// # pub trait Deserialize {} +/// # +/// # mod serde_json { +/// # use super::Deserialize; +/// # use std::io; +/// # +/// # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { +/// # unimplemented!() +/// # } +/// # } +/// # +/// # #[derive(Debug)] +/// # struct ClusterMap; +/// # +/// # impl Deserialize for ClusterMap {} +/// # +/// use anyhow::Result; +/// +/// fn main() -> Result<()> { +/// # return Ok(()); +/// let config = std::fs::read_to_string("cluster.json")?; +/// let map: ClusterMap = serde_json::from_str(&config)?; +/// println!("cluster info: {:#?}", map); +/// Ok(()) +/// } +/// ``` +pub type Result<T, E = Error> = core::result::Result<T, E>; + +/// Provides the `context` method for `Result`. +/// +/// This trait is sealed and cannot be implemented for types outside of +/// `anyhow`. +/// +/// <br> +/// +/// # Example +/// +/// ``` +/// use anyhow::{Context, Result}; +/// use std::fs; +/// use std::path::PathBuf; +/// +/// pub struct ImportantThing { +/// path: PathBuf, +/// } +/// +/// impl ImportantThing { +/// # const IGNORE: &'static str = stringify! { +/// pub fn detach(&mut self) -> Result<()> {...} +/// # }; +/// # fn detach(&mut self) -> Result<()> { +/// # unimplemented!() +/// # } +/// } +/// +/// pub fn do_it(mut it: ImportantThing) -> Result<Vec<u8>> { +/// it.detach().context("Failed to detach the important thing")?; +/// +/// let path = &it.path; +/// let content = fs::read(path) +/// .with_context(|| format!("Failed to read instrs from {}", path.display()))?; +/// +/// Ok(content) +/// } +/// ``` +/// +/// When printed, the outermost context would be printed first and the lower +/// level underlying causes would be enumerated below. +/// +/// ```console +/// Error: Failed to read instrs from ./path/to/instrs.json +/// +/// Caused by: +/// No such file or directory (os error 2) +/// ``` +/// +/// <br> +/// +/// # Effect on downcasting +/// +/// After attaching context of type `C` onto an error of type `E`, the resulting +/// `anyhow::Error` may be downcast to `C` **or** to `E`. +/// +/// That is, in codebases that rely on downcasting, Anyhow's context supports +/// both of the following use cases: +/// +/// - **Attaching context whose type is insignificant onto errors whose type +/// is used in downcasts.** +/// +/// In other error libraries whose context is not designed this way, it can +/// be risky to introduce context to existing code because new context might +/// break existing working downcasts. In Anyhow, any downcast that worked +/// before adding context will continue to work after you add a context, so +/// you should freely add human-readable context to errors wherever it would +/// be helpful. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct SuspiciousError; +/// # +/// # fn helper() -> Result<()> { +/// # bail!(SuspiciousError); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context("Failed to complete the work")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::<SuspiciousError>() { +/// // If helper() returned SuspiciousError, this downcast will +/// // correctly succeed even with the context in between. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` +/// +/// - **Attaching context whose type is used in downcasts onto errors whose +/// type is insignificant.** +/// +/// Some codebases prefer to use machine-readable context to categorize +/// lower level errors in a way that will be actionable to higher levels of +/// the application. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct HelperFailed; +/// # +/// # fn helper() -> Result<()> { +/// # bail!("no such file or directory"); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context(HelperFailed)?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::<HelperFailed>() { +/// // If helper failed, this downcast will succeed because +/// // HelperFailed is the context that has been attached to +/// // that error. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` +pub trait Context<T, E>: context::private::Sealed { + /// Wrap the error value with additional context. + fn context<C>(self, context: C) -> Result<T, Error> + where + C: Display + Send + Sync + 'static; + + /// Wrap the error value with additional context that is evaluated lazily + /// only once an error does occur. + fn with_context<C, F>(self, f: F) -> Result<T, Error> + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C; +} + +// Not public API. Referenced by macro-generated code. +#[doc(hidden)] +pub mod private { + use crate::Error; + use core::fmt::{Debug, Display}; + + #[cfg(backtrace)] + use std::backtrace::Backtrace; + + pub use core::result::Result::Err; + + #[doc(hidden)] + pub mod kind { + pub use crate::kind::{AdhocKind, TraitKind}; + + #[cfg(feature = "std")] + pub use crate::kind::BoxedKind; + } + + pub fn new_adhoc<M>(message: M) -> Error + where + M: Display + Debug + Send + Sync + 'static, + { + Error::from_adhoc(message, backtrace!()) + } +}
diff --git a/tests/simple_deps/vendor/anyhow/src/macros.rs b/tests/simple_deps/vendor/anyhow/src/macros.rs new file mode 100644 index 0000000..15a9208 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/macros.rs
@@ -0,0 +1,163 @@ +/// Return early with an error. +/// +/// This macro is equivalent to `return Err(From::from($err))`. +/// +/// # Example +/// +/// ``` +/// # use anyhow::{bail, Result}; +/// # +/// # fn has_permission(user: usize, resource: usize) -> bool { +/// # true +/// # } +/// # +/// # fn main() -> Result<()> { +/// # let user = 0; +/// # let resource = 0; +/// # +/// if !has_permission(user, resource) { +/// bail!("permission denied for accessing {}", resource); +/// } +/// # Ok(()) +/// # } +/// ``` +/// +/// ``` +/// # use anyhow::{bail, Result}; +/// # use thiserror::Error; +/// # +/// # const MAX_DEPTH: usize = 1; +/// # +/// #[derive(Error, Debug)] +/// enum ScienceError { +/// #[error("recursion limit exceeded")] +/// RecursionLimitExceeded, +/// # #[error("...")] +/// # More = (stringify! { +/// ... +/// # }, 1).1, +/// } +/// +/// # fn main() -> Result<()> { +/// # let depth = 0; +/// # +/// if depth > MAX_DEPTH { +/// bail!(ScienceError::RecursionLimitExceeded); +/// } +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! bail { + ($msg:literal $(,)?) => { + return $crate::private::Err($crate::anyhow!($msg)); + }; + ($err:expr $(,)?) => { + return $crate::private::Err($crate::anyhow!($err)); + }; + ($fmt:expr, $($arg:tt)*) => { + return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + }; +} + +/// Return early with an error if a condition is not satisfied. +/// +/// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`. +/// +/// Analogously to `assert!`, `ensure!` takes a condition and exits the function +/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error` +/// rather than panicking. +/// +/// # Example +/// +/// ``` +/// # use anyhow::{ensure, Result}; +/// # +/// # fn main() -> Result<()> { +/// # let user = 0; +/// # +/// ensure!(user == 0, "only user 0 is allowed"); +/// # Ok(()) +/// # } +/// ``` +/// +/// ``` +/// # use anyhow::{ensure, Result}; +/// # use thiserror::Error; +/// # +/// # const MAX_DEPTH: usize = 1; +/// # +/// #[derive(Error, Debug)] +/// enum ScienceError { +/// #[error("recursion limit exceeded")] +/// RecursionLimitExceeded, +/// # #[error("...")] +/// # More = (stringify! { +/// ... +/// # }, 1).1, +/// } +/// +/// # fn main() -> Result<()> { +/// # let depth = 0; +/// # +/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded); +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! ensure { + ($cond:expr, $msg:literal $(,)?) => { + if !$cond { + return $crate::private::Err($crate::anyhow!($msg)); + } + }; + ($cond:expr, $err:expr $(,)?) => { + if !$cond { + return $crate::private::Err($crate::anyhow!($err)); + } + }; + ($cond:expr, $fmt:expr, $($arg:tt)*) => { + if !$cond { + return $crate::private::Err($crate::anyhow!($fmt, $($arg)*)); + } + }; +} + +/// Construct an ad-hoc error from a string. +/// +/// This evaluates to an `Error`. It can take either just a string, or a format +/// string with arguments. It also can take any custom type which implements +/// `Debug` and `Display`. +/// +/// # Example +/// +/// ``` +/// # type V = (); +/// # +/// use anyhow::{anyhow, Result}; +/// +/// fn lookup(key: &str) -> Result<V> { +/// if key.len() != 16 { +/// return Err(anyhow!("key length must be 16 characters, got {:?}", key)); +/// } +/// +/// // ... +/// # Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! anyhow { + ($msg:literal $(,)?) => { + // Handle $:literal as a special case to make cargo-expanded code more + // concise in the common case. + $crate::private::new_adhoc($msg) + }; + ($err:expr $(,)?) => ({ + use $crate::private::kind::*; + let error = $err; + (&error).anyhow_kind().new(error) + }); + ($fmt:expr, $($arg:tt)*) => { + $crate::private::new_adhoc(format!($fmt, $($arg)*)) + }; +}
diff --git a/tests/simple_deps/vendor/anyhow/src/wrapper.rs b/tests/simple_deps/vendor/anyhow/src/wrapper.rs new file mode 100644 index 0000000..3ebe51a --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/src/wrapper.rs
@@ -0,0 +1,78 @@ +use crate::StdError; +use core::fmt::{self, Debug, Display}; + +#[repr(transparent)] +pub struct MessageError<M>(pub M); + +impl<M> Debug for MessageError<M> +where + M: Display + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl<M> Display for MessageError<M> +where + M: Display + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {} + +#[repr(transparent)] +pub struct DisplayError<M>(pub M); + +impl<M> Debug for DisplayError<M> +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> Display for DisplayError<M> +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl<M> StdError for DisplayError<M> where M: Display + 'static {} + +#[cfg(feature = "std")] +#[repr(transparent)] +pub struct BoxedError(pub Box<dyn StdError + Send + Sync>); + +#[cfg(feature = "std")] +impl Debug for BoxedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +#[cfg(feature = "std")] +impl Display for BoxedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +#[cfg(feature = "std")] +impl StdError for BoxedError { + #[cfg(backtrace)] + fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> { + self.0.backtrace() + } + + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.0.source() + } +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/common/mod.rs b/tests/simple_deps/vendor/anyhow/tests/common/mod.rs new file mode 100644 index 0000000..fc165a5 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/common/mod.rs
@@ -0,0 +1,14 @@ +use anyhow::{bail, Result}; +use std::io; + +pub fn bail_literal() -> Result<()> { + bail!("oh no!"); +} + +pub fn bail_fmt() -> Result<()> { + bail!("{} {}!", "oh", "no"); +} + +pub fn bail_error() -> Result<()> { + bail!(io::Error::new(io::ErrorKind::Other, "oh no!")); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/compiletest.rs b/tests/simple_deps/vendor/anyhow/tests/compiletest.rs new file mode 100644 index 0000000..f9aea23 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/compiletest.rs
@@ -0,0 +1,6 @@ +#[rustversion::attr(not(nightly), ignore)] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/drop/mod.rs b/tests/simple_deps/vendor/anyhow/tests/drop/mod.rs new file mode 100644 index 0000000..1204011 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/drop/mod.rs
@@ -0,0 +1,46 @@ +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::Arc; + +#[derive(Debug)] +pub struct Flag { + atomic: Arc<AtomicBool>, +} + +impl Flag { + pub fn new() -> Self { + Flag { atomic: Arc::new(AtomicBool::new(false)) } + } + + pub fn get(&self) -> bool { + self.atomic.load(SeqCst) + } +} + +#[derive(Debug)] +pub struct DetectDrop { + has_dropped: Flag, +} + +impl DetectDrop { + pub fn new(has_dropped: &Flag) -> Self { + DetectDrop { has_dropped: Flag { atomic: Arc::clone(&has_dropped.atomic) } } + } +} + +impl StdError for DetectDrop {} + +impl Display for DetectDrop { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "oh no!") + } +} + +impl Drop for DetectDrop { + fn drop(&mut self) { + let already_dropped = self.has_dropped.atomic.swap(true, SeqCst); + assert!(!already_dropped); + } +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_autotrait.rs b/tests/simple_deps/vendor/anyhow/tests/test_autotrait.rs new file mode 100644 index 0000000..0c9326d --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_autotrait.rs
@@ -0,0 +1,13 @@ +use anyhow::Error; + +#[test] +fn test_send() { + fn assert_send<T: Send>() {} + assert_send::<Error>(); +} + +#[test] +fn test_sync() { + fn assert_sync<T: Sync>() {} + assert_sync::<Error>(); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_backtrace.rs b/tests/simple_deps/vendor/anyhow/tests/test_backtrace.rs new file mode 100644 index 0000000..ce385f5 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_backtrace.rs
@@ -0,0 +1,13 @@ +#[rustversion::not(nightly)] +#[ignore] +#[test] +fn test_backtrace() {} + +#[rustversion::nightly] +#[test] +fn test_backtrace() { + use anyhow::anyhow; + + let error = anyhow!("oh no!"); + let _ = error.backtrace(); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_boxed.rs b/tests/simple_deps/vendor/anyhow/tests/test_boxed.rs new file mode 100644 index 0000000..851dcbb --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_boxed.rs
@@ -0,0 +1,35 @@ +use anyhow::anyhow; +use std::error::Error as StdError; +use std::io; +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("outer")] +struct MyError { + source: io::Error, +} + +#[test] +fn test_boxed_str() { + let error = Box::<dyn StdError + Send + Sync>::from("oh no!"); + let error = anyhow!(error); + assert_eq!("oh no!", error.to_string()); + assert_eq!( + "oh no!", + error.downcast_ref::<Box<dyn StdError + Send + Sync>>().unwrap().to_string() + ); +} + +#[test] +fn test_boxed_thiserror() { + let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!") }; + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} + +#[test] +fn test_boxed_anyhow() { + let error = anyhow!("oh no!").context("it failed"); + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_chain.rs b/tests/simple_deps/vendor/anyhow/tests/test_chain.rs new file mode 100644 index 0000000..b1c5a3d --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_chain.rs
@@ -0,0 +1,45 @@ +use anyhow::{anyhow, Error}; + +fn error() -> Error { + anyhow!(0).context(1).context(2).context(3) +} + +#[test] +fn test_iter() { + let e = error(); + let mut chain = e.chain(); + assert_eq!("3", chain.next().unwrap().to_string()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!("1", chain.next().unwrap().to_string()); + assert_eq!("0", chain.next().unwrap().to_string()); + assert!(chain.next().is_none()); + assert!(chain.next_back().is_none()); +} + +#[test] +fn test_rev() { + let e = error(); + let mut chain = e.chain().rev(); + assert_eq!("0", chain.next().unwrap().to_string()); + assert_eq!("1", chain.next().unwrap().to_string()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!("3", chain.next().unwrap().to_string()); + assert!(chain.next().is_none()); + assert!(chain.next_back().is_none()); +} + +#[test] +fn test_len() { + let e = error(); + let mut chain = e.chain(); + assert_eq!(4, chain.len()); + assert_eq!("3", chain.next().unwrap().to_string()); + assert_eq!(3, chain.len()); + assert_eq!("0", chain.next_back().unwrap().to_string()); + assert_eq!(2, chain.len()); + assert_eq!("2", chain.next().unwrap().to_string()); + assert_eq!(1, chain.len()); + assert_eq!("1", chain.next_back().unwrap().to_string()); + assert_eq!(0, chain.len()); + assert!(chain.next().is_none()); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_context.rs b/tests/simple_deps/vendor/anyhow/tests/test_context.rs new file mode 100644 index 0000000..4bd1ffe --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_context.rs
@@ -0,0 +1,150 @@ +mod drop; + +use crate::drop::{DetectDrop, Flag}; +use anyhow::{Context, Error, Result}; +use std::fmt::{self, Display}; +use thiserror::Error; + +// https://github.com/dtolnay/anyhow/issues/18 +#[test] +fn test_inference() -> Result<()> { + let x = "1"; + let y: u32 = x.parse().context("...")?; + assert_eq!(y, 1); + Ok(()) +} + +macro_rules! context_type { + ($name:ident) => { + #[derive(Debug)] + struct $name { + message: &'static str, + drop: DetectDrop, + } + + impl Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.message) + } + } + }; +} + +context_type!(HighLevel); +context_type!(MidLevel); + +#[derive(Error, Debug)] +#[error("{message}")] +struct LowLevel { + message: &'static str, + drop: DetectDrop, +} + +struct Dropped { + low: Flag, + mid: Flag, + high: Flag, +} + +impl Dropped { + fn none(&self) -> bool { + !self.low.get() && !self.mid.get() && !self.high.get() + } + + fn all(&self) -> bool { + self.low.get() && self.mid.get() && self.high.get() + } +} + +fn make_chain() -> (Error, Dropped) { + let dropped = Dropped { low: Flag::new(), mid: Flag::new(), high: Flag::new() }; + + let low = + LowLevel { message: "no such file or directory", drop: DetectDrop::new(&dropped.low) }; + + // impl Context for Result<T, E> + let mid = Err::<(), LowLevel>(low) + .context(MidLevel { message: "failed to load config", drop: DetectDrop::new(&dropped.mid) }) + .unwrap_err(); + + // impl Context for Result<T, Error> + let high = Err::<(), Error>(mid) + .context(HighLevel { + message: "failed to start server", + drop: DetectDrop::new(&dropped.high), + }) + .unwrap_err(); + + (high, dropped) +} + +#[test] +fn test_downcast_ref() { + let (err, dropped) = make_chain(); + + assert!(!err.is::<String>()); + assert!(err.downcast_ref::<String>().is_none()); + + assert!(err.is::<HighLevel>()); + let high = err.downcast_ref::<HighLevel>().unwrap(); + assert_eq!(high.to_string(), "failed to start server"); + + assert!(err.is::<MidLevel>()); + let mid = err.downcast_ref::<MidLevel>().unwrap(); + assert_eq!(mid.to_string(), "failed to load config"); + + assert!(err.is::<LowLevel>()); + let low = err.downcast_ref::<LowLevel>().unwrap(); + assert_eq!(low.to_string(), "no such file or directory"); + + assert!(dropped.none()); + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_high() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<HighLevel>().unwrap(); + assert!(!dropped.high.get()); + assert!(dropped.low.get() && dropped.mid.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_mid() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<MidLevel>().unwrap(); + assert!(!dropped.mid.get()); + assert!(dropped.low.get() && dropped.high.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_downcast_low() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<LowLevel>().unwrap(); + assert!(!dropped.low.get()); + assert!(dropped.mid.get() && dropped.high.get()); + + drop(err); + assert!(dropped.all()); +} + +#[test] +fn test_unsuccessful_downcast() { + let (err, dropped) = make_chain(); + + let err = err.downcast::<String>().unwrap_err(); + assert!(dropped.none()); + + drop(err); + assert!(dropped.all()); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_convert.rs b/tests/simple_deps/vendor/anyhow/tests/test_convert.rs new file mode 100644 index 0000000..72da020 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_convert.rs
@@ -0,0 +1,24 @@ +mod drop; + +use self::drop::{DetectDrop, Flag}; +use anyhow::{Error, Result}; +use std::error::Error as StdError; + +#[test] +fn test_convert() { + let has_dropped = Flag::new(); + let error = Error::new(DetectDrop::new(&has_dropped)); + let box_dyn = Box::<dyn StdError + Send + Sync>::from(error); + assert_eq!("oh no!", box_dyn.to_string()); + drop(box_dyn); + assert!(has_dropped.get()); +} + +#[test] +fn test_question_mark() -> Result<(), Box<dyn StdError>> { + fn f() -> Result<()> { + Ok(()) + } + f()?; + Ok(()) +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_downcast.rs b/tests/simple_deps/vendor/anyhow/tests/test_downcast.rs new file mode 100644 index 0000000..c4393e8 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_downcast.rs
@@ -0,0 +1,70 @@ +mod common; +mod drop; + +use self::common::*; +use self::drop::{DetectDrop, Flag}; +use anyhow::Error; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +#[test] +fn test_downcast() { + assert_eq!("oh no!", bail_literal().unwrap_err().downcast::<&str>().unwrap(),); + assert_eq!("oh no!", bail_fmt().unwrap_err().downcast::<String>().unwrap(),); + assert_eq!("oh no!", bail_error().unwrap_err().downcast::<io::Error>().unwrap().to_string(),); +} + +#[test] +fn test_downcast_ref() { + assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(),); + assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_ref::<String>().unwrap(),); + assert_eq!( + "oh no!", + bail_error().unwrap_err().downcast_ref::<io::Error>().unwrap().to_string(), + ); +} + +#[test] +fn test_downcast_mut() { + assert_eq!("oh no!", *bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(),); + assert_eq!("oh no!", bail_fmt().unwrap_err().downcast_mut::<String>().unwrap(),); + assert_eq!( + "oh no!", + bail_error().unwrap_err().downcast_mut::<io::Error>().unwrap().to_string(), + ); +} + +#[test] +fn test_drop() { + let has_dropped = Flag::new(); + let error = Error::new(DetectDrop::new(&has_dropped)); + drop(error.downcast::<DetectDrop>().unwrap()); + assert!(has_dropped.get()); +} + +#[test] +fn test_large_alignment() { + #[repr(align(64))] + #[derive(Debug)] + struct LargeAlignedError(&'static str); + + impl Display for LargeAlignedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.0) + } + } + + impl StdError for LargeAlignedError {} + + let error = Error::new(LargeAlignedError("oh no!")); + assert_eq!("oh no!", error.downcast_ref::<LargeAlignedError>().unwrap().0); +} + +#[test] +fn test_unsuccessful_downcast() { + let mut error = bail_error().unwrap_err(); + assert!(error.downcast_ref::<&str>().is_none()); + assert!(error.downcast_mut::<&str>().is_none()); + assert!(error.downcast::<&str>().is_err()); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_fmt.rs b/tests/simple_deps/vendor/anyhow/tests/test_fmt.rs new file mode 100644 index 0000000..cc49291 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_fmt.rs
@@ -0,0 +1,94 @@ +use anyhow::{bail, Context, Result}; +use std::io; + +fn f() -> Result<()> { + bail!(io::Error::new(io::ErrorKind::PermissionDenied, "oh no!")); +} + +fn g() -> Result<()> { + f().context("f failed") +} + +fn h() -> Result<()> { + g().context("g failed") +} + +const EXPECTED_ALTDISPLAY_F: &str = "oh no!"; + +const EXPECTED_ALTDISPLAY_G: &str = "f failed: oh no!"; + +const EXPECTED_ALTDISPLAY_H: &str = "g failed: f failed: oh no!"; + +const EXPECTED_DEBUG_F: &str = "oh no!"; + +const EXPECTED_DEBUG_G: &str = "\ +f failed + +Caused by: + oh no!\ +"; + +const EXPECTED_DEBUG_H: &str = "\ +g failed + +Caused by: + 0: f failed + 1: oh no!\ +"; + +const EXPECTED_ALTDEBUG_F: &str = "\ +Custom { + kind: PermissionDenied, + error: \"oh no!\", +}\ +"; + +const EXPECTED_ALTDEBUG_G: &str = "\ +Error { + context: \"f failed\", + source: Custom { + kind: PermissionDenied, + error: \"oh no!\", + }, +}\ +"; + +const EXPECTED_ALTDEBUG_H: &str = "\ +Error { + context: \"g failed\", + source: Error { + context: \"f failed\", + source: Custom { + kind: PermissionDenied, + error: \"oh no!\", + }, + }, +}\ +"; + +#[test] +fn test_display() { + assert_eq!("g failed", h().unwrap_err().to_string()); +} + +#[test] +fn test_altdisplay() { + assert_eq!(EXPECTED_ALTDISPLAY_F, format!("{:#}", f().unwrap_err())); + assert_eq!(EXPECTED_ALTDISPLAY_G, format!("{:#}", g().unwrap_err())); + assert_eq!(EXPECTED_ALTDISPLAY_H, format!("{:#}", h().unwrap_err())); +} + +#[test] +#[cfg_attr(not(backtrace), ignore)] +fn test_debug() { + assert_eq!(EXPECTED_DEBUG_F, format!("{:?}", f().unwrap_err())); + assert_eq!(EXPECTED_DEBUG_G, format!("{:?}", g().unwrap_err())); + assert_eq!(EXPECTED_DEBUG_H, format!("{:?}", h().unwrap_err())); +} + +#[test] +fn test_altdebug() { + assert_eq!(EXPECTED_ALTDEBUG_F, format!("{:#?}", f().unwrap_err())); + assert_eq!(EXPECTED_ALTDEBUG_G, format!("{:#?}", g().unwrap_err())); + assert_eq!(EXPECTED_ALTDEBUG_H, format!("{:#?}", h().unwrap_err())); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_macros.rs b/tests/simple_deps/vendor/anyhow/tests/test_macros.rs new file mode 100644 index 0000000..c6888b6 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_macros.rs
@@ -0,0 +1,33 @@ +mod common; + +use self::common::*; +use anyhow::ensure; + +#[test] +fn test_messages() { + assert_eq!("oh no!", bail_literal().unwrap_err().to_string()); + assert_eq!("oh no!", bail_fmt().unwrap_err().to_string()); + assert_eq!("oh no!", bail_error().unwrap_err().to_string()); +} + +#[test] +fn test_ensure() { + let f = || { + ensure!(1 + 1 == 2, "This is correct"); + Ok(()) + }; + assert!(f().is_ok()); + + let v = 1; + let f = || { + ensure!(v + v == 2, "This is correct, v: {}", v); + Ok(()) + }; + assert!(f().is_ok()); + + let f = || { + ensure!(v + v == 1, "This is not correct, v: {}", v); + Ok(()) + }; + assert!(f().is_err()); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_repr.rs b/tests/simple_deps/vendor/anyhow/tests/test_repr.rs new file mode 100644 index 0000000..72f5002 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_repr.rs
@@ -0,0 +1,29 @@ +mod drop; + +use self::drop::{DetectDrop, Flag}; +use anyhow::Error; +use std::marker::Unpin; +use std::mem; + +#[test] +fn test_error_size() { + assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>()); +} + +#[test] +fn test_null_pointer_optimization() { + assert_eq!(mem::size_of::<Result<(), Error>>(), mem::size_of::<usize>()); +} + +#[test] +fn test_autotraits() { + fn assert<E: Unpin + Send + Sync + 'static>() {} + assert::<Error>(); +} + +#[test] +fn test_drop() { + let has_dropped = Flag::new(); + drop(Error::new(DetectDrop::new(&has_dropped))); + assert!(has_dropped.get()); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/test_source.rs b/tests/simple_deps/vendor/anyhow/tests/test_source.rs new file mode 100644 index 0000000..018267d --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/test_source.rs
@@ -0,0 +1,62 @@ +use anyhow::anyhow; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +#[derive(Debug)] +enum TestError { + Io(io::Error), +} + +impl Display for TestError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + TestError::Io(e) => Display::fmt(e, formatter), + } + } +} + +impl StdError for TestError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + TestError::Io(io) => Some(io), + } + } +} + +#[test] +fn test_literal_source() { + let error = anyhow!("oh no!"); + assert!(error.source().is_none()); +} + +#[test] +fn test_variable_source() { + let msg = "oh no!"; + let error = anyhow!(msg); + assert!(error.source().is_none()); + + let msg = msg.to_owned(); + let error = anyhow!(msg); + assert!(error.source().is_none()); +} + +#[test] +fn test_fmt_source() { + let error = anyhow!("{} {}!", "oh", "no"); + assert!(error.source().is_none()); +} + +#[test] +fn test_io_source() { + let io = io::Error::new(io::ErrorKind::Other, "oh no!"); + let error = anyhow!(TestError::Io(io)); + assert_eq!("oh no!", error.source().unwrap().to_string()); +} + +#[test] +fn test_anyhow_from_anyhow() { + let error = anyhow!("oh no!").context("context"); + let error = anyhow!(error); + assert_eq!("oh no!", error.source().unwrap().to_string()); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.rs b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.rs new file mode 100644 index 0000000..d2e89af --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.rs
@@ -0,0 +1,8 @@ +use anyhow::anyhow; + +#[derive(Debug)] +struct Error; + +fn main() { + let _ = anyhow!(Error); +}
diff --git a/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.stderr b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.stderr new file mode 100644 index 0000000..be95737 --- /dev/null +++ b/tests/simple_deps/vendor/anyhow/tests/ui/no-impl.stderr
@@ -0,0 +1,21 @@ +error[E0599]: no method named `anyhow_kind` found for reference `&Error` in the current scope + --> $DIR/no-impl.rs:7:13 + | +4 | struct Error; + | ------------- + | | + | doesn't satisfy `Error: anyhow::kind::TraitKind` + | doesn't satisfy `Error: std::convert::Into<anyhow::Error>` + | doesn't satisfy `Error: std::fmt::Display` +... +7 | let _ = anyhow!(Error); + | ^^^^^^^^^^^^^^ method not found in `&Error` + | + = note: the method `anyhow_kind` exists but the following trait bounds were not satisfied: + `Error: std::convert::Into<anyhow::Error>` + which is required by `Error: anyhow::kind::TraitKind` + `Error: std::fmt::Display` + which is required by `&Error: anyhow::kind::AdhocKind` + `&Error: std::convert::Into<anyhow::Error>` + which is required by `&Error: anyhow::kind::TraitKind` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/target_renaming/BUILD.gn b/tests/target_renaming/BUILD.gn new file mode 100644 index 0000000..fb498c9 --- /dev/null +++ b/tests/target_renaming/BUILD.gn
@@ -0,0 +1,37 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +import("//example.gni") + +renamed_group("example_lib") { + public_deps = [ ":example_lib-v1_0_0" ] +} + +renamed_rule("example_lib-v1_0_0") { + crate_name = "example_lib" + crate_root = "//target_renaming/example_lib/src/lib.rs" + output_name = "example_lib-4b9f22a13a4b11e0" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=4b9f22a13a4b11e0", + "-Cextra-filename=-4b9f22a13a4b11e0", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/target_renaming/Cargo.lock b/tests/target_renaming/Cargo.lock new file mode 100644 index 0000000..20221b9 --- /dev/null +++ b/tests/target_renaming/Cargo.lock
@@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "example_lib" +version = "1.0.0" + +[[package]] +name = "visibility_test" +version = "0.1.0" +dependencies = [ + "example_lib", +]
diff --git a/tests/target_renaming/Cargo.toml b/tests/target_renaming/Cargo.toml new file mode 100644 index 0000000..3b90cad --- /dev/null +++ b/tests/target_renaming/Cargo.toml
@@ -0,0 +1,15 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "visibility_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +example_lib = { version = "1.0", path = "example_lib" } + +# gn config +[gn.package.example_lib."1.0.0"] +target_renaming = { import = "//example.gni", group_name = "renamed_group", rule_name = "renamed_rule" }
diff --git a/tests/target_renaming/example_lib/Cargo.toml b/tests/target_renaming/example_lib/Cargo.toml new file mode 100644 index 0000000..12a5af3 --- /dev/null +++ b/tests/target_renaming/example_lib/Cargo.toml
@@ -0,0 +1,4 @@ +[package] +name = "example_lib" +version = "1.0.0" +edition = "2021"
diff --git a/tests/target_renaming/example_lib/src/lib.rs b/tests/target_renaming/example_lib/src/lib.rs new file mode 100644 index 0000000..70a39d4 --- /dev/null +++ b/tests/target_renaming/example_lib/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/target_renaming/src/lib.rs b/tests/target_renaming/src/lib.rs new file mode 100644 index 0000000..70a39d4 --- /dev/null +++ b/tests/target_renaming/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/testonly/BUILD.gn b/tests/testonly/BUILD.gn new file mode 100644 index 0000000..696d03b --- /dev/null +++ b/tests/testonly/BUILD.gn
@@ -0,0 +1,37 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +group("example_lib") { + public_deps = [ ":example_lib-v1_0_0" ] + testonly = true +} + +rust_library("example_lib-v1_0_0") { + crate_name = "example_lib" + crate_root = "//testonly/example_lib/src/lib.rs" + output_name = "example_lib-4b9f22a13a4b11e0" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=4b9f22a13a4b11e0", + "-Cextra-filename=-4b9f22a13a4b11e0", + ] + + visibility = [ ":*" ] + + testonly = true + applicable_licenses = [] +}
diff --git a/tests/testonly/Cargo.lock b/tests/testonly/Cargo.lock new file mode 100644 index 0000000..17c74bd --- /dev/null +++ b/tests/testonly/Cargo.lock
@@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "example_lib" +version = "1.0.0" + +[[package]] +name = "testonly_test" +version = "0.1.0" +dependencies = [ + "example_lib", +]
diff --git a/tests/testonly/Cargo.toml b/tests/testonly/Cargo.toml new file mode 100644 index 0000000..fb7ef95 --- /dev/null +++ b/tests/testonly/Cargo.toml
@@ -0,0 +1,15 @@ +# Copyright 2023 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "testonly_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +example_lib = { version = "1.0", path = "example_lib" } + +# gn config +[gn.package.example_lib."1.0.0"] +testonly = true
diff --git a/tests/testonly/example_lib/Cargo.lock b/tests/testonly/example_lib/Cargo.lock new file mode 100644 index 0000000..c9d7300 --- /dev/null +++ b/tests/testonly/example_lib/Cargo.lock
@@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "example_lib" +version = "1.0.0"
diff --git a/tests/testonly/example_lib/Cargo.toml b/tests/testonly/example_lib/Cargo.toml new file mode 100644 index 0000000..12a5af3 --- /dev/null +++ b/tests/testonly/example_lib/Cargo.toml
@@ -0,0 +1,4 @@ +[package] +name = "example_lib" +version = "1.0.0" +edition = "2021"
diff --git a/tests/testonly/example_lib/src/lib.rs b/tests/testonly/example_lib/src/lib.rs new file mode 100644 index 0000000..b945863 --- /dev/null +++ b/tests/testonly/example_lib/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2023 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/testonly/src/lib.rs b/tests/testonly/src/lib.rs new file mode 100644 index 0000000..b945863 --- /dev/null +++ b/tests/testonly/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2023 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/visibility/BUILD.gn b/tests/visibility/BUILD.gn new file mode 100644 index 0000000..4e4db9b --- /dev/null +++ b/tests/visibility/BUILD.gn
@@ -0,0 +1,39 @@ +# Copyright 2020 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# DO NOT EDIT. THIS FILE IS GENERATED BY CARGO GNAW. EDIT THE CARGO TOML FILE + +import("//build/licenses/license.gni") + +gn_source_root = rebase_path("//", root_build_dir) +not_needed([ "gn_source_root" ]) + +import("//example.gni") + +group("example_lib") { + public_deps = [ ":example_lib-v1_0_0" ] + + visibility = example_visibility_var +} + +rust_library("example_lib-v1_0_0") { + crate_name = "example_lib" + crate_root = "//visibility/example_lib/src/lib.rs" + output_name = "example_lib-4b9f22a13a4b11e0" + + deps = [] + + rustenv = [] + + rustflags = [ + "--cap-lints=allow", + "--edition=2021", + "-Cmetadata=4b9f22a13a4b11e0", + "-Cextra-filename=-4b9f22a13a4b11e0", + ] + + visibility = [ ":*" ] + + applicable_licenses = [] +}
diff --git a/tests/visibility/Cargo.lock b/tests/visibility/Cargo.lock new file mode 100644 index 0000000..20221b9 --- /dev/null +++ b/tests/visibility/Cargo.lock
@@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "example_lib" +version = "1.0.0" + +[[package]] +name = "visibility_test" +version = "0.1.0" +dependencies = [ + "example_lib", +]
diff --git a/tests/visibility/Cargo.toml b/tests/visibility/Cargo.toml new file mode 100644 index 0000000..d15e6e6 --- /dev/null +++ b/tests/visibility/Cargo.toml
@@ -0,0 +1,15 @@ +# Copyright 2022 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[package] +name = "visibility_test" +version = "0.1.0" +edition = "2018" + +[dependencies] +example_lib = { version = "1.0", path = "example_lib" } + +# gn config +[gn.package.example_lib."1.0.0"] +group_visibility = { import = "//example.gni", variable = "example_visibility_var" }
diff --git a/tests/visibility/example_lib/Cargo.toml b/tests/visibility/example_lib/Cargo.toml new file mode 100644 index 0000000..12a5af3 --- /dev/null +++ b/tests/visibility/example_lib/Cargo.toml
@@ -0,0 +1,4 @@ +[package] +name = "example_lib" +version = "1.0.0" +edition = "2021"
diff --git a/tests/visibility/example_lib/src/lib.rs b/tests/visibility/example_lib/src/lib.rs new file mode 100644 index 0000000..70a39d4 --- /dev/null +++ b/tests/visibility/example_lib/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)
diff --git a/tests/visibility/src/lib.rs b/tests/visibility/src/lib.rs new file mode 100644 index 0000000..70a39d4 --- /dev/null +++ b/tests/visibility/src/lib.rs
@@ -0,0 +1,5 @@ +// Copyright 2022 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Library intentionally left empty :)