Skip to content

Commit

Permalink
missing workspace_default_members should panic
Browse files Browse the repository at this point in the history
  • Loading branch information
sorcio committed Aug 31, 2023
1 parent 74c5272 commit b181569
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 32 deletions.
60 changes: 47 additions & 13 deletions src/lib.rs
Expand Up @@ -151,10 +151,11 @@ pub struct Metadata {
pub packages: Vec<Package>,
/// A list of all workspace members
pub workspace_members: Vec<PackageId>,
/// A list of all workspace members
/// The list of default workspace members
///
/// This is always `None` if running with a version of Cargo older than 1.71.
pub workspace_default_members: Option<Vec<PackageId>>,
/// 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<Resolve>,
/// Workspace root
Expand Down Expand Up @@ -197,16 +198,14 @@ impl Metadata {

/// Get the workspace default packages.
///
/// This will always return `None` if running with a version of Cargo older than 1.71.
pub fn workspace_default_packages(&self) -> Option<Vec<&Package>> {
self.workspace_default_members
.as_ref()
.map(|workspace_default_members| {
self.packages
.iter()
.filter(|&p| workspace_default_members.contains(&p.id))
.collect()
})
/// # 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()
}
}

Expand All @@ -221,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<Vec<PackageId>>);

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]
Expand Down
20 changes: 17 additions & 3 deletions tests/selftest.rs
Expand Up @@ -135,9 +135,6 @@ fn metadata_deps() {
let workspace_packages = metadata.workspace_packages();
assert_eq!(workspace_packages.len(), 1);
assert_eq!(&workspace_packages[0].id, this_id);
if let Some(default_packages) = metadata.workspace_default_packages() {
assert_eq!(default_packages, workspace_packages);
}

let lib = this
.targets
Expand All @@ -164,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);
}
}
61 changes: 45 additions & 16 deletions tests/test_samples.rs
Expand Up @@ -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": [
{
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -188,7 +200,7 @@ fn all_the_fields() {
.unwrap();
assert_eq!(meta.workspace_root.file_name().unwrap(), "all");
assert_eq!(
serde_json::from_value::<WorkspaceMetadata>(meta.workspace_metadata).unwrap(),
serde_json::from_value::<WorkspaceMetadata>(meta.workspace_metadata.clone()).unwrap(),
WorkspaceMetadata {
testobject: TestObject {
myvalue: "abc".to_string()
Expand All @@ -198,9 +210,7 @@ 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, Some(meta.workspace_members));
} else {
assert_eq!(meta.workspace_default_members, None);
assert_eq!(&*meta.workspace_default_members, &meta.workspace_members);
}

assert_eq!(meta.packages.len(), 9);
Expand Down Expand Up @@ -456,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]
Expand Down Expand Up @@ -696,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;
}

0 comments on commit b181569

Please sign in to comment.