aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart/src/commands/show.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-04 14:48:25 +0900
committernsfisis <nsfisis@gmail.com>2026-05-04 15:44:11 +0900
commitd4309521f10b6f0090ef2548fb8f241949074e1f (patch)
tree3c7fa9049b34e73e9a73e21bf19f8a3674c54c57 /crates/mozart/src/commands/show.rs
parenta24d6e2f148417b32188cd1e643439a2858f4eac (diff)
downloadphp-mozart-d4309521f10b6f0090ef2548fb8f241949074e1f.tar.gz
php-mozart-d4309521f10b6f0090ef2548fb8f241949074e1f.tar.zst
php-mozart-d4309521f10b6f0090ef2548fb8f241949074e1f.zip
feat(show): expand license output with name, OSI flag, and URL
Mirror Composer's Command\ShowCommand::printLicenses(): emit one line per license identifier, expanded to "<full name> (<id>) [(OSI approved)] <url>" when the id is in the SPDX database, or just the id otherwise. The URL is built by mozart-spdx-licenses' new LicenseInfo::url() so the construction lives in the SPDX crate, like Composer's SpdxLicenses::getLicenseByIdentifier().
Diffstat (limited to 'crates/mozart/src/commands/show.rs')
-rw-r--r--crates/mozart/src/commands/show.rs118
1 files changed, 95 insertions, 23 deletions
diff --git a/crates/mozart/src/commands/show.rs b/crates/mozart/src/commands/show.rs
index c59a595..cf15bb2 100644
--- a/crates/mozart/src/commands/show.rs
+++ b/crates/mozart/src/commands/show.rs
@@ -671,10 +671,14 @@ fn show_installed_package_detail(
Verbosity::Normal,
);
- // License
- if let Some(licenses) = get_installed_license(pkg) {
+ // License — one line per identifier, matching Composer's printLicenses.
+ for license_id in get_installed_licenses(pkg) {
console.write_stdout(
- &format!("{} : {}", console_format!("<info>license</info>"), licenses),
+ &format!(
+ "{} : {}",
+ console_format!("<info>license</info>"),
+ format_license_for_show(&license_id),
+ ),
Verbosity::Normal,
);
}
@@ -1128,16 +1132,18 @@ fn show_locked_package_detail(
Verbosity::Normal,
);
- // License
+ // License — one line per identifier, matching Composer's printLicenses.
if let Some(ref licenses) = pkg.license {
- console.write_stdout(
- &format!(
- "{} : {}",
- console_format!("<info>license</info>"),
- licenses.join(", ")
- ),
- Verbosity::Normal,
- );
+ for license_id in licenses {
+ console.write_stdout(
+ &format!(
+ "{} : {}",
+ console_format!("<info>license</info>"),
+ format_license_for_show(license_id),
+ ),
+ Verbosity::Normal,
+ );
+ }
}
// Homepage
@@ -1276,7 +1282,11 @@ fn show_self(
);
if let Some(ref license) = root.license {
console.write_stdout(
- &format!("{} : {}", console_format!("<info>license</info>"), license),
+ &format!(
+ "{} : {}",
+ console_format!("<info>license</info>"),
+ format_license_for_show(license),
+ ),
Verbosity::Normal,
);
}
@@ -1875,18 +1885,35 @@ fn get_installed_keywords(pkg: &mozart_registry::installed::InstalledPackageEntr
.unwrap_or_default()
}
-/// Extract license from an InstalledPackageEntry's extra_fields.
-fn get_installed_license(
- pkg: &mozart_registry::installed::InstalledPackageEntry,
-) -> Option<String> {
- pkg.extra_fields.get("license").and_then(|v| {
- v.as_array().map(|arr| {
+/// Extract license identifiers from an InstalledPackageEntry's extra_fields.
+fn get_installed_licenses(pkg: &mozart_registry::installed::InstalledPackageEntry) -> Vec<String> {
+ pkg.extra_fields
+ .get("license")
+ .and_then(|v| v.as_array())
+ .map(|arr| {
arr.iter()
- .filter_map(|v| v.as_str())
- .collect::<Vec<_>>()
- .join(", ")
+ .filter_map(|v| v.as_str().map(String::from))
+ .collect()
})
- })
+ .unwrap_or_default()
+}
+
+/// Format a single license identifier for the `show` text output. Mirrors
+/// Composer's `Command\ShowCommand::printLicenses()`:
+/// * unknown id → just the id
+/// * OSI-approved → `<full name> (<id>) (OSI approved) <url>`
+/// * otherwise → `<full name> (<id>) <url>`
+fn format_license_for_show(license_id: &str) -> String {
+ match mozart_spdx_licenses::spdx().get_license_by_identifier(license_id) {
+ None => license_id.to_string(),
+ Some(info) if info.osi_approved => format!(
+ "{} ({}) (OSI approved) {}",
+ info.full_name,
+ license_id,
+ info.url(),
+ ),
+ Some(info) => format!("{} ({}) {}", info.full_name, license_id, info.url()),
+ }
}
/// Extract homepage from an InstalledPackageEntry's extra_fields.
@@ -1937,6 +1964,51 @@ fn normalize_version_simple(version: &str) -> String {
mod tests {
use super::*;
+ // ── format_license_for_show ─────────────────────────────────────────────
+
+ #[test]
+ fn test_format_license_for_show_osi_approved() {
+ let out = format_license_for_show("MIT");
+ assert!(
+ out.contains("MIT License") && out.contains("(MIT)") && out.contains("(OSI approved)"),
+ "got: {out}",
+ );
+ assert!(
+ out.contains("https://spdx.org/licenses/MIT.html#licenseText"),
+ "got: {out}",
+ );
+ }
+
+ #[test]
+ fn test_format_license_for_show_non_osi() {
+ // CC-BY-4.0 is in the SPDX list but is not OSI-approved.
+ let out = format_license_for_show("CC-BY-4.0");
+ assert!(
+ out.contains("(CC-BY-4.0)") && !out.contains("(OSI approved)"),
+ "got: {out}",
+ );
+ assert!(
+ out.contains("https://spdx.org/licenses/CC-BY-4.0.html#licenseText"),
+ "got: {out}",
+ );
+ }
+
+ #[test]
+ fn test_format_license_for_show_unknown_falls_back_to_id() {
+ assert_eq!(format_license_for_show("not-a-license"), "not-a-license");
+ }
+
+ #[test]
+ fn test_format_license_for_show_url_uses_canonical_id_casing() {
+ // Lookup is case-insensitive, but the URL uses the canonical id casing
+ // from the SPDX database — matching SpdxLicenses::getLicenseByIdentifier.
+ let out = format_license_for_show("mit");
+ assert!(
+ out.contains("https://spdx.org/licenses/MIT.html#licenseText"),
+ "got: {out}",
+ );
+ }
+
// ── format_version ──────────────────────────────────────────────────────
#[test]