diff --git a/Cargo.lock b/Cargo.lock index 0d25561..d23cf31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,7 +179,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "codeowners" -version = "0.3.0" +version = "0.3.1" dependencies = [ "assert_cmd", "clap", diff --git a/Cargo.toml b/Cargo.toml index e32dbb8..d7033f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codeowners" -version = "0.3.0" +version = "0.3.1" edition = "2024" [profile.release] diff --git a/src/project.rs b/src/project.rs index c09b247..1ad5063 100644 --- a/src/project.rs +++ b/src/project.rs @@ -111,6 +111,7 @@ pub mod deserializers { #[derive(Deserialize)] pub struct RubyPackage { pub owner: Option, + pub metadata: Option, } #[derive(Deserialize)] diff --git a/src/project_builder.rs b/src/project_builder.rs index 1945b0a..8d96529 100644 --- a/src/project_builder.rs +++ b/src/project_builder.rs @@ -309,7 +309,17 @@ fn ruby_package_owner(path: &Path) -> Result, Error> { let file = File::open(path).change_context(Error::Io)?; let deserializer: deserializers::RubyPackage = serde_yaml::from_reader(file).change_context(Error::SerdeYaml)?; - Ok(deserializer.owner) + let top_level_owner = deserializer.owner; + let metadata_owner = deserializer.metadata.and_then(|metadata| metadata.owner); + + // Error if both are present to avoid ambiguity + match (top_level_owner.as_ref(), metadata_owner.as_ref()) { + (Some(_), Some(_)) => Err(error_stack::report!(Error::Io).attach_printable(format!( + "Package at {} has both 'owner' and 'metadata.owner' defined. Please use only one.", + path.display() + ))), + _ => Ok(top_level_owner.or(metadata_owner)), + } } fn javascript_package_owner(path: &Path) -> Result, Error> { @@ -334,4 +344,44 @@ mod tests { fn test_glob_match() { assert!(glob_match(OWNED_GLOB, "script/.eslintrc.js")); } + + #[test] + fn test_ruby_package_owner_top_level() { + let yaml = "owner: TeamA\n"; + let temp_file = tempfile::NamedTempFile::new().unwrap(); + std::fs::write(temp_file.path(), yaml).unwrap(); + + let owner = ruby_package_owner(temp_file.path()).unwrap(); + assert_eq!(owner, Some("TeamA".to_string())); + } + + #[test] + fn test_ruby_package_owner_metadata() { + let yaml = "metadata:\n owner: TeamB\n"; + let temp_file = tempfile::NamedTempFile::new().unwrap(); + std::fs::write(temp_file.path(), yaml).unwrap(); + + let owner = ruby_package_owner(temp_file.path()).unwrap(); + assert_eq!(owner, Some("TeamB".to_string())); + } + + #[test] + fn test_ruby_package_owner_errors_when_both_present() { + let yaml = "owner: TeamA\nmetadata:\n owner: TeamB\n"; + let temp_file = tempfile::NamedTempFile::new().unwrap(); + std::fs::write(temp_file.path(), yaml).unwrap(); + + let result = ruby_package_owner(temp_file.path()); + assert!(result.is_err()); + } + + #[test] + fn test_ruby_package_owner_no_owner() { + let yaml = "name: my_package\n"; + let temp_file = tempfile::NamedTempFile::new().unwrap(); + std::fs::write(temp_file.path(), yaml).unwrap(); + + let owner = ruby_package_owner(temp_file.path()).unwrap(); + assert_eq!(owner, None); + } }