xref: /aosp_15_r20/external/bazelbuild-rules_rust/crate_universe/src/config.rs (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1*d4726bddSHONG Yifan //! A module for configuration information
2*d4726bddSHONG Yifan 
3*d4726bddSHONG Yifan use std::cmp::Ordering;
4*d4726bddSHONG Yifan use std::collections::{BTreeMap, BTreeSet};
5*d4726bddSHONG Yifan use std::fmt::Formatter;
6*d4726bddSHONG Yifan use std::iter::Sum;
7*d4726bddSHONG Yifan use std::ops::Add;
8*d4726bddSHONG Yifan use std::path::Path;
9*d4726bddSHONG Yifan use std::str::FromStr;
10*d4726bddSHONG Yifan use std::{fmt, fs};
11*d4726bddSHONG Yifan 
12*d4726bddSHONG Yifan use anyhow::{Context, Result};
13*d4726bddSHONG Yifan use cargo_lock::package::GitReference;
14*d4726bddSHONG Yifan use cargo_metadata::Package;
15*d4726bddSHONG Yifan use semver::VersionReq;
16*d4726bddSHONG Yifan use serde::de::value::SeqAccessDeserializer;
17*d4726bddSHONG Yifan use serde::de::{Deserializer, SeqAccess, Unexpected, Visitor};
18*d4726bddSHONG Yifan use serde::{Deserialize, Serialize, Serializer};
19*d4726bddSHONG Yifan 
20*d4726bddSHONG Yifan use crate::select::{Select, Selectable};
21*d4726bddSHONG Yifan use crate::utils::starlark::Label;
22*d4726bddSHONG Yifan use crate::utils::target_triple::TargetTriple;
23*d4726bddSHONG Yifan 
24*d4726bddSHONG Yifan /// Representations of different kinds of crate vendoring into workspaces.
25*d4726bddSHONG Yifan #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
26*d4726bddSHONG Yifan #[serde(rename_all = "lowercase")]
27*d4726bddSHONG Yifan pub(crate) enum VendorMode {
28*d4726bddSHONG Yifan     /// Crates having full source being vendored into a workspace
29*d4726bddSHONG Yifan     Local,
30*d4726bddSHONG Yifan 
31*d4726bddSHONG Yifan     /// Crates having only BUILD files with repository rules vendored into a workspace
32*d4726bddSHONG Yifan     Remote,
33*d4726bddSHONG Yifan }
34*d4726bddSHONG Yifan 
35*d4726bddSHONG Yifan impl std::fmt::Display for VendorMode {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result36*d4726bddSHONG Yifan     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37*d4726bddSHONG Yifan         fmt::Display::fmt(
38*d4726bddSHONG Yifan             match self {
39*d4726bddSHONG Yifan                 VendorMode::Local => "local",
40*d4726bddSHONG Yifan                 VendorMode::Remote => "remote",
41*d4726bddSHONG Yifan             },
42*d4726bddSHONG Yifan             f,
43*d4726bddSHONG Yifan         )
44*d4726bddSHONG Yifan     }
45*d4726bddSHONG Yifan }
46*d4726bddSHONG Yifan 
47*d4726bddSHONG Yifan #[derive(Debug, Serialize, Deserialize, Clone)]
48*d4726bddSHONG Yifan #[serde(deny_unknown_fields)]
49*d4726bddSHONG Yifan pub(crate) struct RenderConfig {
50*d4726bddSHONG Yifan     /// The name of the repository being rendered
51*d4726bddSHONG Yifan     pub(crate) repository_name: String,
52*d4726bddSHONG Yifan 
53*d4726bddSHONG Yifan     /// The pattern to use for BUILD file names.
54*d4726bddSHONG Yifan     /// Eg. `//:BUILD.{name}-{version}.bazel`
55*d4726bddSHONG Yifan     #[serde(default = "default_build_file_template")]
56*d4726bddSHONG Yifan     pub(crate) build_file_template: String,
57*d4726bddSHONG Yifan 
58*d4726bddSHONG Yifan     /// The pattern to use for a crate target.
59*d4726bddSHONG Yifan     /// Eg. `@{repository}__{name}-{version}//:{target}`
60*d4726bddSHONG Yifan     #[serde(default = "default_crate_label_template")]
61*d4726bddSHONG Yifan     pub(crate) crate_label_template: String,
62*d4726bddSHONG Yifan 
63*d4726bddSHONG Yifan     /// The pattern to use for the `defs.bzl` and `BUILD.bazel`
64*d4726bddSHONG Yifan     /// file names used for the crates module.
65*d4726bddSHONG Yifan     /// Eg. `//:{file}`
66*d4726bddSHONG Yifan     #[serde(default = "default_crates_module_template")]
67*d4726bddSHONG Yifan     pub(crate) crates_module_template: String,
68*d4726bddSHONG Yifan 
69*d4726bddSHONG Yifan     /// The pattern used for a crate's repository name.
70*d4726bddSHONG Yifan     /// Eg. `{repository}__{name}-{version}`
71*d4726bddSHONG Yifan     #[serde(default = "default_crate_repository_template")]
72*d4726bddSHONG Yifan     pub(crate) crate_repository_template: String,
73*d4726bddSHONG Yifan 
74*d4726bddSHONG Yifan     /// Default alias rule to use for packages.  Can be overridden by annotations.
75*d4726bddSHONG Yifan     #[serde(default)]
76*d4726bddSHONG Yifan     pub(crate) default_alias_rule: AliasRule,
77*d4726bddSHONG Yifan 
78*d4726bddSHONG Yifan     /// The default of the `package_name` parameter to use for the module macros like `all_crate_deps`.
79*d4726bddSHONG Yifan     /// In general, this should be be unset to allow the macros to do auto-detection in the analysis phase.
80*d4726bddSHONG Yifan     pub(crate) default_package_name: Option<String>,
81*d4726bddSHONG Yifan 
82*d4726bddSHONG Yifan     /// Whether to generate `target_compatible_with` annotations on the generated BUILD files.  This
83*d4726bddSHONG Yifan     /// catches a `target_triple`being targeted that isn't declared in `supported_platform_triples`.
84*d4726bddSHONG Yifan     #[serde(default = "default_generate_target_compatible_with")]
85*d4726bddSHONG Yifan     pub(crate) generate_target_compatible_with: bool,
86*d4726bddSHONG Yifan 
87*d4726bddSHONG Yifan     /// The pattern to use for platform constraints.
88*d4726bddSHONG Yifan     /// Eg. `@rules_rust//rust/platform:{triple}`.
89*d4726bddSHONG Yifan     #[serde(default = "default_platforms_template")]
90*d4726bddSHONG Yifan     pub(crate) platforms_template: String,
91*d4726bddSHONG Yifan 
92*d4726bddSHONG Yifan     /// The command to use for regenerating generated files.
93*d4726bddSHONG Yifan     pub(crate) regen_command: String,
94*d4726bddSHONG Yifan 
95*d4726bddSHONG Yifan     /// An optional configuration for rendering content to be rendered into repositories.
96*d4726bddSHONG Yifan     pub(crate) vendor_mode: Option<VendorMode>,
97*d4726bddSHONG Yifan 
98*d4726bddSHONG Yifan     /// Whether to generate package metadata
99*d4726bddSHONG Yifan     #[serde(default = "default_generate_rules_license_metadata")]
100*d4726bddSHONG Yifan     pub(crate) generate_rules_license_metadata: bool,
101*d4726bddSHONG Yifan }
102*d4726bddSHONG Yifan 
103*d4726bddSHONG Yifan // Default is manually implemented so that the default values match the default
104*d4726bddSHONG Yifan // values when deserializing, which involves calling the vairous `default_x()`
105*d4726bddSHONG Yifan // functions specified in `#[serde(default = "default_x")]`.
106*d4726bddSHONG Yifan impl Default for RenderConfig {
default() -> Self107*d4726bddSHONG Yifan     fn default() -> Self {
108*d4726bddSHONG Yifan         RenderConfig {
109*d4726bddSHONG Yifan             repository_name: String::default(),
110*d4726bddSHONG Yifan             build_file_template: default_build_file_template(),
111*d4726bddSHONG Yifan             crate_label_template: default_crate_label_template(),
112*d4726bddSHONG Yifan             crates_module_template: default_crates_module_template(),
113*d4726bddSHONG Yifan             crate_repository_template: default_crate_repository_template(),
114*d4726bddSHONG Yifan             default_alias_rule: AliasRule::default(),
115*d4726bddSHONG Yifan             default_package_name: Option::default(),
116*d4726bddSHONG Yifan             generate_target_compatible_with: default_generate_target_compatible_with(),
117*d4726bddSHONG Yifan             platforms_template: default_platforms_template(),
118*d4726bddSHONG Yifan             regen_command: String::default(),
119*d4726bddSHONG Yifan             vendor_mode: Option::default(),
120*d4726bddSHONG Yifan             generate_rules_license_metadata: default_generate_rules_license_metadata(),
121*d4726bddSHONG Yifan         }
122*d4726bddSHONG Yifan     }
123*d4726bddSHONG Yifan }
124*d4726bddSHONG Yifan 
125*d4726bddSHONG Yifan impl RenderConfig {
are_sources_present(&self) -> bool126*d4726bddSHONG Yifan     pub(crate) fn are_sources_present(&self) -> bool {
127*d4726bddSHONG Yifan         self.vendor_mode == Some(VendorMode::Local)
128*d4726bddSHONG Yifan     }
129*d4726bddSHONG Yifan }
130*d4726bddSHONG Yifan 
default_build_file_template() -> String131*d4726bddSHONG Yifan fn default_build_file_template() -> String {
132*d4726bddSHONG Yifan     "//:BUILD.{name}-{version}.bazel".to_owned()
133*d4726bddSHONG Yifan }
134*d4726bddSHONG Yifan 
default_crates_module_template() -> String135*d4726bddSHONG Yifan fn default_crates_module_template() -> String {
136*d4726bddSHONG Yifan     "//:{file}".to_owned()
137*d4726bddSHONG Yifan }
138*d4726bddSHONG Yifan 
default_crate_label_template() -> String139*d4726bddSHONG Yifan fn default_crate_label_template() -> String {
140*d4726bddSHONG Yifan     "@{repository}__{name}-{version}//:{target}".to_owned()
141*d4726bddSHONG Yifan }
142*d4726bddSHONG Yifan 
default_crate_repository_template() -> String143*d4726bddSHONG Yifan fn default_crate_repository_template() -> String {
144*d4726bddSHONG Yifan     "{repository}__{name}-{version}".to_owned()
145*d4726bddSHONG Yifan }
146*d4726bddSHONG Yifan 
default_platforms_template() -> String147*d4726bddSHONG Yifan fn default_platforms_template() -> String {
148*d4726bddSHONG Yifan     "@rules_rust//rust/platform:{triple}".to_owned()
149*d4726bddSHONG Yifan }
150*d4726bddSHONG Yifan 
default_generate_target_compatible_with() -> bool151*d4726bddSHONG Yifan fn default_generate_target_compatible_with() -> bool {
152*d4726bddSHONG Yifan     true
153*d4726bddSHONG Yifan }
154*d4726bddSHONG Yifan 
default_generate_rules_license_metadata() -> bool155*d4726bddSHONG Yifan fn default_generate_rules_license_metadata() -> bool {
156*d4726bddSHONG Yifan     false
157*d4726bddSHONG Yifan }
158*d4726bddSHONG Yifan 
159*d4726bddSHONG Yifan /// A representation of some Git identifier used to represent the "revision" or "pin" of a checkout.
160*d4726bddSHONG Yifan #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
161*d4726bddSHONG Yifan pub(crate) enum Commitish {
162*d4726bddSHONG Yifan     /// From a tag.
163*d4726bddSHONG Yifan     Tag(String),
164*d4726bddSHONG Yifan 
165*d4726bddSHONG Yifan     /// From the HEAD of a branch.
166*d4726bddSHONG Yifan     Branch(String),
167*d4726bddSHONG Yifan 
168*d4726bddSHONG Yifan     /// From a specific revision.
169*d4726bddSHONG Yifan     Rev(String),
170*d4726bddSHONG Yifan }
171*d4726bddSHONG Yifan 
172*d4726bddSHONG Yifan impl From<GitReference> for Commitish {
from(git_ref: GitReference) -> Self173*d4726bddSHONG Yifan     fn from(git_ref: GitReference) -> Self {
174*d4726bddSHONG Yifan         match git_ref {
175*d4726bddSHONG Yifan             GitReference::Tag(v) => Self::Tag(v),
176*d4726bddSHONG Yifan             GitReference::Branch(v) => Self::Branch(v),
177*d4726bddSHONG Yifan             GitReference::Rev(v) => Self::Rev(v),
178*d4726bddSHONG Yifan         }
179*d4726bddSHONG Yifan     }
180*d4726bddSHONG Yifan }
181*d4726bddSHONG Yifan 
182*d4726bddSHONG Yifan #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
183*d4726bddSHONG Yifan pub(crate) enum AliasRule {
184*d4726bddSHONG Yifan     #[default]
185*d4726bddSHONG Yifan     #[serde(rename = "alias")]
186*d4726bddSHONG Yifan     Alias,
187*d4726bddSHONG Yifan     #[serde(rename = "dbg")]
188*d4726bddSHONG Yifan     Dbg,
189*d4726bddSHONG Yifan     #[serde(rename = "fastbuild")]
190*d4726bddSHONG Yifan     Fastbuild,
191*d4726bddSHONG Yifan     #[serde(rename = "opt")]
192*d4726bddSHONG Yifan     Opt,
193*d4726bddSHONG Yifan     #[serde(untagged)]
194*d4726bddSHONG Yifan     Custom { bzl: String, rule: String },
195*d4726bddSHONG Yifan }
196*d4726bddSHONG Yifan 
197*d4726bddSHONG Yifan impl AliasRule {
bzl(&self) -> Option<String>198*d4726bddSHONG Yifan     pub(crate) fn bzl(&self) -> Option<String> {
199*d4726bddSHONG Yifan         match self {
200*d4726bddSHONG Yifan             AliasRule::Alias => None,
201*d4726bddSHONG Yifan             AliasRule::Dbg | AliasRule::Fastbuild | AliasRule::Opt => {
202*d4726bddSHONG Yifan                 Some("//:alias_rules.bzl".to_owned())
203*d4726bddSHONG Yifan             }
204*d4726bddSHONG Yifan             AliasRule::Custom { bzl, .. } => Some(bzl.clone()),
205*d4726bddSHONG Yifan         }
206*d4726bddSHONG Yifan     }
207*d4726bddSHONG Yifan 
rule(&self) -> String208*d4726bddSHONG Yifan     pub(crate) fn rule(&self) -> String {
209*d4726bddSHONG Yifan         match self {
210*d4726bddSHONG Yifan             AliasRule::Alias => "alias".to_owned(),
211*d4726bddSHONG Yifan             AliasRule::Dbg => "transition_alias_dbg".to_owned(),
212*d4726bddSHONG Yifan             AliasRule::Fastbuild => "transition_alias_fastbuild".to_owned(),
213*d4726bddSHONG Yifan             AliasRule::Opt => "transition_alias_opt".to_owned(),
214*d4726bddSHONG Yifan             AliasRule::Custom { rule, .. } => rule.clone(),
215*d4726bddSHONG Yifan         }
216*d4726bddSHONG Yifan     }
217*d4726bddSHONG Yifan }
218*d4726bddSHONG Yifan 
219*d4726bddSHONG Yifan #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
220*d4726bddSHONG Yifan pub(crate) struct CrateAnnotations {
221*d4726bddSHONG Yifan     /// Which subset of the crate's bins should get produced as `rust_binary` targets.
222*d4726bddSHONG Yifan     pub(crate) gen_binaries: Option<GenBinaries>,
223*d4726bddSHONG Yifan 
224*d4726bddSHONG Yifan     /// Determins whether or not Cargo build scripts should be generated for the current package
225*d4726bddSHONG Yifan     pub(crate) gen_build_script: Option<bool>,
226*d4726bddSHONG Yifan 
227*d4726bddSHONG Yifan     /// Additional data to pass to
228*d4726bddSHONG Yifan     /// [deps](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-deps) attribute.
229*d4726bddSHONG Yifan     pub(crate) deps: Option<Select<BTreeSet<Label>>>,
230*d4726bddSHONG Yifan 
231*d4726bddSHONG Yifan     /// Additional data to pass to
232*d4726bddSHONG Yifan     /// [proc_macro_deps](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-proc_macro_deps) attribute.
233*d4726bddSHONG Yifan     pub(crate) proc_macro_deps: Option<Select<BTreeSet<Label>>>,
234*d4726bddSHONG Yifan 
235*d4726bddSHONG Yifan     /// Additional data to pass to  the target's
236*d4726bddSHONG Yifan     /// [crate_features](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-crate_features) attribute.
237*d4726bddSHONG Yifan     pub(crate) crate_features: Option<Select<BTreeSet<String>>>,
238*d4726bddSHONG Yifan 
239*d4726bddSHONG Yifan     /// Additional data to pass to  the target's
240*d4726bddSHONG Yifan     /// [data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-data) attribute.
241*d4726bddSHONG Yifan     pub(crate) data: Option<Select<BTreeSet<Label>>>,
242*d4726bddSHONG Yifan 
243*d4726bddSHONG Yifan     /// An optional glob pattern to set on the
244*d4726bddSHONG Yifan     /// [data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-data) attribute.
245*d4726bddSHONG Yifan     pub(crate) data_glob: Option<BTreeSet<String>>,
246*d4726bddSHONG Yifan 
247*d4726bddSHONG Yifan     /// Additional data to pass to
248*d4726bddSHONG Yifan     /// [compile_data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-compile_data) attribute.
249*d4726bddSHONG Yifan     pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
250*d4726bddSHONG Yifan 
251*d4726bddSHONG Yifan     /// An optional glob pattern to set on the
252*d4726bddSHONG Yifan     /// [compile_data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-compile_data) attribute.
253*d4726bddSHONG Yifan     pub(crate) compile_data_glob: Option<BTreeSet<String>>,
254*d4726bddSHONG Yifan 
255*d4726bddSHONG Yifan     /// If true, disables pipelining for library targets generated for this crate.
256*d4726bddSHONG Yifan     pub(crate) disable_pipelining: bool,
257*d4726bddSHONG Yifan 
258*d4726bddSHONG Yifan     /// Additional data to pass to  the target's
259*d4726bddSHONG Yifan     /// [rustc_env](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_env) attribute.
260*d4726bddSHONG Yifan     pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
261*d4726bddSHONG Yifan 
262*d4726bddSHONG Yifan     /// Additional data to pass to  the target's
263*d4726bddSHONG Yifan     /// [rustc_env_files](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_env_files) attribute.
264*d4726bddSHONG Yifan     pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
265*d4726bddSHONG Yifan 
266*d4726bddSHONG Yifan     /// Additional data to pass to the target's
267*d4726bddSHONG Yifan     /// [rustc_flags](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_flags) attribute.
268*d4726bddSHONG Yifan     pub(crate) rustc_flags: Option<Select<Vec<String>>>,
269*d4726bddSHONG Yifan 
270*d4726bddSHONG Yifan     /// Additional dependencies to pass to a build script's
271*d4726bddSHONG Yifan     /// [deps](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-deps) attribute.
272*d4726bddSHONG Yifan     pub(crate) build_script_deps: Option<Select<BTreeSet<Label>>>,
273*d4726bddSHONG Yifan 
274*d4726bddSHONG Yifan     /// Additional data to pass to a build script's
275*d4726bddSHONG Yifan     /// [proc_macro_deps](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-proc_macro_deps) attribute.
276*d4726bddSHONG Yifan     pub(crate) build_script_proc_macro_deps: Option<Select<BTreeSet<Label>>>,
277*d4726bddSHONG Yifan 
278*d4726bddSHONG Yifan     /// Additional data to pass to a build script's
279*d4726bddSHONG Yifan     /// [build_script_data](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-data) attribute.
280*d4726bddSHONG Yifan     pub(crate) build_script_data: Option<Select<BTreeSet<Label>>>,
281*d4726bddSHONG Yifan 
282*d4726bddSHONG Yifan     /// Additional data to pass to a build script's
283*d4726bddSHONG Yifan     /// [tools](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-tools) attribute.
284*d4726bddSHONG Yifan     pub(crate) build_script_tools: Option<Select<BTreeSet<Label>>>,
285*d4726bddSHONG Yifan 
286*d4726bddSHONG Yifan     /// An optional glob pattern to set on the
287*d4726bddSHONG Yifan     /// [build_script_data](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-build_script_env) attribute.
288*d4726bddSHONG Yifan     pub(crate) build_script_data_glob: Option<BTreeSet<String>>,
289*d4726bddSHONG Yifan 
290*d4726bddSHONG Yifan     /// Additional environment variables to pass to a build script's
291*d4726bddSHONG Yifan     /// [build_script_env](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-rustc_env) attribute.
292*d4726bddSHONG Yifan     pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
293*d4726bddSHONG Yifan 
294*d4726bddSHONG Yifan     /// Additional rustc_env flags to pass to a build script's
295*d4726bddSHONG Yifan     /// [rustc_env](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-rustc_env) attribute.
296*d4726bddSHONG Yifan     pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
297*d4726bddSHONG Yifan 
298*d4726bddSHONG Yifan     /// Additional labels to pass to a build script's
299*d4726bddSHONG Yifan     /// [toolchains](https://bazel.build/reference/be/common-definitions#common-attributes) attribute.
300*d4726bddSHONG Yifan     pub(crate) build_script_toolchains: Option<BTreeSet<Label>>,
301*d4726bddSHONG Yifan 
302*d4726bddSHONG Yifan     /// Directory to run the crate's build script in. If not set, will run in the manifest directory, otherwise a directory relative to the exec root.
303*d4726bddSHONG Yifan     pub(crate) build_script_rundir: Option<Select<String>>,
304*d4726bddSHONG Yifan 
305*d4726bddSHONG Yifan     /// A scratch pad used to write arbitrary text to target BUILD files.
306*d4726bddSHONG Yifan     pub(crate) additive_build_file_content: Option<String>,
307*d4726bddSHONG Yifan 
308*d4726bddSHONG Yifan     /// For git sourced crates, this is a the
309*d4726bddSHONG Yifan     /// [git_repository::shallow_since](https://docs.bazel.build/versions/main/repo/git.html#new_git_repository-shallow_since) attribute.
310*d4726bddSHONG Yifan     pub(crate) shallow_since: Option<String>,
311*d4726bddSHONG Yifan 
312*d4726bddSHONG Yifan     /// The `patch_args` attribute of a Bazel repository rule. See
313*d4726bddSHONG Yifan     /// [http_archive.patch_args](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_args)
314*d4726bddSHONG Yifan     pub(crate) patch_args: Option<Vec<String>>,
315*d4726bddSHONG Yifan 
316*d4726bddSHONG Yifan     /// The `patch_tool` attribute of a Bazel repository rule. See
317*d4726bddSHONG Yifan     /// [http_archive.patch_tool](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_tool)
318*d4726bddSHONG Yifan     pub(crate) patch_tool: Option<String>,
319*d4726bddSHONG Yifan 
320*d4726bddSHONG Yifan     /// The `patches` attribute of a Bazel repository rule. See
321*d4726bddSHONG Yifan     /// [http_archive.patches](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patches)
322*d4726bddSHONG Yifan     pub(crate) patches: Option<BTreeSet<String>>,
323*d4726bddSHONG Yifan 
324*d4726bddSHONG Yifan     /// Extra targets the should be aliased during rendering.
325*d4726bddSHONG Yifan     pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
326*d4726bddSHONG Yifan 
327*d4726bddSHONG Yifan     /// Transition rule to use instead of `native.alias()`.
328*d4726bddSHONG Yifan     pub(crate) alias_rule: Option<AliasRule>,
329*d4726bddSHONG Yifan 
330*d4726bddSHONG Yifan     /// The crates to use instead of the generated one.
331*d4726bddSHONG Yifan     pub(crate) override_targets: Option<BTreeMap<String, Label>>,
332*d4726bddSHONG Yifan }
333*d4726bddSHONG Yifan 
334*d4726bddSHONG Yifan macro_rules! joined_extra_member {
335*d4726bddSHONG Yifan     ($lhs:expr, $rhs:expr, $fn_new:expr, $fn_extend:expr) => {
336*d4726bddSHONG Yifan         if let Some(lhs) = $lhs {
337*d4726bddSHONG Yifan             if let Some(rhs) = $rhs {
338*d4726bddSHONG Yifan                 let mut new = $fn_new();
339*d4726bddSHONG Yifan                 $fn_extend(&mut new, lhs);
340*d4726bddSHONG Yifan                 $fn_extend(&mut new, rhs);
341*d4726bddSHONG Yifan                 Some(new)
342*d4726bddSHONG Yifan             } else {
343*d4726bddSHONG Yifan                 Some(lhs)
344*d4726bddSHONG Yifan             }
345*d4726bddSHONG Yifan         } else if $rhs.is_some() {
346*d4726bddSHONG Yifan             $rhs
347*d4726bddSHONG Yifan         } else {
348*d4726bddSHONG Yifan             None
349*d4726bddSHONG Yifan         }
350*d4726bddSHONG Yifan     };
351*d4726bddSHONG Yifan }
352*d4726bddSHONG Yifan 
353*d4726bddSHONG Yifan impl Add for CrateAnnotations {
354*d4726bddSHONG Yifan     type Output = CrateAnnotations;
355*d4726bddSHONG Yifan 
add(self, rhs: Self) -> Self::Output356*d4726bddSHONG Yifan     fn add(self, rhs: Self) -> Self::Output {
357*d4726bddSHONG Yifan         fn select_merge<T>(lhs: Option<Select<T>>, rhs: Option<Select<T>>) -> Option<Select<T>>
358*d4726bddSHONG Yifan         where
359*d4726bddSHONG Yifan             T: Selectable,
360*d4726bddSHONG Yifan         {
361*d4726bddSHONG Yifan             match (lhs, rhs) {
362*d4726bddSHONG Yifan                 (Some(lhs), Some(rhs)) => Some(Select::merge(lhs, rhs)),
363*d4726bddSHONG Yifan                 (Some(lhs), None) => Some(lhs),
364*d4726bddSHONG Yifan                 (None, Some(rhs)) => Some(rhs),
365*d4726bddSHONG Yifan                 (None, None) => None,
366*d4726bddSHONG Yifan             }
367*d4726bddSHONG Yifan         }
368*d4726bddSHONG Yifan 
369*d4726bddSHONG Yifan         let concat_string = |lhs: &mut String, rhs: String| {
370*d4726bddSHONG Yifan             *lhs = format!("{lhs}{rhs}");
371*d4726bddSHONG Yifan         };
372*d4726bddSHONG Yifan 
373*d4726bddSHONG Yifan         #[rustfmt::skip]
374*d4726bddSHONG Yifan         let output = CrateAnnotations {
375*d4726bddSHONG Yifan             gen_binaries: self.gen_binaries.or(rhs.gen_binaries),
376*d4726bddSHONG Yifan             gen_build_script: self.gen_build_script.or(rhs.gen_build_script),
377*d4726bddSHONG Yifan             deps: select_merge(self.deps, rhs.deps),
378*d4726bddSHONG Yifan             proc_macro_deps: select_merge(self.proc_macro_deps, rhs.proc_macro_deps),
379*d4726bddSHONG Yifan             crate_features: select_merge(self.crate_features, rhs.crate_features),
380*d4726bddSHONG Yifan             data: select_merge(self.data, rhs.data),
381*d4726bddSHONG Yifan             data_glob: joined_extra_member!(self.data_glob, rhs.data_glob, BTreeSet::new, BTreeSet::extend),
382*d4726bddSHONG Yifan             disable_pipelining: self.disable_pipelining || rhs.disable_pipelining,
383*d4726bddSHONG Yifan             compile_data: select_merge(self.compile_data, rhs.compile_data),
384*d4726bddSHONG Yifan             compile_data_glob: joined_extra_member!(self.compile_data_glob, rhs.compile_data_glob, BTreeSet::new, BTreeSet::extend),
385*d4726bddSHONG Yifan             rustc_env: select_merge(self.rustc_env, rhs.rustc_env),
386*d4726bddSHONG Yifan             rustc_env_files: select_merge(self.rustc_env_files, rhs.rustc_env_files),
387*d4726bddSHONG Yifan             rustc_flags: select_merge(self.rustc_flags, rhs.rustc_flags),
388*d4726bddSHONG Yifan             build_script_deps: select_merge(self.build_script_deps, rhs.build_script_deps),
389*d4726bddSHONG Yifan             build_script_proc_macro_deps: select_merge(self.build_script_proc_macro_deps, rhs.build_script_proc_macro_deps),
390*d4726bddSHONG Yifan             build_script_data: select_merge(self.build_script_data, rhs.build_script_data),
391*d4726bddSHONG Yifan             build_script_tools: select_merge(self.build_script_tools, rhs.build_script_tools),
392*d4726bddSHONG Yifan             build_script_data_glob: joined_extra_member!(self.build_script_data_glob, rhs.build_script_data_glob, BTreeSet::new, BTreeSet::extend),
393*d4726bddSHONG Yifan             build_script_env: select_merge(self.build_script_env, rhs.build_script_env),
394*d4726bddSHONG Yifan             build_script_rustc_env: select_merge(self.build_script_rustc_env, rhs.build_script_rustc_env),
395*d4726bddSHONG Yifan             build_script_toolchains: joined_extra_member!(self.build_script_toolchains, rhs.build_script_toolchains, BTreeSet::new, BTreeSet::extend),
396*d4726bddSHONG Yifan             build_script_rundir: self.build_script_rundir.or(rhs.build_script_rundir),
397*d4726bddSHONG Yifan             additive_build_file_content: joined_extra_member!(self.additive_build_file_content, rhs.additive_build_file_content, String::new, concat_string),
398*d4726bddSHONG Yifan             shallow_since: self.shallow_since.or(rhs.shallow_since),
399*d4726bddSHONG Yifan             patch_args: joined_extra_member!(self.patch_args, rhs.patch_args, Vec::new, Vec::extend),
400*d4726bddSHONG Yifan             patch_tool: self.patch_tool.or(rhs.patch_tool),
401*d4726bddSHONG Yifan             patches: joined_extra_member!(self.patches, rhs.patches, BTreeSet::new, BTreeSet::extend),
402*d4726bddSHONG Yifan             extra_aliased_targets: joined_extra_member!(self.extra_aliased_targets, rhs.extra_aliased_targets, BTreeMap::new, BTreeMap::extend),
403*d4726bddSHONG Yifan             alias_rule: self.alias_rule.or(rhs.alias_rule),
404*d4726bddSHONG Yifan             override_targets: self.override_targets.or(rhs.override_targets),
405*d4726bddSHONG Yifan         };
406*d4726bddSHONG Yifan 
407*d4726bddSHONG Yifan         output
408*d4726bddSHONG Yifan     }
409*d4726bddSHONG Yifan }
410*d4726bddSHONG Yifan 
411*d4726bddSHONG Yifan impl Sum for CrateAnnotations {
sum<I: Iterator<Item = Self>>(iter: I) -> Self412*d4726bddSHONG Yifan     fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
413*d4726bddSHONG Yifan         iter.fold(CrateAnnotations::default(), |a, b| a + b)
414*d4726bddSHONG Yifan     }
415*d4726bddSHONG Yifan }
416*d4726bddSHONG Yifan 
417*d4726bddSHONG Yifan /// A subset of `crate.annotation` that we allow packages to define in their
418*d4726bddSHONG Yifan /// free-form Cargo.toml metadata.
419*d4726bddSHONG Yifan ///
420*d4726bddSHONG Yifan /// ```toml
421*d4726bddSHONG Yifan /// [package.metadata.bazel]
422*d4726bddSHONG Yifan /// additive_build_file_contents = """
423*d4726bddSHONG Yifan ///     ...
424*d4726bddSHONG Yifan /// """
425*d4726bddSHONG Yifan /// data = ["font.woff2"]
426*d4726bddSHONG Yifan /// extra_aliased_targets = { ... }
427*d4726bddSHONG Yifan /// gen_build_script = false
428*d4726bddSHONG Yifan /// ```
429*d4726bddSHONG Yifan ///
430*d4726bddSHONG Yifan /// These are considered default values which apply if the Bazel workspace does
431*d4726bddSHONG Yifan /// not specify a different value for the same annotation in their
432*d4726bddSHONG Yifan /// crates_repository attributes.
433*d4726bddSHONG Yifan #[derive(Debug, Deserialize)]
434*d4726bddSHONG Yifan pub(crate) struct AnnotationsProvidedByPackage {
435*d4726bddSHONG Yifan     pub(crate) gen_build_script: Option<bool>,
436*d4726bddSHONG Yifan     pub(crate) data: Option<Select<BTreeSet<Label>>>,
437*d4726bddSHONG Yifan     pub(crate) data_glob: Option<BTreeSet<String>>,
438*d4726bddSHONG Yifan     pub(crate) deps: Option<Select<BTreeSet<Label>>>,
439*d4726bddSHONG Yifan     pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
440*d4726bddSHONG Yifan     pub(crate) compile_data_glob: Option<BTreeSet<String>>,
441*d4726bddSHONG Yifan     pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
442*d4726bddSHONG Yifan     pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
443*d4726bddSHONG Yifan     pub(crate) rustc_flags: Option<Select<Vec<String>>>,
444*d4726bddSHONG Yifan     pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
445*d4726bddSHONG Yifan     pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
446*d4726bddSHONG Yifan     pub(crate) build_script_rundir: Option<Select<String>>,
447*d4726bddSHONG Yifan     pub(crate) additive_build_file_content: Option<String>,
448*d4726bddSHONG Yifan     pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
449*d4726bddSHONG Yifan }
450*d4726bddSHONG Yifan 
451*d4726bddSHONG Yifan impl CrateAnnotations {
apply_defaults_from_package_metadata( &mut self, pkg_metadata: &serde_json::Value, )452*d4726bddSHONG Yifan     pub(crate) fn apply_defaults_from_package_metadata(
453*d4726bddSHONG Yifan         &mut self,
454*d4726bddSHONG Yifan         pkg_metadata: &serde_json::Value,
455*d4726bddSHONG Yifan     ) {
456*d4726bddSHONG Yifan         #[deny(unused_variables)]
457*d4726bddSHONG Yifan         let AnnotationsProvidedByPackage {
458*d4726bddSHONG Yifan             gen_build_script,
459*d4726bddSHONG Yifan             data,
460*d4726bddSHONG Yifan             data_glob,
461*d4726bddSHONG Yifan             deps,
462*d4726bddSHONG Yifan             compile_data,
463*d4726bddSHONG Yifan             compile_data_glob,
464*d4726bddSHONG Yifan             rustc_env,
465*d4726bddSHONG Yifan             rustc_env_files,
466*d4726bddSHONG Yifan             rustc_flags,
467*d4726bddSHONG Yifan             build_script_env,
468*d4726bddSHONG Yifan             build_script_rustc_env,
469*d4726bddSHONG Yifan             build_script_rundir,
470*d4726bddSHONG Yifan             additive_build_file_content,
471*d4726bddSHONG Yifan             extra_aliased_targets,
472*d4726bddSHONG Yifan         } = match AnnotationsProvidedByPackage::deserialize(&pkg_metadata["bazel"]) {
473*d4726bddSHONG Yifan             Ok(annotations) => annotations,
474*d4726bddSHONG Yifan             // Ignore bad annotations. The set of supported annotations evolves
475*d4726bddSHONG Yifan             // over time across different versions of crate_universe, and we
476*d4726bddSHONG Yifan             // don't want a library to be impossible to import into Bazel for
477*d4726bddSHONG Yifan             // having old or broken annotations. The Bazel workspace can specify
478*d4726bddSHONG Yifan             // its own correct annotations.
479*d4726bddSHONG Yifan             Err(_) => return,
480*d4726bddSHONG Yifan         };
481*d4726bddSHONG Yifan 
482*d4726bddSHONG Yifan         fn default<T>(workspace_value: &mut Option<T>, default_value: Option<T>) {
483*d4726bddSHONG Yifan             if workspace_value.is_none() {
484*d4726bddSHONG Yifan                 *workspace_value = default_value;
485*d4726bddSHONG Yifan             }
486*d4726bddSHONG Yifan         }
487*d4726bddSHONG Yifan 
488*d4726bddSHONG Yifan         default(&mut self.gen_build_script, gen_build_script);
489*d4726bddSHONG Yifan         default(&mut self.gen_build_script, gen_build_script);
490*d4726bddSHONG Yifan         default(&mut self.data, data);
491*d4726bddSHONG Yifan         default(&mut self.data_glob, data_glob);
492*d4726bddSHONG Yifan         default(&mut self.deps, deps);
493*d4726bddSHONG Yifan         default(&mut self.compile_data, compile_data);
494*d4726bddSHONG Yifan         default(&mut self.compile_data_glob, compile_data_glob);
495*d4726bddSHONG Yifan         default(&mut self.rustc_env, rustc_env);
496*d4726bddSHONG Yifan         default(&mut self.rustc_env_files, rustc_env_files);
497*d4726bddSHONG Yifan         default(&mut self.rustc_flags, rustc_flags);
498*d4726bddSHONG Yifan         default(&mut self.build_script_env, build_script_env);
499*d4726bddSHONG Yifan         default(&mut self.build_script_rustc_env, build_script_rustc_env);
500*d4726bddSHONG Yifan         default(&mut self.build_script_rundir, build_script_rundir);
501*d4726bddSHONG Yifan         default(
502*d4726bddSHONG Yifan             &mut self.additive_build_file_content,
503*d4726bddSHONG Yifan             additive_build_file_content,
504*d4726bddSHONG Yifan         );
505*d4726bddSHONG Yifan         default(&mut self.extra_aliased_targets, extra_aliased_targets);
506*d4726bddSHONG Yifan     }
507*d4726bddSHONG Yifan }
508*d4726bddSHONG Yifan 
509*d4726bddSHONG Yifan /// A unique identifier for Crates
510*d4726bddSHONG Yifan #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
511*d4726bddSHONG Yifan pub struct CrateId {
512*d4726bddSHONG Yifan     /// The name of the crate
513*d4726bddSHONG Yifan     pub name: String,
514*d4726bddSHONG Yifan 
515*d4726bddSHONG Yifan     /// The crate's semantic version
516*d4726bddSHONG Yifan     pub version: semver::Version,
517*d4726bddSHONG Yifan }
518*d4726bddSHONG Yifan 
519*d4726bddSHONG Yifan impl CrateId {
520*d4726bddSHONG Yifan     /// Construct a new [CrateId]
new(name: String, version: semver::Version) -> Self521*d4726bddSHONG Yifan     pub(crate) fn new(name: String, version: semver::Version) -> Self {
522*d4726bddSHONG Yifan         Self { name, version }
523*d4726bddSHONG Yifan     }
524*d4726bddSHONG Yifan }
525*d4726bddSHONG Yifan 
526*d4726bddSHONG Yifan impl From<&Package> for CrateId {
from(package: &Package) -> Self527*d4726bddSHONG Yifan     fn from(package: &Package) -> Self {
528*d4726bddSHONG Yifan         Self {
529*d4726bddSHONG Yifan             name: package.name.clone(),
530*d4726bddSHONG Yifan             version: package.version.clone(),
531*d4726bddSHONG Yifan         }
532*d4726bddSHONG Yifan     }
533*d4726bddSHONG Yifan }
534*d4726bddSHONG Yifan 
535*d4726bddSHONG Yifan impl Serialize for CrateId {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,536*d4726bddSHONG Yifan     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
537*d4726bddSHONG Yifan     where
538*d4726bddSHONG Yifan         S: Serializer,
539*d4726bddSHONG Yifan     {
540*d4726bddSHONG Yifan         serializer.serialize_str(&format!("{} {}", self.name, self.version))
541*d4726bddSHONG Yifan     }
542*d4726bddSHONG Yifan }
543*d4726bddSHONG Yifan 
544*d4726bddSHONG Yifan struct CrateIdVisitor;
545*d4726bddSHONG Yifan impl<'de> Visitor<'de> for CrateIdVisitor {
546*d4726bddSHONG Yifan     type Value = CrateId;
547*d4726bddSHONG Yifan 
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result548*d4726bddSHONG Yifan     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
549*d4726bddSHONG Yifan         formatter.write_str("Expected string value of `{name} {version}`.")
550*d4726bddSHONG Yifan     }
551*d4726bddSHONG Yifan 
visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error,552*d4726bddSHONG Yifan     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
553*d4726bddSHONG Yifan     where
554*d4726bddSHONG Yifan         E: serde::de::Error,
555*d4726bddSHONG Yifan     {
556*d4726bddSHONG Yifan         let (name, version_str) = v.rsplit_once(' ').ok_or_else(|| {
557*d4726bddSHONG Yifan             E::custom(format!(
558*d4726bddSHONG Yifan                 "Expected string value of `{{name}} {{version}}`. Got '{v}'"
559*d4726bddSHONG Yifan             ))
560*d4726bddSHONG Yifan         })?;
561*d4726bddSHONG Yifan         let version = semver::Version::parse(version_str).map_err(|err| {
562*d4726bddSHONG Yifan             E::custom(format!(
563*d4726bddSHONG Yifan                 "Couldn't parse {version_str} as a semver::Version: {err}"
564*d4726bddSHONG Yifan             ))
565*d4726bddSHONG Yifan         })?;
566*d4726bddSHONG Yifan         Ok(CrateId {
567*d4726bddSHONG Yifan             name: name.to_string(),
568*d4726bddSHONG Yifan             version,
569*d4726bddSHONG Yifan         })
570*d4726bddSHONG Yifan     }
571*d4726bddSHONG Yifan }
572*d4726bddSHONG Yifan 
573*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for CrateId {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>,574*d4726bddSHONG Yifan     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
575*d4726bddSHONG Yifan     where
576*d4726bddSHONG Yifan         D: serde::Deserializer<'de>,
577*d4726bddSHONG Yifan     {
578*d4726bddSHONG Yifan         deserializer.deserialize_str(CrateIdVisitor)
579*d4726bddSHONG Yifan     }
580*d4726bddSHONG Yifan }
581*d4726bddSHONG Yifan 
582*d4726bddSHONG Yifan impl std::fmt::Display for CrateId {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result583*d4726bddSHONG Yifan     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
584*d4726bddSHONG Yifan         fmt::Display::fmt(&format!("{} {}", self.name, self.version), f)
585*d4726bddSHONG Yifan     }
586*d4726bddSHONG Yifan }
587*d4726bddSHONG Yifan 
588*d4726bddSHONG Yifan #[derive(Debug, Hash, Clone, PartialEq, Eq)]
589*d4726bddSHONG Yifan pub(crate) enum GenBinaries {
590*d4726bddSHONG Yifan     All,
591*d4726bddSHONG Yifan     Some(BTreeSet<String>),
592*d4726bddSHONG Yifan }
593*d4726bddSHONG Yifan 
594*d4726bddSHONG Yifan impl Default for GenBinaries {
default() -> Self595*d4726bddSHONG Yifan     fn default() -> Self {
596*d4726bddSHONG Yifan         GenBinaries::Some(BTreeSet::new())
597*d4726bddSHONG Yifan     }
598*d4726bddSHONG Yifan }
599*d4726bddSHONG Yifan 
600*d4726bddSHONG Yifan impl Serialize for GenBinaries {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,601*d4726bddSHONG Yifan     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
602*d4726bddSHONG Yifan     where
603*d4726bddSHONG Yifan         S: Serializer,
604*d4726bddSHONG Yifan     {
605*d4726bddSHONG Yifan         match self {
606*d4726bddSHONG Yifan             GenBinaries::All => serializer.serialize_bool(true),
607*d4726bddSHONG Yifan             GenBinaries::Some(set) if set.is_empty() => serializer.serialize_bool(false),
608*d4726bddSHONG Yifan             GenBinaries::Some(set) => serializer.collect_seq(set),
609*d4726bddSHONG Yifan         }
610*d4726bddSHONG Yifan     }
611*d4726bddSHONG Yifan }
612*d4726bddSHONG Yifan 
613*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for GenBinaries {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,614*d4726bddSHONG Yifan     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
615*d4726bddSHONG Yifan     where
616*d4726bddSHONG Yifan         D: Deserializer<'de>,
617*d4726bddSHONG Yifan     {
618*d4726bddSHONG Yifan         deserializer.deserialize_any(GenBinariesVisitor)
619*d4726bddSHONG Yifan     }
620*d4726bddSHONG Yifan }
621*d4726bddSHONG Yifan 
622*d4726bddSHONG Yifan struct GenBinariesVisitor;
623*d4726bddSHONG Yifan impl<'de> Visitor<'de> for GenBinariesVisitor {
624*d4726bddSHONG Yifan     type Value = GenBinaries;
625*d4726bddSHONG Yifan 
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result626*d4726bddSHONG Yifan     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
627*d4726bddSHONG Yifan         formatter.write_str("boolean, or array of bin names")
628*d4726bddSHONG Yifan     }
629*d4726bddSHONG Yifan 
visit_bool<E>(self, gen_binaries: bool) -> Result<Self::Value, E>630*d4726bddSHONG Yifan     fn visit_bool<E>(self, gen_binaries: bool) -> Result<Self::Value, E> {
631*d4726bddSHONG Yifan         if gen_binaries {
632*d4726bddSHONG Yifan             Ok(GenBinaries::All)
633*d4726bddSHONG Yifan         } else {
634*d4726bddSHONG Yifan             Ok(GenBinaries::Some(BTreeSet::new()))
635*d4726bddSHONG Yifan         }
636*d4726bddSHONG Yifan     }
637*d4726bddSHONG Yifan 
visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error> where A: SeqAccess<'de>,638*d4726bddSHONG Yifan     fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
639*d4726bddSHONG Yifan     where
640*d4726bddSHONG Yifan         A: SeqAccess<'de>,
641*d4726bddSHONG Yifan     {
642*d4726bddSHONG Yifan         BTreeSet::deserialize(SeqAccessDeserializer::new(seq)).map(GenBinaries::Some)
643*d4726bddSHONG Yifan     }
644*d4726bddSHONG Yifan }
645*d4726bddSHONG Yifan 
646*d4726bddSHONG Yifan /// Workspace specific settings to control how targets are generated
647*d4726bddSHONG Yifan #[derive(Debug, Default, Serialize, Deserialize, Clone)]
648*d4726bddSHONG Yifan #[serde(deny_unknown_fields)]
649*d4726bddSHONG Yifan pub(crate) struct Config {
650*d4726bddSHONG Yifan     /// Whether to generate `rust_binary` targets for all bins by default
651*d4726bddSHONG Yifan     pub(crate) generate_binaries: bool,
652*d4726bddSHONG Yifan 
653*d4726bddSHONG Yifan     /// Whether or not to generate Cargo build scripts by default
654*d4726bddSHONG Yifan     pub(crate) generate_build_scripts: bool,
655*d4726bddSHONG Yifan 
656*d4726bddSHONG Yifan     /// Additional settings to apply to generated crates
657*d4726bddSHONG Yifan     #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
658*d4726bddSHONG Yifan     pub(crate) annotations: BTreeMap<CrateNameAndVersionReq, CrateAnnotations>,
659*d4726bddSHONG Yifan 
660*d4726bddSHONG Yifan     /// Settings used to determine various render info
661*d4726bddSHONG Yifan     pub(crate) rendering: RenderConfig,
662*d4726bddSHONG Yifan 
663*d4726bddSHONG Yifan     /// The contents of a Cargo configuration file
664*d4726bddSHONG Yifan     pub(crate) cargo_config: Option<toml::Value>,
665*d4726bddSHONG Yifan 
666*d4726bddSHONG Yifan     /// A set of platform triples to use in generated select statements
667*d4726bddSHONG Yifan     #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
668*d4726bddSHONG Yifan     pub(crate) supported_platform_triples: BTreeSet<TargetTriple>,
669*d4726bddSHONG Yifan }
670*d4726bddSHONG Yifan 
671*d4726bddSHONG Yifan impl Config {
try_from_path<T: AsRef<Path>>(path: T) -> Result<Self>672*d4726bddSHONG Yifan     pub(crate) fn try_from_path<T: AsRef<Path>>(path: T) -> Result<Self> {
673*d4726bddSHONG Yifan         let data = fs::read_to_string(path)?;
674*d4726bddSHONG Yifan         Ok(serde_json::from_str(&data)?)
675*d4726bddSHONG Yifan     }
676*d4726bddSHONG Yifan }
677*d4726bddSHONG Yifan 
678*d4726bddSHONG Yifan #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
679*d4726bddSHONG Yifan pub struct CrateNameAndVersionReq {
680*d4726bddSHONG Yifan     /// The name of the crate
681*d4726bddSHONG Yifan     pub name: String,
682*d4726bddSHONG Yifan 
683*d4726bddSHONG Yifan     version_req_string: VersionReqString,
684*d4726bddSHONG Yifan }
685*d4726bddSHONG Yifan 
686*d4726bddSHONG Yifan impl Serialize for CrateNameAndVersionReq {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,687*d4726bddSHONG Yifan     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
688*d4726bddSHONG Yifan     where
689*d4726bddSHONG Yifan         S: Serializer,
690*d4726bddSHONG Yifan     {
691*d4726bddSHONG Yifan         serializer.serialize_str(&format!(
692*d4726bddSHONG Yifan             "{} {}",
693*d4726bddSHONG Yifan             self.name, self.version_req_string.original
694*d4726bddSHONG Yifan         ))
695*d4726bddSHONG Yifan     }
696*d4726bddSHONG Yifan }
697*d4726bddSHONG Yifan 
698*d4726bddSHONG Yifan struct CrateNameAndVersionReqVisitor;
699*d4726bddSHONG Yifan impl<'de> Visitor<'de> for CrateNameAndVersionReqVisitor {
700*d4726bddSHONG Yifan     type Value = CrateNameAndVersionReq;
701*d4726bddSHONG Yifan 
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result702*d4726bddSHONG Yifan     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
703*d4726bddSHONG Yifan         formatter.write_str("Expected string value of `{name} {version}`.")
704*d4726bddSHONG Yifan     }
705*d4726bddSHONG Yifan 
visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error,706*d4726bddSHONG Yifan     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
707*d4726bddSHONG Yifan     where
708*d4726bddSHONG Yifan         E: serde::de::Error,
709*d4726bddSHONG Yifan     {
710*d4726bddSHONG Yifan         let (name, version) = v.rsplit_once(' ').ok_or_else(|| {
711*d4726bddSHONG Yifan             E::custom(format!(
712*d4726bddSHONG Yifan                 "Expected string value of `{{name}} {{version}}`. Got '{v}'"
713*d4726bddSHONG Yifan             ))
714*d4726bddSHONG Yifan         })?;
715*d4726bddSHONG Yifan         version
716*d4726bddSHONG Yifan             .parse()
717*d4726bddSHONG Yifan             .map(|version| CrateNameAndVersionReq {
718*d4726bddSHONG Yifan                 name: name.to_string(),
719*d4726bddSHONG Yifan                 version_req_string: version,
720*d4726bddSHONG Yifan             })
721*d4726bddSHONG Yifan             .map_err(|err| E::custom(err.to_string()))
722*d4726bddSHONG Yifan     }
723*d4726bddSHONG Yifan }
724*d4726bddSHONG Yifan 
725*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for CrateNameAndVersionReq {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>,726*d4726bddSHONG Yifan     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
727*d4726bddSHONG Yifan     where
728*d4726bddSHONG Yifan         D: serde::Deserializer<'de>,
729*d4726bddSHONG Yifan     {
730*d4726bddSHONG Yifan         deserializer.deserialize_str(CrateNameAndVersionReqVisitor)
731*d4726bddSHONG Yifan     }
732*d4726bddSHONG Yifan }
733*d4726bddSHONG Yifan 
734*d4726bddSHONG Yifan /// A version requirement (i.e. a semver::VersionReq) which preserves the original string it was parsed from.
735*d4726bddSHONG Yifan /// This means that you can report back to the user whether they wrote `1` or `1.0.0` or `^1.0.0` or `>=1,<2`,
736*d4726bddSHONG Yifan /// and support exact round-trip serialization and deserialization.
737*d4726bddSHONG Yifan #[derive(Clone, Debug)]
738*d4726bddSHONG Yifan pub struct VersionReqString {
739*d4726bddSHONG Yifan     original: String,
740*d4726bddSHONG Yifan 
741*d4726bddSHONG Yifan     parsed: VersionReq,
742*d4726bddSHONG Yifan }
743*d4726bddSHONG Yifan 
744*d4726bddSHONG Yifan impl FromStr for VersionReqString {
745*d4726bddSHONG Yifan     type Err = anyhow::Error;
746*d4726bddSHONG Yifan 
from_str(original: &str) -> Result<Self, Self::Err>747*d4726bddSHONG Yifan     fn from_str(original: &str) -> Result<Self, Self::Err> {
748*d4726bddSHONG Yifan         let parsed = VersionReq::parse(original)
749*d4726bddSHONG Yifan             .context("VersionReqString must be a valid semver requirement")?;
750*d4726bddSHONG Yifan         Ok(VersionReqString {
751*d4726bddSHONG Yifan             original: original.to_owned(),
752*d4726bddSHONG Yifan             parsed,
753*d4726bddSHONG Yifan         })
754*d4726bddSHONG Yifan     }
755*d4726bddSHONG Yifan }
756*d4726bddSHONG Yifan 
757*d4726bddSHONG Yifan impl PartialEq for VersionReqString {
eq(&self, other: &Self) -> bool758*d4726bddSHONG Yifan     fn eq(&self, other: &Self) -> bool {
759*d4726bddSHONG Yifan         self.original == other.original
760*d4726bddSHONG Yifan     }
761*d4726bddSHONG Yifan }
762*d4726bddSHONG Yifan 
763*d4726bddSHONG Yifan impl Eq for VersionReqString {}
764*d4726bddSHONG Yifan 
765*d4726bddSHONG Yifan impl PartialOrd for VersionReqString {
partial_cmp(&self, other: &Self) -> Option<Ordering>766*d4726bddSHONG Yifan     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
767*d4726bddSHONG Yifan         Some(self.cmp(other))
768*d4726bddSHONG Yifan     }
769*d4726bddSHONG Yifan }
770*d4726bddSHONG Yifan 
771*d4726bddSHONG Yifan impl Ord for VersionReqString {
cmp(&self, other: &Self) -> Ordering772*d4726bddSHONG Yifan     fn cmp(&self, other: &Self) -> Ordering {
773*d4726bddSHONG Yifan         Ord::cmp(&self.original, &other.original)
774*d4726bddSHONG Yifan     }
775*d4726bddSHONG Yifan }
776*d4726bddSHONG Yifan 
777*d4726bddSHONG Yifan impl Serialize for VersionReqString {
serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> where S: Serializer,778*d4726bddSHONG Yifan     fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
779*d4726bddSHONG Yifan     where
780*d4726bddSHONG Yifan         S: Serializer,
781*d4726bddSHONG Yifan     {
782*d4726bddSHONG Yifan         serializer.serialize_str(&self.original)
783*d4726bddSHONG Yifan     }
784*d4726bddSHONG Yifan }
785*d4726bddSHONG Yifan 
786*d4726bddSHONG Yifan impl<'de> Deserialize<'de> for VersionReqString {
deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> where D: Deserializer<'de>,787*d4726bddSHONG Yifan     fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
788*d4726bddSHONG Yifan     where
789*d4726bddSHONG Yifan         D: Deserializer<'de>,
790*d4726bddSHONG Yifan     {
791*d4726bddSHONG Yifan         struct StringVisitor;
792*d4726bddSHONG Yifan 
793*d4726bddSHONG Yifan         impl<'de> Visitor<'de> for StringVisitor {
794*d4726bddSHONG Yifan             type Value = String;
795*d4726bddSHONG Yifan 
796*d4726bddSHONG Yifan             fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
797*d4726bddSHONG Yifan                 formatter.write_str("string of a semver requirement")
798*d4726bddSHONG Yifan             }
799*d4726bddSHONG Yifan         }
800*d4726bddSHONG Yifan 
801*d4726bddSHONG Yifan         let original = deserializer.deserialize_str(StringVisitor)?;
802*d4726bddSHONG Yifan         let parsed = VersionReq::parse(&original).map_err(|_| {
803*d4726bddSHONG Yifan             serde::de::Error::invalid_value(
804*d4726bddSHONG Yifan                 Unexpected::Str(&original),
805*d4726bddSHONG Yifan                 &"a valid semver requirement",
806*d4726bddSHONG Yifan             )
807*d4726bddSHONG Yifan         })?;
808*d4726bddSHONG Yifan         Ok(VersionReqString { original, parsed })
809*d4726bddSHONG Yifan     }
810*d4726bddSHONG Yifan }
811*d4726bddSHONG Yifan 
812*d4726bddSHONG Yifan impl CrateNameAndVersionReq {
813*d4726bddSHONG Yifan     #[cfg(test)]
new(name: String, version_req_string: VersionReqString) -> CrateNameAndVersionReq814*d4726bddSHONG Yifan     pub fn new(name: String, version_req_string: VersionReqString) -> CrateNameAndVersionReq {
815*d4726bddSHONG Yifan         CrateNameAndVersionReq {
816*d4726bddSHONG Yifan             name,
817*d4726bddSHONG Yifan             version_req_string,
818*d4726bddSHONG Yifan         }
819*d4726bddSHONG Yifan     }
820*d4726bddSHONG Yifan 
821*d4726bddSHONG Yifan     /// Compares a [CrateNameAndVersionReq] against a [cargo_metadata::Package].
matches(&self, package: &Package) -> bool822*d4726bddSHONG Yifan     pub fn matches(&self, package: &Package) -> bool {
823*d4726bddSHONG Yifan         // If the package name does not match, it's obviously
824*d4726bddSHONG Yifan         // not the right package
825*d4726bddSHONG Yifan         if self.name != "*" && self.name != package.name {
826*d4726bddSHONG Yifan             return false;
827*d4726bddSHONG Yifan         }
828*d4726bddSHONG Yifan 
829*d4726bddSHONG Yifan         // First see if the package version matches exactly
830*d4726bddSHONG Yifan         if package.version.to_string() == self.version_req_string.original {
831*d4726bddSHONG Yifan             return true;
832*d4726bddSHONG Yifan         }
833*d4726bddSHONG Yifan 
834*d4726bddSHONG Yifan         // If the version provided is the wildcard "*", it matches. Do not
835*d4726bddSHONG Yifan         // delegate to the semver crate in this case because semver does not
836*d4726bddSHONG Yifan         // consider "*" to match prerelease packages. That's expected behavior
837*d4726bddSHONG Yifan         // in the context of declaring package dependencies, but not in the
838*d4726bddSHONG Yifan         // context of declaring which versions of preselected packages an
839*d4726bddSHONG Yifan         // annotation applies to.
840*d4726bddSHONG Yifan         if self.version_req_string.original == "*" {
841*d4726bddSHONG Yifan             return true;
842*d4726bddSHONG Yifan         }
843*d4726bddSHONG Yifan 
844*d4726bddSHONG Yifan         // Next, check to see if the version provided is a semver req and
845*d4726bddSHONG Yifan         // check if the package matches the condition
846*d4726bddSHONG Yifan         self.version_req_string.parsed.matches(&package.version)
847*d4726bddSHONG Yifan     }
848*d4726bddSHONG Yifan }
849*d4726bddSHONG Yifan 
850*d4726bddSHONG Yifan #[cfg(test)]
851*d4726bddSHONG Yifan mod test {
852*d4726bddSHONG Yifan     use super::*;
853*d4726bddSHONG Yifan 
854*d4726bddSHONG Yifan     use crate::test::*;
855*d4726bddSHONG Yifan 
856*d4726bddSHONG Yifan     #[test]
test_crate_id_serde()857*d4726bddSHONG Yifan     fn test_crate_id_serde() {
858*d4726bddSHONG Yifan         let id: CrateId = serde_json::from_str("\"crate 0.1.0\"").unwrap();
859*d4726bddSHONG Yifan         assert_eq!(
860*d4726bddSHONG Yifan             id,
861*d4726bddSHONG Yifan             CrateId::new("crate".to_owned(), semver::Version::new(0, 1, 0))
862*d4726bddSHONG Yifan         );
863*d4726bddSHONG Yifan         assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
864*d4726bddSHONG Yifan     }
865*d4726bddSHONG Yifan 
866*d4726bddSHONG Yifan     #[test]
test_crate_id_matches()867*d4726bddSHONG Yifan     fn test_crate_id_matches() {
868*d4726bddSHONG Yifan         let mut package = mock_cargo_metadata_package();
869*d4726bddSHONG Yifan         let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "0.1.0".parse().unwrap());
870*d4726bddSHONG Yifan 
871*d4726bddSHONG Yifan         package.version = cargo_metadata::semver::Version::new(0, 1, 0);
872*d4726bddSHONG Yifan         assert!(id.matches(&package));
873*d4726bddSHONG Yifan 
874*d4726bddSHONG Yifan         package.version = cargo_metadata::semver::Version::new(1, 0, 0);
875*d4726bddSHONG Yifan         assert!(!id.matches(&package));
876*d4726bddSHONG Yifan     }
877*d4726bddSHONG Yifan 
878*d4726bddSHONG Yifan     #[test]
test_crate_name_and_version_req_serde()879*d4726bddSHONG Yifan     fn test_crate_name_and_version_req_serde() {
880*d4726bddSHONG Yifan         let id: CrateNameAndVersionReq = serde_json::from_str("\"crate 0.1.0\"").unwrap();
881*d4726bddSHONG Yifan         assert_eq!(
882*d4726bddSHONG Yifan             id,
883*d4726bddSHONG Yifan             CrateNameAndVersionReq::new(
884*d4726bddSHONG Yifan                 "crate".to_owned(),
885*d4726bddSHONG Yifan                 VersionReqString::from_str("0.1.0").unwrap()
886*d4726bddSHONG Yifan             )
887*d4726bddSHONG Yifan         );
888*d4726bddSHONG Yifan         assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
889*d4726bddSHONG Yifan     }
890*d4726bddSHONG Yifan 
891*d4726bddSHONG Yifan     #[test]
test_crate_name_and_version_req_serde_semver()892*d4726bddSHONG Yifan     fn test_crate_name_and_version_req_serde_semver() {
893*d4726bddSHONG Yifan         let id: CrateNameAndVersionReq = serde_json::from_str("\"crate *\"").unwrap();
894*d4726bddSHONG Yifan         assert_eq!(
895*d4726bddSHONG Yifan             id,
896*d4726bddSHONG Yifan             CrateNameAndVersionReq::new(
897*d4726bddSHONG Yifan                 "crate".to_owned(),
898*d4726bddSHONG Yifan                 VersionReqString::from_str("*").unwrap()
899*d4726bddSHONG Yifan             )
900*d4726bddSHONG Yifan         );
901*d4726bddSHONG Yifan         assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate *\"");
902*d4726bddSHONG Yifan     }
903*d4726bddSHONG Yifan 
904*d4726bddSHONG Yifan     #[test]
test_crate_name_and_version_req_semver_matches()905*d4726bddSHONG Yifan     fn test_crate_name_and_version_req_semver_matches() {
906*d4726bddSHONG Yifan         let mut package = mock_cargo_metadata_package();
907*d4726bddSHONG Yifan         package.version = cargo_metadata::semver::Version::new(1, 0, 0);
908*d4726bddSHONG Yifan         let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "*".parse().unwrap());
909*d4726bddSHONG Yifan         assert!(id.matches(&package));
910*d4726bddSHONG Yifan 
911*d4726bddSHONG Yifan         let mut prerelease = mock_cargo_metadata_package();
912*d4726bddSHONG Yifan         prerelease.version = cargo_metadata::semver::Version::parse("1.0.0-pre.0").unwrap();
913*d4726bddSHONG Yifan         assert!(id.matches(&prerelease));
914*d4726bddSHONG Yifan 
915*d4726bddSHONG Yifan         let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "<1".parse().unwrap());
916*d4726bddSHONG Yifan         assert!(!id.matches(&package));
917*d4726bddSHONG Yifan     }
918*d4726bddSHONG Yifan 
919*d4726bddSHONG Yifan     #[test]
deserialize_config()920*d4726bddSHONG Yifan     fn deserialize_config() {
921*d4726bddSHONG Yifan         let runfiles = runfiles::Runfiles::create().unwrap();
922*d4726bddSHONG Yifan         let path = runfiles::rlocation!(
923*d4726bddSHONG Yifan             runfiles,
924*d4726bddSHONG Yifan             "rules_rust/crate_universe/test_data/serialized_configs/config.json"
925*d4726bddSHONG Yifan         );
926*d4726bddSHONG Yifan 
927*d4726bddSHONG Yifan         let content = std::fs::read_to_string(path).unwrap();
928*d4726bddSHONG Yifan 
929*d4726bddSHONG Yifan         let config: Config = serde_json::from_str(&content).unwrap();
930*d4726bddSHONG Yifan 
931*d4726bddSHONG Yifan         // Annotations
932*d4726bddSHONG Yifan         let annotation = config
933*d4726bddSHONG Yifan             .annotations
934*d4726bddSHONG Yifan             .get(&CrateNameAndVersionReq::new(
935*d4726bddSHONG Yifan                 "rand".to_owned(),
936*d4726bddSHONG Yifan                 "0.8.5".parse().unwrap(),
937*d4726bddSHONG Yifan             ))
938*d4726bddSHONG Yifan             .unwrap();
939*d4726bddSHONG Yifan         assert_eq!(
940*d4726bddSHONG Yifan             annotation.crate_features,
941*d4726bddSHONG Yifan             Some(Select::from_value(BTreeSet::from(["small_rng".to_owned()])))
942*d4726bddSHONG Yifan         );
943*d4726bddSHONG Yifan 
944*d4726bddSHONG Yifan         // Global settings
945*d4726bddSHONG Yifan         assert!(config.cargo_config.is_none());
946*d4726bddSHONG Yifan         assert!(!config.generate_binaries);
947*d4726bddSHONG Yifan         assert!(!config.generate_build_scripts);
948*d4726bddSHONG Yifan 
949*d4726bddSHONG Yifan         // Render Config
950*d4726bddSHONG Yifan         assert_eq!(
951*d4726bddSHONG Yifan             config.rendering.platforms_template,
952*d4726bddSHONG Yifan             "//custom/platform:{triple}"
953*d4726bddSHONG Yifan         );
954*d4726bddSHONG Yifan     }
955*d4726bddSHONG Yifan }
956