diff --git a/src/lib.rs b/src/lib.rs index a1d586ac..d7c3413c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,6 +151,11 @@ pub struct Metadata { pub packages: Vec, /// A list of all workspace members pub workspace_members: Vec, + /// The list of default workspace members + /// + /// This not available if running with a version of Cargo older than 1.71. + #[serde(skip_serializing_if = "workspace_default_members_is_missing")] + pub workspace_default_members: WorkspaceDefaultMembers, /// Dependencies graph pub resolve: Option, /// Workspace root @@ -190,6 +195,18 @@ impl Metadata { .filter(|&p| self.workspace_members.contains(&p.id)) .collect() } + + /// Get the workspace default packages. + /// + /// # Panics + /// + /// This will panic if running with a version of Cargo older than 1.71. + pub fn workspace_default_packages(&self) -> Vec<&Package> { + self.packages + .iter() + .filter(|&p| self.workspace_default_members.contains(&p.id)) + .collect() + } } impl<'a> std::ops::Index<&'a PackageId> for Metadata { @@ -203,6 +220,41 @@ impl<'a> std::ops::Index<&'a PackageId> for Metadata { } } +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(transparent)] +/// A list of default workspace members. +/// +/// See [`Metadata::workspace_default_members`]. +/// +/// It is only available if running a version of Cargo of 1.71 or newer. +/// +/// # Panics +/// +/// Dereferencing when running an older version of Cargo will panic. +pub struct WorkspaceDefaultMembers(Option>); + +impl core::ops::Deref for WorkspaceDefaultMembers { + type Target = [PackageId]; + + fn deref(&self) -> &Self::Target { + self.0 + .as_ref() + .expect("WorkspaceDefaultMembers should only be dereferenced on Cargo versions >= 1.71") + } +} + +/// Return true if a valid value for [`WorkspaceDefaultMembers`] is missing, and +/// dereferencing it would panic. +/// +/// Internal helper for `skip_serializing_if` and test code. Might be removed in +/// the future. +#[doc(hidden)] +pub fn workspace_default_members_is_missing( + workspace_default_members: &WorkspaceDefaultMembers, +) -> bool { + workspace_default_members.0.is_none() +} + #[derive(Clone, Serialize, Deserialize, Debug)] #[cfg_attr(feature = "builder", derive(Builder))] #[non_exhaustive] diff --git a/tests/selftest.rs b/tests/selftest.rs index d6ab6183..dbcc67ec 100644 --- a/tests/selftest.rs +++ b/tests/selftest.rs @@ -161,3 +161,20 @@ fn metadata_deps() { assert!(serde.req.matches(&Version::parse("1.99.99").unwrap())); assert!(!serde.req.matches(&Version::parse("2.0.0").unwrap())); } + +#[test] +fn workspace_default_packages() { + use cargo_metadata::workspace_default_members_is_missing; + + let metadata = MetadataCommand::new() + .manifest_path("Cargo.toml") + .exec() + .unwrap(); + let workspace_packages = metadata.workspace_packages(); + // this will only trigger on cargo versions that expose + // workspace_default_members (that is, cargo >= 1.71) + if !workspace_default_members_is_missing(&metadata.workspace_default_members) { + let default_packages = metadata.workspace_default_packages(); + assert_eq!(default_packages, workspace_packages); + } +} diff --git a/tests/test_samples.rs b/tests/test_samples.rs index 15128b07..c834778a 100644 --- a/tests/test_samples.rs +++ b/tests/test_samples.rs @@ -5,19 +5,19 @@ extern crate serde_json; use camino::Utf8PathBuf; use cargo_metadata::{ - ArtifactDebuginfo, CargoOpt, DependencyKind, Edition, Message, Metadata, MetadataCommand, + workspace_default_members_is_missing, ArtifactDebuginfo, CargoOpt, DependencyKind, Edition, + Message, Metadata, MetadataCommand, }; -#[test] -fn old_minimal() { - // Output from oldest supported version (1.24). - // This intentionally has as many null fields as possible. - // 1.8 is when metadata was introduced. - // Older versions not supported because the following are required: - // - `workspace_members` added in 1.13 - // - `target_directory` added in 1.19 - // - `workspace_root` added in 1.24 - let json = r#" +/// Output from oldest version ever supported (1.24). +/// +/// This intentionally has as many null fields as possible. +/// 1.8 is when metadata was introduced. +/// Older versions not supported because the following are required: +/// - `workspace_members` added in 1.13 +/// - `target_directory` added in 1.19 +/// - `workspace_root` added in 1.24 +const JSON_OLD_MINIMAL: &str = r#" { "packages": [ { @@ -65,7 +65,10 @@ fn old_minimal() { "workspace_root": "/foo" } "#; - let meta: Metadata = serde_json::from_str(json).unwrap(); + +#[test] +fn old_minimal() { + let meta: Metadata = serde_json::from_str(JSON_OLD_MINIMAL).unwrap(); assert_eq!(meta.packages.len(), 1); let pkg = &meta.packages[0]; assert_eq!(pkg.name, "foo"); @@ -121,6 +124,15 @@ fn old_minimal() { assert_eq!(meta.workspace_root, "/foo"); assert_eq!(meta.workspace_metadata, serde_json::Value::Null); assert_eq!(meta.target_directory, "/foo/target"); + + assert!(workspace_default_members_is_missing( + &meta.workspace_default_members + )); + let serialized = serde_json::to_value(meta).unwrap(); + assert!(!serialized + .as_object() + .unwrap() + .contains_key("workspace_default_members")); } macro_rules! sorted { @@ -178,6 +190,7 @@ fn all_the_fields() { // path added in 1.51 // default_run added in 1.55 // rust_version added in 1.58 + // workspace_default_members added in 1.71 eprintln!("Skipping all_the_fields test, cargo {} is too old.", ver); return; } @@ -187,7 +200,7 @@ fn all_the_fields() { .unwrap(); assert_eq!(meta.workspace_root.file_name().unwrap(), "all"); assert_eq!( - serde_json::from_value::(meta.workspace_metadata).unwrap(), + serde_json::from_value::(meta.workspace_metadata.clone()).unwrap(), WorkspaceMetadata { testobject: TestObject { myvalue: "abc".to_string() @@ -196,6 +209,9 @@ fn all_the_fields() { ); assert_eq!(meta.workspace_members.len(), 1); assert!(meta.workspace_members[0].to_string().starts_with("all")); + if ver >= semver::Version::parse("1.71.0").unwrap() { + assert_eq!(&*meta.workspace_default_members, &meta.workspace_members); + } assert_eq!(meta.packages.len(), 9); let all = meta.packages.iter().find(|p| p.name == "all").unwrap(); @@ -450,6 +466,18 @@ fn all_the_fields() { kind.target.as_ref().map(|x| x.to_string()), Some("cfg(windows)".to_string()) ); + + let serialized = serde_json::to_value(meta).unwrap(); + if ver >= semver::Version::parse("1.71.0").unwrap() { + assert!(serialized.as_object().unwrap()["workspace_default_members"] + .as_array() + .is_some()); + } else { + assert!(!serialized + .as_object() + .unwrap() + .contains_key("workspace_default_members")); + } } #[test] @@ -690,3 +718,10 @@ fn debuginfo_variants() { } } } + +#[test] +#[should_panic = "WorkspaceDefaultMembers should only be dereferenced on Cargo versions >= 1.71"] +fn missing_workspace_default_members() { + let meta: Metadata = serde_json::from_str(JSON_OLD_MINIMAL).unwrap(); + let _ = &*meta.workspace_default_members; +}