diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-21 20:09:29 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-21 20:09:29 +0900 |
| commit | 28924141921856e12d77b5005e0d2567c8b17deb (patch) | |
| tree | e958a6c1058d518534293b69afa697e8382db214 /crates | |
| parent | f0dffe1511790cbd78929d37b8b22bd5e22b0bc3 (diff) | |
| download | php-mozart-28924141921856e12d77b5005e0d2567c8b17deb.tar.gz php-mozart-28924141921856e12d77b5005e0d2567c8b17deb.tar.zst php-mozart-28924141921856e12d77b5005e0d2567c8b17deb.zip | |
fix(licenses): handle root license as array and add ANSI color to header
composer.json allows license to be either a string or an array. Parse
the raw JSON to support both forms, emit license as a proper array in
JSON output, and apply console::comment() coloring to the text header.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/mozart/src/commands/licenses.rs | 121 |
1 files changed, 108 insertions, 13 deletions
diff --git a/crates/mozart/src/commands/licenses.rs b/crates/mozart/src/commands/licenses.rs index e68c469..6a5c295 100644 --- a/crates/mozart/src/commands/licenses.rs +++ b/crates/mozart/src/commands/licenses.rs @@ -56,7 +56,22 @@ pub fn execute(args: &LicensesArgs, cli: &super::Cli) -> anyhow::Result<()> { .and_then(|v| v.as_str()) .unwrap_or("No version set") .to_string(); - let root_license = root.license.clone().unwrap_or_else(|| "none".to_string()); + + // Parse root license as Vec<String>: composer.json allows either a string or an array. + let root_licenses: Vec<String> = { + // Read the raw JSON value so we can handle both string and array forms. + let raw_json = std::fs::read_to_string(&composer_json_path)?; + let raw_value: serde_json::Value = serde_json::from_str(&raw_json)?; + match raw_value.get("license") { + Some(serde_json::Value::String(s)) => vec![s.clone()], + Some(serde_json::Value::Array(arr)) => arr + .iter() + .filter_map(|v| v.as_str()) + .map(|s| s.to_string()) + .collect(), + _ => vec![], + } + }; // Load dependency entries let entries = if args.locked { @@ -67,9 +82,9 @@ pub fn execute(args: &LicensesArgs, cli: &super::Cli) -> anyhow::Result<()> { // Render output match format { - "json" => render_json(&root_name, &root_version, &root_license, &entries)?, + "json" => render_json(&root_name, &root_version, &root_licenses, &entries)?, "summary" => render_summary(&entries), - _ => render_text(&root_name, &root_version, &root_license, &entries), + _ => render_text(&root_name, &root_version, &root_licenses, &entries), } Ok(()) @@ -174,11 +189,21 @@ fn count_licenses(entries: &[LicenseEntry]) -> Vec<(String, usize)> { // ─── Rendering ─────────────────────────────────────────────────────────────── -fn render_text(root_name: &str, root_version: &str, root_license: &str, entries: &[LicenseEntry]) { +fn render_text( + root_name: &str, + root_version: &str, + root_licenses: &[String], + entries: &[LicenseEntry], +) { + let license_display = if root_licenses.is_empty() { + "none".to_string() + } else { + root_licenses.join(", ") + }; // Print root package header - println!("Name: {}", root_name); - println!("Version: {}", root_version); - println!("Licenses: {}", root_license); + println!("Name: {}", crate::console::comment(root_name)); + println!("Version: {}", crate::console::comment(root_version)); + println!("Licenses: {}", crate::console::comment(&license_display)); println!("Dependencies:"); println!(); @@ -210,14 +235,13 @@ fn render_text(root_name: &str, root_version: &str, root_license: &str, entries: fn render_json( root_name: &str, root_version: &str, - root_license: &str, + root_licenses: &[String], entries: &[LicenseEntry], ) -> anyhow::Result<()> { - let root_license_arr: Vec<serde_json::Value> = if root_license == "none" { - vec![] - } else { - vec![serde_json::Value::String(root_license.to_string())] - }; + let root_license_arr: Vec<serde_json::Value> = root_licenses + .iter() + .map(|s| serde_json::Value::String(s.clone())) + .collect(); let mut dependencies: serde_json::Map<String, serde_json::Value> = serde_json::Map::new(); for entry in entries { @@ -575,4 +599,75 @@ mod tests { let entries_all = load_locked_licenses(working_dir, false).unwrap(); assert_eq!(entries_all.len(), 2); } + + // ── Root license parsing ────────────────────────────────────────────────── + + #[test] + fn test_root_license_array_in_json() { + use tempfile::tempdir; + + let dir = tempdir().unwrap(); + let working_dir = dir.path(); + let composer_json_path = working_dir.join("composer.json"); + + // Write a composer.json where "license" is an array + std::fs::write( + &composer_json_path, + r#"{"name": "test/project", "license": ["MIT", "Apache-2.0"]}"#, + ) + .unwrap(); + + let raw_json = std::fs::read_to_string(&composer_json_path).unwrap(); + let raw_value: serde_json::Value = serde_json::from_str(&raw_json).unwrap(); + + let root_licenses: Vec<String> = match raw_value.get("license") { + Some(serde_json::Value::String(s)) => vec![s.clone()], + Some(serde_json::Value::Array(arr)) => arr + .iter() + .filter_map(|v| v.as_str()) + .map(|s| s.to_string()) + .collect(), + _ => vec![], + }; + + assert_eq!(root_licenses, vec!["MIT", "Apache-2.0"]); + } + + #[test] + fn test_render_json_root_license_is_array() { + let entries: Vec<LicenseEntry> = vec![]; + + // Single license string becomes a one-element array in JSON output + let root_licenses = vec!["MIT".to_string()]; + let root_license_arr: Vec<serde_json::Value> = root_licenses + .iter() + .map(|s| serde_json::Value::String(s.clone())) + .collect(); + let output = serde_json::json!({ + "name": "test/project", + "version": "1.0.0", + "license": root_license_arr, + "dependencies": {}, + }); + assert!(output["license"].is_array()); + assert_eq!(output["license"][0], "MIT"); + + // Multiple licenses are also emitted as an array + let root_licenses_multi = vec!["MIT".to_string(), "Apache-2.0".to_string()]; + let root_license_arr_multi: Vec<serde_json::Value> = root_licenses_multi + .iter() + .map(|s| serde_json::Value::String(s.clone())) + .collect(); + let output_multi = serde_json::json!({ + "name": "test/project", + "version": "1.0.0", + "license": root_license_arr_multi, + "dependencies": serde_json::json!({}), + }); + assert!(output_multi["license"].is_array()); + assert_eq!(output_multi["license"].as_array().unwrap().len(), 2); + + // Ensure the helper produces consistent results for empty entries + let _ = entries; + } } |
