new: drive the license menu, parsing and sniffing from data/licenses.yml

This commit is contained in:
2026-09-18 13:52:24 +02:00
parent 12407c8eac
commit dd1c70c91a
6 changed files with 496 additions and 100 deletions
+19 -29
View File
@@ -24,6 +24,7 @@ use indicatif::MultiProgress;
use crate::new::detect::{self, Detection};
use crate::new::git;
use crate::new::licenses;
use crate::new::options::{self, NewCli, NewOptions, SourceDir, SourceFormat, TemplateId};
use crate::new::origin::GitOrigin;
use crate::new::templates::{self, ProbeResult, ScaffoldOutcome};
@@ -39,20 +40,6 @@ const SOURCE_PATH: &str = "Package the sources in another directory…";
/// The "everything else" entry of the license menu.
const LICENSE_OTHER: &str = "Other (enter a SPDX identifier)";
/// The curated SPDX identifiers of the license menu (without the free-text
/// entry), matching [`options::License::parse`]'s known spellings.
pub const KNOWN_LICENSES: [&str; 9] = [
"MIT",
"Apache-2.0",
"GPL-2.0+",
"GPL-3.0+",
"LGPL-2.1+",
"LGPL-3.0+",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
];
/// Labels of the interactive `select` questions. `prompt::select` renders
/// `> <label><answer>` verbatim — unlike [`prompt::text`], it appends no
/// formatting of its own — so each label carries its own separator:
@@ -783,29 +770,32 @@ fn language_choice(
}
}
/// The license menu: the curated SPDX list plus the free-text entry.
/// The license menu: the `menu` labels of the bundled license table
/// (`data/licenses.yml`) plus the free-text entry.
fn license_menu() -> Vec<String> {
KNOWN_LICENSES
licenses::entries()
.iter()
.copied()
.map(str::to_string)
.map(|entry| entry.menu.clone())
.chain(std::iter::once(LICENSE_OTHER.to_string()))
.collect()
}
/// The defaults of the license question for a probed SPDX identifier: the
/// matching curated entry (case-insensitive) is preselected; anything else
/// preselects the free-text entry prefilled with the probe. Without a probe
/// the curated list defaults to MIT.
/// matching curated entry (case-insensitive over the menu labels) is
/// preselected; anything else preselects the free-text entry prefilled with
/// the probe. Without a probe the curated list defaults to MIT (the first
/// menu entry of the bundled table).
fn license_question_default(probe_license: Option<&str>) -> (String, String) {
match probe_license {
Some(license) => match KNOWN_LICENSES
.iter()
.find(|k| k.eq_ignore_ascii_case(license))
{
Some(known) => ((*known).to_string(), String::new()),
None => (LICENSE_OTHER.to_string(), license.to_string()),
},
Some(license) => {
match licenses::entries()
.iter()
.find(|entry| entry.menu.eq_ignore_ascii_case(license))
{
Some(known) => (known.menu.clone(), String::new()),
None => (LICENSE_OTHER.to_string(), license.to_string()),
}
}
None => ("MIT".to_string(), String::new()),
}
}
@@ -1237,7 +1227,7 @@ mod tests {
#[test]
fn license_menu_and_defaults() {
let menu = license_menu();
assert_eq!(menu.len(), KNOWN_LICENSES.len() + 1);
assert_eq!(menu.len(), licenses::entries().len() + 1);
assert_eq!(menu[0], "MIT");
assert_eq!(menu.last().unwrap(), LICENSE_OTHER);