1use std::cmp::Ordering;
4use std::collections::{BTreeMap, BTreeSet};
5use std::fmt::Formatter;
6use std::iter::Sum;
7use std::ops::Add;
8use std::path::Path;
9use std::str::FromStr;
10use std::{fmt, fs};
11
12use anyhow::{Context, Result};
13use cargo_lock::package::GitReference;
14use cargo_metadata::Package;
15use semver::VersionReq;
16use serde::de::value::SeqAccessDeserializer;
17use serde::de::{Deserializer, SeqAccess, Unexpected, Visitor};
18use serde::{Deserialize, Serialize, Serializer};
19
20use crate::select::{Select, Selectable};
21use crate::utils::starlark::Label;
22use crate::utils::target_triple::TargetTriple;
23
24#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
26#[serde(rename_all = "lowercase")]
27pub(crate) enum VendorMode {
28 Local,
30
31 Remote,
33}
34
35impl std::fmt::Display for VendorMode {
36 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)]
49pub(crate) struct RenderConfig {
50 pub(crate) repository_name: String,
52
53 #[serde(default = "default_build_file_template")]
56 pub(crate) build_file_template: String,
57
58 #[serde(default = "default_crate_label_template")]
61 pub(crate) crate_label_template: String,
62
63 #[serde(default = "default_crate_alias_template")]
66 pub(crate) crate_alias_template: String,
67
68 #[serde(default = "default_crates_module_template")]
72 pub(crate) crates_module_template: String,
73
74 #[serde(default = "default_crate_repository_template")]
77 pub(crate) crate_repository_template: String,
78
79 #[serde(default)]
81 pub(crate) default_alias_rule: AliasRule,
82
83 pub(crate) default_package_name: Option<String>,
86
87 #[serde(default = "default_generate_target_compatible_with")]
90 pub(crate) generate_target_compatible_with: bool,
91
92 #[serde(default = "default_platforms_template")]
95 pub(crate) platforms_template: String,
96
97 pub(crate) regen_command: String,
99
100 pub(crate) vendor_mode: Option<VendorMode>,
102
103 #[serde(default = "default_generate_rules_license_metadata")]
105 pub(crate) generate_rules_license_metadata: bool,
106
107 pub(crate) generate_cargo_toml_env_vars: bool,
110}
111
112impl Default for RenderConfig {
116 fn default() -> Self {
117 RenderConfig {
118 repository_name: String::default(),
119 build_file_template: default_build_file_template(),
120 crate_label_template: default_crate_label_template(),
121 crate_alias_template: default_crate_alias_template(),
122 crates_module_template: default_crates_module_template(),
123 crate_repository_template: default_crate_repository_template(),
124 default_alias_rule: AliasRule::default(),
125 default_package_name: Option::default(),
126 generate_cargo_toml_env_vars: default_generate_cargo_toml_env_vars(),
127 generate_target_compatible_with: default_generate_target_compatible_with(),
128 platforms_template: default_platforms_template(),
129 regen_command: String::default(),
130 vendor_mode: Option::default(),
131 generate_rules_license_metadata: default_generate_rules_license_metadata(),
132 }
133 }
134}
135
136impl RenderConfig {
137 pub(crate) fn are_sources_present(&self) -> bool {
138 self.vendor_mode == Some(VendorMode::Local)
139 }
140}
141
142fn default_build_file_template() -> String {
143 "//:BUILD.{name}-{version}.bazel".to_owned()
144}
145
146fn default_crates_module_template() -> String {
147 "//:{file}".to_owned()
148}
149
150fn default_crate_label_template() -> String {
151 "@{repository}__{name}-{version}//:{target}".to_owned()
152}
153
154fn default_crate_alias_template() -> String {
155 "//:{name}-{version}".to_owned()
156}
157
158fn default_crate_repository_template() -> String {
159 "{repository}__{name}-{version}".to_owned()
160}
161
162fn default_platforms_template() -> String {
163 "@rules_rust//rust/platform:{triple}".to_owned()
164}
165
166fn default_generate_cargo_toml_env_vars() -> bool {
167 true
168}
169
170fn default_generate_target_compatible_with() -> bool {
171 true
172}
173
174fn default_generate_rules_license_metadata() -> bool {
175 false
176}
177
178#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
180pub(crate) enum Commitish {
181 Tag(String),
183
184 Branch(String),
186
187 Rev(String),
189}
190
191impl From<GitReference> for Commitish {
192 fn from(git_ref: GitReference) -> Self {
193 match git_ref {
194 GitReference::Tag(v) => Self::Tag(v),
195 GitReference::Branch(v) => Self::Branch(v),
196 GitReference::Rev(v) => Self::Rev(v),
197 }
198 }
199}
200
201#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
202pub(crate) enum AliasRule {
203 #[default]
204 #[serde(rename = "alias")]
205 Alias,
206 #[serde(rename = "dbg")]
207 Dbg,
208 #[serde(rename = "fastbuild")]
209 Fastbuild,
210 #[serde(rename = "opt")]
211 Opt,
212 #[serde(untagged)]
213 Custom { bzl: String, rule: String },
214}
215
216impl AliasRule {
217 pub(crate) fn bzl(&self) -> Option<String> {
218 match self {
219 AliasRule::Alias => None,
220 AliasRule::Dbg | AliasRule::Fastbuild | AliasRule::Opt => {
221 Some("//:alias_rules.bzl".to_owned())
222 }
223 AliasRule::Custom { bzl, .. } => Some(bzl.clone()),
224 }
225 }
226
227 pub(crate) fn rule(&self) -> String {
228 match self {
229 AliasRule::Alias => "alias".to_owned(),
230 AliasRule::Dbg => "transition_alias_dbg".to_owned(),
231 AliasRule::Fastbuild => "transition_alias_fastbuild".to_owned(),
232 AliasRule::Opt => "transition_alias_opt".to_owned(),
233 AliasRule::Custom { rule, .. } => rule.clone(),
234 }
235 }
236}
237
238#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
239pub(crate) struct CrateAnnotations {
240 pub(crate) gen_binaries: Option<GenBinaries>,
242
243 pub(crate) gen_build_script: Option<bool>,
245
246 pub(crate) deps: Option<Select<BTreeSet<Label>>>,
249
250 pub(crate) proc_macro_deps: Option<Select<BTreeSet<Label>>>,
253
254 pub(crate) crate_features: Option<Select<BTreeSet<String>>>,
257
258 pub(crate) data: Option<Select<BTreeSet<Label>>>,
261
262 pub(crate) data_glob: Option<BTreeSet<String>>,
265
266 pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
269
270 pub(crate) compile_data_glob: Option<BTreeSet<String>>,
273
274 pub(crate) compile_data_glob_excludes: Option<BTreeSet<String>>,
277
278 pub(crate) disable_pipelining: bool,
280
281 pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
284
285 pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
288
289 pub(crate) rustc_flags: Option<Select<Vec<String>>>,
292
293 pub(crate) build_script_deps: Option<Select<BTreeSet<Label>>>,
296
297 pub(crate) build_script_link_deps: Option<Select<BTreeSet<Label>>>,
300
301 pub(crate) build_script_proc_macro_deps: Option<Select<BTreeSet<Label>>>,
304
305 pub(crate) build_script_compile_data: Option<Select<BTreeSet<Label>>>,
308
309 pub(crate) build_script_data: Option<Select<BTreeSet<Label>>>,
312
313 pub(crate) build_script_tools: Option<Select<BTreeSet<Label>>>,
316
317 pub(crate) build_script_data_glob: Option<BTreeSet<String>>,
320
321 pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
324
325 pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
328
329 pub(crate) build_script_toolchains: Option<BTreeSet<Label>>,
332
333 pub(crate) build_script_use_default_shell_env: Option<i32>,
336
337 pub(crate) build_script_rundir: Option<Select<String>>,
339
340 pub(crate) additive_build_file_content: Option<String>,
342
343 pub(crate) shallow_since: Option<String>,
346
347 pub(crate) patch_args: Option<Vec<String>>,
350
351 pub(crate) patch_tool: Option<String>,
354
355 pub(crate) patches: Option<BTreeSet<String>>,
358
359 pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
361
362 pub(crate) alias_rule: Option<AliasRule>,
364
365 pub(crate) override_targets: Option<BTreeMap<String, Label>>,
367}
368
369macro_rules! joined_extra_member {
370 ($lhs:expr, $rhs:expr, $fn_new:expr, $fn_extend:expr) => {
371 if let Some(lhs) = $lhs {
372 if let Some(rhs) = $rhs {
373 let mut new = $fn_new();
374 $fn_extend(&mut new, lhs);
375 $fn_extend(&mut new, rhs);
376 Some(new)
377 } else {
378 Some(lhs)
379 }
380 } else if $rhs.is_some() {
381 $rhs
382 } else {
383 None
384 }
385 };
386}
387
388impl Add for CrateAnnotations {
389 type Output = CrateAnnotations;
390
391 fn add(self, rhs: Self) -> Self::Output {
392 fn select_merge<T>(lhs: Option<Select<T>>, rhs: Option<Select<T>>) -> Option<Select<T>>
393 where
394 T: Selectable,
395 {
396 match (lhs, rhs) {
397 (Some(lhs), Some(rhs)) => Some(Select::merge(lhs, rhs)),
398 (Some(lhs), None) => Some(lhs),
399 (None, Some(rhs)) => Some(rhs),
400 (None, None) => None,
401 }
402 }
403
404 let concat_string = |lhs: &mut String, rhs: String| {
405 *lhs = format!("{lhs}{rhs}");
406 };
407
408 #[rustfmt::skip]
409 let output = CrateAnnotations {
410 gen_binaries: self.gen_binaries.or(rhs.gen_binaries),
411 gen_build_script: self.gen_build_script.or(rhs.gen_build_script),
412 deps: select_merge(self.deps, rhs.deps),
413 proc_macro_deps: select_merge(self.proc_macro_deps, rhs.proc_macro_deps),
414 crate_features: select_merge(self.crate_features, rhs.crate_features),
415 data: select_merge(self.data, rhs.data),
416 data_glob: joined_extra_member!(self.data_glob, rhs.data_glob, BTreeSet::new, BTreeSet::extend),
417 disable_pipelining: self.disable_pipelining || rhs.disable_pipelining,
418 compile_data: select_merge(self.compile_data, rhs.compile_data),
419 compile_data_glob: joined_extra_member!(self.compile_data_glob, rhs.compile_data_glob, BTreeSet::new, BTreeSet::extend),
420 compile_data_glob_excludes: joined_extra_member!(self.compile_data_glob_excludes, rhs.compile_data_glob_excludes, BTreeSet::new, BTreeSet::extend),
421 rustc_env: select_merge(self.rustc_env, rhs.rustc_env),
422 rustc_env_files: select_merge(self.rustc_env_files, rhs.rustc_env_files),
423 rustc_flags: select_merge(self.rustc_flags, rhs.rustc_flags),
424 build_script_deps: select_merge(self.build_script_deps, rhs.build_script_deps),
425 build_script_link_deps: select_merge(self.build_script_link_deps, rhs.build_script_link_deps),
426 build_script_proc_macro_deps: select_merge(self.build_script_proc_macro_deps, rhs.build_script_proc_macro_deps),
427 build_script_compile_data: select_merge(self.build_script_compile_data, rhs.build_script_compile_data),
428 build_script_data: select_merge(self.build_script_data, rhs.build_script_data),
429 build_script_tools: select_merge(self.build_script_tools, rhs.build_script_tools),
430 build_script_data_glob: joined_extra_member!(self.build_script_data_glob, rhs.build_script_data_glob, BTreeSet::new, BTreeSet::extend),
431 build_script_env: select_merge(self.build_script_env, rhs.build_script_env),
432 build_script_rustc_env: select_merge(self.build_script_rustc_env, rhs.build_script_rustc_env),
433 build_script_toolchains: joined_extra_member!(self.build_script_toolchains, rhs.build_script_toolchains, BTreeSet::new, BTreeSet::extend),
434 build_script_use_default_shell_env: self.build_script_use_default_shell_env.or(rhs.build_script_use_default_shell_env),
435 build_script_rundir: self.build_script_rundir.or(rhs.build_script_rundir),
436 additive_build_file_content: joined_extra_member!(self.additive_build_file_content, rhs.additive_build_file_content, String::new, concat_string),
437 shallow_since: self.shallow_since.or(rhs.shallow_since),
438 patch_args: joined_extra_member!(self.patch_args, rhs.patch_args, Vec::new, Vec::extend),
439 patch_tool: self.patch_tool.or(rhs.patch_tool),
440 patches: joined_extra_member!(self.patches, rhs.patches, BTreeSet::new, BTreeSet::extend),
441 extra_aliased_targets: joined_extra_member!(self.extra_aliased_targets, rhs.extra_aliased_targets, BTreeMap::new, BTreeMap::extend),
442 alias_rule: self.alias_rule.or(rhs.alias_rule),
443 override_targets: self.override_targets.or(rhs.override_targets),
444 };
445
446 output
447 }
448}
449
450impl Sum for CrateAnnotations {
451 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
452 iter.fold(CrateAnnotations::default(), |a, b| a + b)
453 }
454}
455
456#[derive(Debug, Deserialize)]
473pub(crate) struct AnnotationsProvidedByPackage {
474 pub(crate) gen_build_script: Option<bool>,
475 pub(crate) data: Option<Select<BTreeSet<Label>>>,
476 pub(crate) data_glob: Option<BTreeSet<String>>,
477 pub(crate) deps: Option<Select<BTreeSet<Label>>>,
478 pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
479 pub(crate) compile_data_glob: Option<BTreeSet<String>>,
480 pub(crate) compile_data_glob_excludes: Option<BTreeSet<String>>,
481 pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
482 pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
483 pub(crate) rustc_flags: Option<Select<Vec<String>>>,
484 pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
485 pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
486 pub(crate) build_script_rundir: Option<Select<String>>,
487 pub(crate) additive_build_file_content: Option<String>,
488 pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
489}
490
491impl CrateAnnotations {
492 pub(crate) fn apply_defaults_from_package_metadata(
493 &mut self,
494 pkg_metadata: &serde_json::Value,
495 ) {
496 #[deny(unused_variables)]
497 let AnnotationsProvidedByPackage {
498 gen_build_script,
499 data,
500 data_glob,
501 deps,
502 compile_data,
503 compile_data_glob,
504 compile_data_glob_excludes,
505 rustc_env,
506 rustc_env_files,
507 rustc_flags,
508 build_script_env,
509 build_script_rustc_env,
510 build_script_rundir,
511 additive_build_file_content,
512 extra_aliased_targets,
513 } = match AnnotationsProvidedByPackage::deserialize(&pkg_metadata["bazel"]) {
514 Ok(annotations) => annotations,
515 Err(_) => return,
521 };
522
523 fn default<T>(workspace_value: &mut Option<T>, default_value: Option<T>) {
524 if workspace_value.is_none() {
525 *workspace_value = default_value;
526 }
527 }
528
529 default(&mut self.gen_build_script, gen_build_script);
530 default(&mut self.gen_build_script, gen_build_script);
531 default(&mut self.data, data);
532 default(&mut self.data_glob, data_glob);
533 default(&mut self.deps, deps);
534 default(&mut self.compile_data, compile_data);
535 default(&mut self.compile_data_glob, compile_data_glob);
536 default(
537 &mut self.compile_data_glob_excludes,
538 compile_data_glob_excludes,
539 );
540 default(&mut self.rustc_env, rustc_env);
541 default(&mut self.rustc_env_files, rustc_env_files);
542 default(&mut self.rustc_flags, rustc_flags);
543 default(&mut self.build_script_env, build_script_env);
544 default(&mut self.build_script_rustc_env, build_script_rustc_env);
545 default(&mut self.build_script_rundir, build_script_rundir);
546 default(
547 &mut self.additive_build_file_content,
548 additive_build_file_content,
549 );
550 default(&mut self.extra_aliased_targets, extra_aliased_targets);
551 }
552}
553
554#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
556pub struct CrateId {
557 pub name: String,
559
560 pub version: semver::Version,
562}
563
564impl CrateId {
565 pub(crate) fn new(name: String, version: semver::Version) -> Self {
567 Self { name, version }
568 }
569}
570
571impl From<&Package> for CrateId {
572 fn from(package: &Package) -> Self {
573 Self {
574 name: package.name.clone(),
575 version: package.version.clone(),
576 }
577 }
578}
579
580impl Serialize for CrateId {
581 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
582 where
583 S: Serializer,
584 {
585 serializer.serialize_str(&format!("{} {}", self.name, self.version))
586 }
587}
588
589struct CrateIdVisitor;
590impl Visitor<'_> for CrateIdVisitor {
591 type Value = CrateId;
592
593 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
594 formatter.write_str("Expected string value of `{name} {version}`.")
595 }
596
597 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
598 where
599 E: serde::de::Error,
600 {
601 let (name, version_str) = v.rsplit_once(' ').ok_or_else(|| {
602 E::custom(format!(
603 "Expected string value of `{{name}} {{version}}`. Got '{v}'"
604 ))
605 })?;
606 let version = semver::Version::parse(version_str).map_err(|err| {
607 E::custom(format!(
608 "Couldn't parse {version_str} as a semver::Version: {err}"
609 ))
610 })?;
611 Ok(CrateId {
612 name: name.to_string(),
613 version,
614 })
615 }
616}
617
618impl<'de> Deserialize<'de> for CrateId {
619 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
620 where
621 D: serde::Deserializer<'de>,
622 {
623 deserializer.deserialize_str(CrateIdVisitor)
624 }
625}
626
627impl std::fmt::Display for CrateId {
628 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
629 fmt::Display::fmt(&format!("{} {}", self.name, self.version), f)
630 }
631}
632
633#[derive(Debug, Hash, Clone, PartialEq, Eq)]
634pub(crate) enum GenBinaries {
635 All,
636 Some(BTreeSet<String>),
637}
638
639impl Default for GenBinaries {
640 fn default() -> Self {
641 GenBinaries::Some(BTreeSet::new())
642 }
643}
644
645impl Serialize for GenBinaries {
646 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
647 where
648 S: Serializer,
649 {
650 match self {
651 GenBinaries::All => serializer.serialize_bool(true),
652 GenBinaries::Some(set) if set.is_empty() => serializer.serialize_bool(false),
653 GenBinaries::Some(set) => serializer.collect_seq(set),
654 }
655 }
656}
657
658impl<'de> Deserialize<'de> for GenBinaries {
659 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
660 where
661 D: Deserializer<'de>,
662 {
663 deserializer.deserialize_any(GenBinariesVisitor)
664 }
665}
666
667struct GenBinariesVisitor;
668impl<'de> Visitor<'de> for GenBinariesVisitor {
669 type Value = GenBinaries;
670
671 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
672 formatter.write_str("boolean, or array of bin names")
673 }
674
675 fn visit_bool<E>(self, gen_binaries: bool) -> Result<Self::Value, E> {
676 if gen_binaries {
677 Ok(GenBinaries::All)
678 } else {
679 Ok(GenBinaries::Some(BTreeSet::new()))
680 }
681 }
682
683 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
684 where
685 A: SeqAccess<'de>,
686 {
687 BTreeSet::deserialize(SeqAccessDeserializer::new(seq)).map(GenBinaries::Some)
688 }
689}
690
691#[derive(Debug, Default, Serialize, Deserialize, Clone)]
693#[serde(deny_unknown_fields)]
694pub(crate) struct Config {
695 pub(crate) generate_binaries: bool,
697
698 pub(crate) generate_build_scripts: bool,
700
701 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
703 pub(crate) annotations: BTreeMap<CrateNameAndVersionReq, CrateAnnotations>,
704
705 pub(crate) rendering: RenderConfig,
707
708 pub(crate) cargo_config: Option<toml::Value>,
710
711 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
713 pub(crate) supported_platform_triples: BTreeSet<TargetTriple>,
714}
715
716impl Config {
717 pub(crate) fn try_from_path<T: AsRef<Path>>(path: T) -> Result<Self> {
718 let data = fs::read_to_string(path)?;
719 Ok(serde_json::from_str(&data)?)
720 }
721}
722
723#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
724pub struct CrateNameAndVersionReq {
725 pub name: String,
727
728 version_req_string: VersionReqString,
729}
730
731impl Serialize for CrateNameAndVersionReq {
732 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
733 where
734 S: Serializer,
735 {
736 serializer.serialize_str(&format!(
737 "{} {}",
738 self.name, self.version_req_string.original
739 ))
740 }
741}
742
743struct CrateNameAndVersionReqVisitor;
744impl Visitor<'_> for CrateNameAndVersionReqVisitor {
745 type Value = CrateNameAndVersionReq;
746
747 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
748 formatter.write_str("Expected string value of `{name} {version}`.")
749 }
750
751 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
752 where
753 E: serde::de::Error,
754 {
755 let (name, version) = v.rsplit_once(' ').ok_or_else(|| {
756 E::custom(format!(
757 "Expected string value of `{{name}} {{version}}`. Got '{v}'"
758 ))
759 })?;
760 version
761 .parse()
762 .map(|version| CrateNameAndVersionReq {
763 name: name.to_string(),
764 version_req_string: version,
765 })
766 .map_err(|err| E::custom(err.to_string()))
767 }
768}
769
770impl<'de> Deserialize<'de> for CrateNameAndVersionReq {
771 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
772 where
773 D: serde::Deserializer<'de>,
774 {
775 deserializer.deserialize_str(CrateNameAndVersionReqVisitor)
776 }
777}
778
779#[derive(Clone, Debug)]
783pub struct VersionReqString {
784 original: String,
785
786 parsed: VersionReq,
787}
788
789impl FromStr for VersionReqString {
790 type Err = anyhow::Error;
791
792 fn from_str(original: &str) -> Result<Self, Self::Err> {
793 let parsed = VersionReq::parse(original)
794 .context("VersionReqString must be a valid semver requirement")?;
795 Ok(VersionReqString {
796 original: original.to_owned(),
797 parsed,
798 })
799 }
800}
801
802impl PartialEq for VersionReqString {
803 fn eq(&self, other: &Self) -> bool {
804 self.original == other.original
805 }
806}
807
808impl Eq for VersionReqString {}
809
810impl PartialOrd for VersionReqString {
811 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
812 Some(self.cmp(other))
813 }
814}
815
816impl Ord for VersionReqString {
817 fn cmp(&self, other: &Self) -> Ordering {
818 Ord::cmp(&self.original, &other.original)
819 }
820}
821
822impl Serialize for VersionReqString {
823 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
824 where
825 S: Serializer,
826 {
827 serializer.serialize_str(&self.original)
828 }
829}
830
831impl<'de> Deserialize<'de> for VersionReqString {
832 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
833 where
834 D: Deserializer<'de>,
835 {
836 struct StringVisitor;
837
838 impl Visitor<'_> for StringVisitor {
839 type Value = String;
840
841 fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
842 formatter.write_str("string of a semver requirement")
843 }
844 }
845
846 let original = deserializer.deserialize_str(StringVisitor)?;
847 let parsed = VersionReq::parse(&original).map_err(|_| {
848 serde::de::Error::invalid_value(
849 Unexpected::Str(&original),
850 &"a valid semver requirement",
851 )
852 })?;
853 Ok(VersionReqString { original, parsed })
854 }
855}
856
857impl CrateNameAndVersionReq {
858 #[cfg(test)]
859 pub fn new(name: String, version_req_string: VersionReqString) -> CrateNameAndVersionReq {
860 CrateNameAndVersionReq {
861 name,
862 version_req_string,
863 }
864 }
865
866 pub fn matches(&self, package: &Package) -> bool {
868 if self.name != "*" && self.name != package.name {
871 return false;
872 }
873
874 if package.version.to_string() == self.version_req_string.original {
876 return true;
877 }
878
879 if self.version_req_string.original == "*" {
886 return true;
887 }
888
889 self.version_req_string.parsed.matches(&package.version)
892 }
893}
894
895#[cfg(test)]
896mod test {
897 use super::*;
898
899 use crate::test::*;
900
901 #[test]
902 fn test_crate_id_serde() {
903 let id: CrateId = serde_json::from_str("\"crate 0.1.0\"").unwrap();
904 assert_eq!(
905 id,
906 CrateId::new("crate".to_owned(), semver::Version::new(0, 1, 0))
907 );
908 assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
909 }
910
911 #[test]
912 fn test_crate_id_matches() {
913 let mut package = mock_cargo_metadata_package();
914 let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "0.1.0".parse().unwrap());
915
916 package.version = cargo_metadata::semver::Version::new(0, 1, 0);
917 assert!(id.matches(&package));
918
919 package.version = cargo_metadata::semver::Version::new(1, 0, 0);
920 assert!(!id.matches(&package));
921 }
922
923 #[test]
924 fn test_crate_name_and_version_req_serde() {
925 let id: CrateNameAndVersionReq = serde_json::from_str("\"crate 0.1.0\"").unwrap();
926 assert_eq!(
927 id,
928 CrateNameAndVersionReq::new(
929 "crate".to_owned(),
930 VersionReqString::from_str("0.1.0").unwrap()
931 )
932 );
933 assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
934 }
935
936 #[test]
937 fn test_crate_name_and_version_req_serde_semver() {
938 let id: CrateNameAndVersionReq = serde_json::from_str("\"crate *\"").unwrap();
939 assert_eq!(
940 id,
941 CrateNameAndVersionReq::new(
942 "crate".to_owned(),
943 VersionReqString::from_str("*").unwrap()
944 )
945 );
946 assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate *\"");
947 }
948
949 #[test]
950 fn test_crate_name_and_version_req_semver_matches() {
951 let mut package = mock_cargo_metadata_package();
952 package.version = cargo_metadata::semver::Version::new(1, 0, 0);
953 let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "*".parse().unwrap());
954 assert!(id.matches(&package));
955
956 let mut prerelease = mock_cargo_metadata_package();
957 prerelease.version = cargo_metadata::semver::Version::parse("1.0.0-pre.0").unwrap();
958 assert!(id.matches(&prerelease));
959
960 let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "<1".parse().unwrap());
961 assert!(!id.matches(&package));
962 }
963
964 #[test]
965 fn deserialize_config() {
966 let runfiles = runfiles::Runfiles::create().unwrap();
967 let path = runfiles::rlocation!(
968 runfiles,
969 "rules_rust/crate_universe/test_data/serialized_configs/config.json"
970 )
971 .unwrap();
972
973 let content = std::fs::read_to_string(path).unwrap();
974
975 let config: Config = serde_json::from_str(&content).unwrap();
976
977 let annotation = config
979 .annotations
980 .get(&CrateNameAndVersionReq::new(
981 "rand".to_owned(),
982 "0.8.5".parse().unwrap(),
983 ))
984 .unwrap();
985 assert_eq!(
986 annotation.crate_features,
987 Some(Select::from_value(BTreeSet::from(["small_rng".to_owned()])))
988 );
989
990 assert!(config.cargo_config.is_none());
992 assert!(!config.generate_binaries);
993 assert!(!config.generate_build_scripts);
994
995 assert_eq!(
997 config.rendering.platforms_template,
998 "//custom/platform:{triple}"
999 );
1000 }
1001}