From 501a063061b79b436431711ae68d31822c888a86 Mon Sep 17 00:00:00 2001 From: Michael Ciraci Date: Sat, 20 Jan 2024 11:01:35 -0600 Subject: [PATCH 1/4] Converting Target kind and crate_types to enum representation, along with an Unknown(String) as a fallback --- src/lib.rs | 122 ++++++++++++++++++++++++++++++++++++++++-- tests/selftest.rs | 16 +++--- tests/test_samples.rs | 77 ++++++++++++++++++++++---- 3 files changed, 194 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5af9f1eb..46c80aa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -509,7 +509,7 @@ pub struct Target { /// `bin`, `lib`, `rlib`, `dylib`, `cdylib`, `staticlib`, `proc-macro`. /// /// Other possible values may be added in the future. - pub kind: Vec, + pub kind: Vec, /// Similar to `kind`, but only reports the /// [Cargo crate types](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field): /// `bin`, `lib`, `rlib`, `dylib`, `cdylib`, `staticlib`, `proc-macro`. @@ -518,7 +518,7 @@ pub struct Target { /// Other possible values may be added in the future. #[serde(default)] #[cfg_attr(feature = "builder", builder(default))] - pub crate_types: Vec, + pub crate_types: Vec, #[serde(default)] #[cfg_attr(feature = "builder", builder(default))] @@ -555,7 +555,8 @@ pub struct Target { impl Target { fn is_kind(&self, name: &str) -> bool { - self.kind.iter().any(|kind| kind == name) + let enum_kind: TargetKind = name.into(); + self.kind.iter().any(|kind| kind == &enum_kind) } /// Return true if this target is of kind "lib". @@ -594,6 +595,121 @@ impl Target { } } +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +/// Kind of target. +/// +/// The possible values are `example`, `test`, `bench`, `custom-build` and +/// [Cargo crate types](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field): +/// `bin`, `lib`, `rlib`, `dylib`, `cdylib`, `staticlib`, `proc-macro`. +/// +/// Other possible values may be added in the future. +pub enum TargetKind { + /// `cargo bench` target + #[serde(rename = "bench")] + Bench, + /// Binary executable target + #[serde(rename = "bin")] + Bin, + /// Custom build target + #[serde(rename = "custom-build")] + CustomBuild, + /// Dynamic system library target + #[serde(rename = "cdylib")] + CDyLib, + /// Dynamic Rust library target + #[serde(rename = "dylib")] + DyLib, + /// Example target + #[serde(rename = "example")] + Example, + /// Rust library + #[serde(rename = "lib")] + Lib, + /// Procedural Macro + #[serde(rename = "proc-macro")] + ProcMacro, + /// Rust library for use as an intermediate artifact + #[serde(rename = "rlib")] + RLib, + /// Static system library + #[serde(rename = "staticlib")] + StaticLib, + /// Test target + #[serde(rename = "test")] + Test, + /// Unknown type + #[serde(untagged)] + Unknown(String), +} + +impl From<&str> for TargetKind { + fn from(value: &str) -> Self { + match value { + "example" => TargetKind::Example, + "test" => TargetKind::Test, + "bench" => TargetKind::Bench, + "custom-build" => TargetKind::CustomBuild, + "bin" => TargetKind::Bin, + "lib" => TargetKind::Lib, + "rlib" => TargetKind::RLib, + "dylib" => TargetKind::DyLib, + "cdylib" => TargetKind::CDyLib, + "staticlib" => TargetKind::StaticLib, + "proc-macro" => TargetKind::ProcMacro, + x => TargetKind::Unknown(x.to_string()), + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +/// Similar to `kind`, but only reports the +/// [Cargo crate types](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field): +/// `bin`, `lib`, `rlib`, `dylib`, `cdylib`, `staticlib`, `proc-macro`. +/// Everything that's not a proc macro or a library of some kind is reported as "bin". +/// +/// Other possible values may be added in the future. +pub enum CrateType { + /// Binary executable target + #[serde(rename = "bin")] + Bin, + /// Dynamic system library target + #[serde(rename = "cdylib")] + CDyLib, + /// Dynamic Rust library target + #[serde(rename = "dylib")] + DyLib, + /// Rust library + #[serde(rename = "lib")] + Lib, + /// Procedural Macro + #[serde(rename = "proc-macro")] + ProcMacro, + /// Rust library for use as an intermediate artifact + #[serde(rename = "rlib")] + RLib, + /// Static system library + #[serde(rename = "staticlib")] + StaticLib, + /// Unkown type + #[serde(untagged)] + Unknown(String), +} + +impl From<&str> for CrateType { + fn from(value: &str) -> Self { + match value { + "bin" => CrateType::Bin, + "lib" => CrateType::Lib, + "rlib" => CrateType::RLib, + "dylib" => CrateType::DyLib, + "cdylib" => CrateType::CDyLib, + "staticlib" => CrateType::StaticLib, + "proc-macro" => CrateType::ProcMacro, + x => CrateType::Unknown(x.to_string()), + } + } +} + /// The Rust edition /// /// As of writing this comment rust editions 2024, 2027 and 2030 are not actually a thing yet but are parsed nonetheless for future proofing. diff --git a/tests/selftest.rs b/tests/selftest.rs index dbcc67ec..e6dadf77 100644 --- a/tests/selftest.rs +++ b/tests/selftest.rs @@ -25,13 +25,13 @@ fn metadata() { .iter() .find(|t| t.name == "cargo_metadata") .unwrap(); - assert_eq!(lib.kind[0], "lib"); - assert_eq!(lib.crate_types[0], "lib"); + assert_eq!(lib.kind[0], "lib".into()); + assert_eq!(lib.crate_types[0], "lib".into()); let selftest = this.targets.iter().find(|t| t.name == "selftest").unwrap(); assert_eq!(selftest.name, "selftest"); - assert_eq!(selftest.kind[0], "test"); - assert_eq!(selftest.crate_types[0], "bin"); + assert_eq!(selftest.kind[0], "test".into()); + assert_eq!(selftest.crate_types[0], "bin".into()); let package_metadata = &metadata.packages[0] .metadata @@ -141,13 +141,13 @@ fn metadata_deps() { .iter() .find(|t| t.name == "cargo_metadata") .unwrap(); - assert_eq!(lib.kind[0], "lib"); - assert_eq!(lib.crate_types[0], "lib"); + assert_eq!(lib.kind[0], "lib".into()); + assert_eq!(lib.crate_types[0], "lib".into()); let selftest = this.targets.iter().find(|t| t.name == "selftest").unwrap(); assert_eq!(selftest.name, "selftest"); - assert_eq!(selftest.kind[0], "test"); - assert_eq!(selftest.crate_types[0], "bin"); + assert_eq!(selftest.kind[0], "test".into()); + assert_eq!(selftest.crate_types[0], "bin".into()); let dependencies = &this.dependencies; diff --git a/tests/test_samples.rs b/tests/test_samples.rs index c834778a..a0f513f3 100644 --- a/tests/test_samples.rs +++ b/tests/test_samples.rs @@ -95,8 +95,8 @@ fn old_minimal() { assert_eq!(pkg.targets.len(), 1); let target = &pkg.targets[0]; assert_eq!(target.name, "foo"); - assert_eq!(target.kind, vec!["bin"]); - assert_eq!(target.crate_types, vec!["bin"]); + assert_eq!(target.kind, vec!["bin".into()]); + assert_eq!(target.crate_types, vec!["bin".into()]); assert_eq!(target.required_features.len(), 0); assert_eq!(target.src_path, "/foo/src/main.rs"); assert_eq!(target.edition, Edition::E2015); @@ -309,10 +309,13 @@ fn all_the_fields() { assert_eq!(all.targets.len(), 8); let lib = get_file_name!("lib.rs"); assert_eq!(lib.name, "all"); - assert_eq!(sorted!(lib.kind), vec!["cdylib", "rlib", "staticlib"]); + assert_eq!( + sorted!(lib.kind), + vec!["cdylib".into(), "rlib".into(), "staticlib".into()] + ); assert_eq!( sorted!(lib.crate_types), - vec!["cdylib", "rlib", "staticlib"] + vec!["cdylib".into(), "rlib".into(), "staticlib".into()] ); assert_eq!(lib.required_features.len(), 0); assert_eq!(lib.edition, Edition::E2018); @@ -321,8 +324,8 @@ fn all_the_fields() { assert!(lib.doc); let main = get_file_name!("main.rs"); - assert_eq!(main.crate_types, vec!["bin"]); - assert_eq!(main.kind, vec!["bin"]); + assert_eq!(main.crate_types, vec!["bin".into()]); + assert_eq!(main.kind, vec!["bin".into()]); assert!(!main.doctest); assert!(main.test); assert!(main.doc); @@ -335,17 +338,17 @@ fn all_the_fields() { assert_eq!(reqfeat.required_features, vec!["feat2"]); let ex1 = get_file_name!("ex1.rs"); - assert_eq!(ex1.kind, vec!["example"]); + assert_eq!(ex1.kind, vec!["example".into()]); assert!(!ex1.test); let t1 = get_file_name!("t1.rs"); - assert_eq!(t1.kind, vec!["test"]); + assert_eq!(t1.kind, vec!["test".into()]); let b1 = get_file_name!("b1.rs"); - assert_eq!(b1.kind, vec!["bench"]); + assert_eq!(b1.kind, vec!["bench".into()]); let build = get_file_name!("build.rs"); - assert_eq!(build.kind, vec!["custom-build"]); + assert_eq!(build.kind, vec!["custom-build".into()]); if ver >= semver::Version::parse("1.60.0").unwrap() { // 1.60 now reports optional dependencies within the features table @@ -725,3 +728,57 @@ fn missing_workspace_default_members() { let meta: Metadata = serde_json::from_str(JSON_OLD_MINIMAL).unwrap(); let _ = &*meta.workspace_default_members; } + +#[test] +fn test_unknown_target_kind_and_crate_type() { + // Both kind and crate_type set to a type not yet known + let json = r#" +{ + "packages": [ + { + "name": "alt", + "version": "0.1.0", + "id": "alt 0.1.0 (path+file:///alt)", + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ + "future-kind" + ], + "crate_types": [ + "future-type" + ], + "name": "alt", + "src_path": "/alt/src/lib.rs", + "edition": "2018" + } + ], + "features": {}, + "manifest_path": "/alt/Cargo.toml", + "metadata": null, + "authors": [], + "categories": [], + "keywords": [], + "readme": null, + "repository": null, + "edition": "2018", + "links": null + } + ], + "workspace_members": [ + "alt 0.1.0 (path+file:///alt)" + ], + "resolve": null, + "target_directory": "/alt/target", + "version": 1, + "workspace_root": "/alt" +} +"#; + let meta: Metadata = serde_json::from_str(json).unwrap(); + assert_eq!(meta.packages.len(), 1); + assert_eq!(meta.packages[0].targets.len(), 1); + let target = &meta.packages[0].targets[0]; + assert_eq!(target.kind[0], "future-kind".into()); + assert_eq!(target.crate_types[0], "future-type".into()); +} From 6a6c3b72874bbb6513bb1c030cf3fffeae62d975 Mon Sep 17 00:00:00 2001 From: Michael Ciraci Date: Sat, 20 Jan 2024 11:12:05 -0600 Subject: [PATCH 2/4] Converting Target::is_kind to accept an enum instead of String. This change is purely internal to the library --- src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 46c80aa4..39fbf06c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -554,44 +554,43 @@ pub struct Target { } impl Target { - fn is_kind(&self, name: &str) -> bool { - let enum_kind: TargetKind = name.into(); - self.kind.iter().any(|kind| kind == &enum_kind) + fn is_kind(&self, name: TargetKind) -> bool { + self.kind.iter().any(|kind| kind == &name) } /// Return true if this target is of kind "lib". pub fn is_lib(&self) -> bool { - self.is_kind("lib") + self.is_kind(TargetKind::Lib) } /// Return true if this target is of kind "bin". pub fn is_bin(&self) -> bool { - self.is_kind("bin") + self.is_kind(TargetKind::Bin) } /// Return true if this target is of kind "example". pub fn is_example(&self) -> bool { - self.is_kind("example") + self.is_kind(TargetKind::Example) } /// Return true if this target is of kind "test". pub fn is_test(&self) -> bool { - self.is_kind("test") + self.is_kind(TargetKind::Test) } /// Return true if this target is of kind "bench". pub fn is_bench(&self) -> bool { - self.is_kind("bench") + self.is_kind(TargetKind::Bench) } /// Return true if this target is of kind "custom-build". pub fn is_custom_build(&self) -> bool { - self.is_kind("custom-build") + self.is_kind(TargetKind::CustomBuild) } /// Return true if this target is of kind "proc-macro". pub fn is_proc_macro(&self) -> bool { - self.is_kind("proc-macro") + self.is_kind(TargetKind::ProcMacro) } } From 5313f32eacf6853a563eca7739c453021a7a36b0 Mon Sep 17 00:00:00 2001 From: Michael Ciraci Date: Sat, 20 Jan 2024 11:39:37 -0600 Subject: [PATCH 3/4] Updating changelog and marking enums as non_exhaustive --- CHANGELOG.md | 1 + src/lib.rs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c045c7d0..3a6cdf60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Changed - Made `parse_stream` more versatile by accepting anything that implements `Read`. +- Converted `TargetKind` and `CrateType` to an enum representation. ### Removed diff --git a/src/lib.rs b/src/lib.rs index 39fbf06c..4c951faa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -594,7 +594,6 @@ impl Target { } } -#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] /// Kind of target. /// /// The possible values are `example`, `test`, `bench`, `custom-build` and @@ -602,6 +601,8 @@ impl Target { /// `bin`, `lib`, `rlib`, `dylib`, `cdylib`, `staticlib`, `proc-macro`. /// /// Other possible values may be added in the future. +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[non_exhaustive] pub enum TargetKind { /// `cargo bench` target #[serde(rename = "bench")] @@ -660,13 +661,14 @@ impl From<&str> for TargetKind { } } -#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] /// Similar to `kind`, but only reports the /// [Cargo crate types](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field): /// `bin`, `lib`, `rlib`, `dylib`, `cdylib`, `staticlib`, `proc-macro`. /// Everything that's not a proc macro or a library of some kind is reported as "bin". /// /// Other possible values may be added in the future. +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[non_exhaustive] pub enum CrateType { /// Binary executable target #[serde(rename = "bin")] From cb54bb5f099f1cd51d6453d81994b78c19084671 Mon Sep 17 00:00:00 2001 From: Michael Ciraci Date: Sat, 20 Jan 2024 14:10:22 -0600 Subject: [PATCH 4/4] Correcting tests for nightly --- tests/test_samples.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/tests/test_samples.rs b/tests/test_samples.rs index a0f513f3..2f406c5b 100644 --- a/tests/test_samples.rs +++ b/tests/test_samples.rs @@ -208,7 +208,7 @@ fn all_the_fields() { } ); assert_eq!(meta.workspace_members.len(), 1); - assert!(meta.workspace_members[0].to_string().starts_with("all")); + assert!(meta.workspace_members[0].to_string().contains("all")); if ver >= semver::Version::parse("1.71.0").unwrap() { assert_eq!(&*meta.workspace_default_members, &meta.workspace_members); } @@ -217,7 +217,7 @@ fn all_the_fields() { let all = meta.packages.iter().find(|p| p.name == "all").unwrap(); assert_eq!(all.version, semver::Version::parse("0.1.0").unwrap()); assert_eq!(all.authors, vec!["Jane Doe "]); - assert!(all.id.to_string().starts_with("all")); + assert!(all.id.to_string().contains("all")); assert_eq!(all.description, Some("Package description.".to_string())); assert_eq!(all.license, Some("MIT/Apache-2.0".to_string())); assert_eq!(all.license_file, Some(Utf8PathBuf::from("LICENSE"))); @@ -393,18 +393,13 @@ fn all_the_fields() { ); let resolve = meta.resolve.as_ref().unwrap(); - assert!(resolve - .root - .as_ref() - .unwrap() - .to_string() - .starts_with("all")); + assert!(resolve.root.as_ref().unwrap().to_string().contains("all")); assert_eq!(resolve.nodes.len(), 9); let path_dep = resolve .nodes .iter() - .find(|n| n.id.to_string().starts_with("path-dep")) + .find(|n| n.id.to_string().contains("path-dep")) .unwrap(); assert_eq!(path_dep.deps.len(), 0); assert_eq!(path_dep.dependencies.len(), 0); @@ -413,29 +408,29 @@ fn all_the_fields() { let bitflags = resolve .nodes .iter() - .find(|n| n.id.to_string().starts_with("bitflags")) + .find(|n| n.id.to_string().contains("bitflags")) .unwrap(); assert_eq!(bitflags.features, vec!["default"]); let featdep = resolve .nodes .iter() - .find(|n| n.id.to_string().starts_with("featdep")) + .find(|n| n.id.to_string().contains("featdep")) .unwrap(); assert_eq!(featdep.features, vec!["i128"]); let all = resolve .nodes .iter() - .find(|n| n.id.to_string().starts_with("all")) + .find(|n| n.id.to_string().contains("all")) .unwrap(); assert_eq!(all.dependencies.len(), 8); assert_eq!(all.deps.len(), 8); let newname = all.deps.iter().find(|d| d.name == "newname").unwrap(); - assert!(newname.pkg.to_string().starts_with("oldname")); + assert!(newname.pkg.to_string().contains("oldname")); // Note the underscore here. let path_dep = all.deps.iter().find(|d| d.name == "path_dep").unwrap(); - assert!(path_dep.pkg.to_string().starts_with("path-dep")); + assert!(path_dep.pkg.to_string().contains("path-dep")); assert_eq!(path_dep.dep_kinds.len(), 1); let kind = &path_dep.dep_kinds[0]; assert_eq!(kind.kind, DependencyKind::Normal); @@ -446,7 +441,7 @@ fn all_the_fields() { .iter() .find(|d| d.name == "different_name") .unwrap(); - assert!(namedep.pkg.to_string().starts_with("namedep")); + assert!(namedep.pkg.to_string().contains("namedep")); assert_eq!(sorted!(all.features), vec!["bitflags", "default", "feat1"]); let bdep = all.deps.iter().find(|d| d.name == "bdep").unwrap(); @@ -599,7 +594,7 @@ fn advanced_feature_configuration() { let all = resolve .nodes .iter() - .find(|n| n.id.to_string().starts_with("all")) + .find(|n| !n.features.is_empty()) .unwrap(); all.features.clone()