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