new: let an explicit --lang win over build-system detection
The wizard overwrote cli.lang with the detected ecosystem even when the user passed --lang, and re-asked the language question in the ambiguous and skeleton cases despite the documented 'flag > detected > default' merge order. The flag now short-circuits the language step entirely (detection stays informational); behavior without the flag is unchanged.
This commit is contained in:
+123
-20
@@ -122,10 +122,30 @@ async fn run_wizard(mut cli: NewCli) -> Result<NewOptions, Box<dyn Error>> {
|
||||
cli.name = Some(answer);
|
||||
}
|
||||
|
||||
// 2. Language / build system.
|
||||
let mut preselected: Option<TemplateId> = None;
|
||||
match &detection {
|
||||
Detection::Single(id) if detection_decides => {
|
||||
// 2. Language / build system. An explicit `--lang` wins over any
|
||||
// detection (flag > detected/probe > default): it is never
|
||||
// overwritten and the question is never re-asked — the detection is
|
||||
// only logged for information.
|
||||
match language_choice(cli.lang.as_deref(), &detection, detection_decides) {
|
||||
LanguageChoice::Flag => match &detection {
|
||||
Detection::Single(id) => log::info!(
|
||||
"Detected: {} project in {}; --lang takes precedence",
|
||||
id.display_name(),
|
||||
detect_dir.display()
|
||||
),
|
||||
Detection::Ambiguous(candidates) => log::info!(
|
||||
"Several build systems found in {} ({}); --lang takes \
|
||||
precedence",
|
||||
detect_dir.display(),
|
||||
candidates
|
||||
.iter()
|
||||
.map(|id| id.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Detection::Empty => {}
|
||||
},
|
||||
LanguageChoice::Detected(id) => {
|
||||
log::info!(
|
||||
"Detected: {} project in {}",
|
||||
id.display_name(),
|
||||
@@ -133,12 +153,16 @@ async fn run_wizard(mut cli: NewCli) -> Result<NewOptions, Box<dyn Error>> {
|
||||
);
|
||||
cli.lang = Some(id.as_str().to_string());
|
||||
}
|
||||
Detection::Single(id) => {
|
||||
// A skeleton was asked for: still ask, preselecting the
|
||||
// detected ecosystem.
|
||||
preselected = Some(*id);
|
||||
LanguageChoice::Ask(preselected) => {
|
||||
let menu = language_menu(&[]);
|
||||
let default = preselected
|
||||
.unwrap_or(TemplateId::Empty)
|
||||
.display_name()
|
||||
.to_string();
|
||||
let id = select_template(&menu, &default)?;
|
||||
cli.lang = Some(id.as_str().to_string());
|
||||
}
|
||||
Detection::Ambiguous(candidates) => {
|
||||
LanguageChoice::Ambiguous(candidates) => {
|
||||
log::info!(
|
||||
"Several build systems found in {} ({}): candidates listed \
|
||||
first, the highest-precedence one preselected",
|
||||
@@ -149,20 +173,10 @@ async fn run_wizard(mut cli: NewCli) -> Result<NewOptions, Box<dyn Error>> {
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
let menu = language_menu(candidates);
|
||||
let menu = language_menu(&candidates);
|
||||
let id = select_template(&menu, &menu[0])?;
|
||||
cli.lang = Some(id.as_str().to_string());
|
||||
}
|
||||
Detection::Empty => {}
|
||||
}
|
||||
if cli.lang.is_none() {
|
||||
let menu = language_menu(&[]);
|
||||
let default = preselected
|
||||
.unwrap_or(TemplateId::Empty)
|
||||
.display_name()
|
||||
.to_string();
|
||||
let id = select_template(&menu, &default)?;
|
||||
cli.lang = Some(id.as_str().to_string());
|
||||
}
|
||||
let template = TemplateId::parse(cli.lang.as_deref().unwrap_or_default())?;
|
||||
|
||||
@@ -690,6 +704,42 @@ fn language_menu(candidates: &[TemplateId]) -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// What the language step does with the detection result: how an explicit
|
||||
/// `--lang` flag combines with it (flag > detected/probe > default).
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum LanguageChoice {
|
||||
/// An explicit `--lang` wins: it is kept as-is, the question is never
|
||||
/// re-asked and the detection stays informational.
|
||||
Flag,
|
||||
/// No flag, confident detection, packaging the detected directory: the
|
||||
/// detection decides.
|
||||
Detected(TemplateId),
|
||||
/// Ask the question, preselecting the detected ecosystem (a skeleton
|
||||
/// was asked for); `None` when nothing was detected.
|
||||
Ask(Option<TemplateId>),
|
||||
/// Ask the question with the ambiguous candidates listed first (the
|
||||
/// highest-precedence one preselected).
|
||||
Ambiguous(Vec<TemplateId>),
|
||||
}
|
||||
|
||||
/// The language step's decision for one wizard run. With the flag absent
|
||||
/// this mirrors the historical behavior exactly; the flag always wins.
|
||||
fn language_choice(
|
||||
flag: Option<&str>,
|
||||
detection: &Detection,
|
||||
detection_decides: bool,
|
||||
) -> LanguageChoice {
|
||||
if flag.is_some() {
|
||||
return LanguageChoice::Flag;
|
||||
}
|
||||
match detection {
|
||||
Detection::Single(id) if detection_decides => LanguageChoice::Detected(*id),
|
||||
Detection::Single(id) => LanguageChoice::Ask(Some(*id)),
|
||||
Detection::Ambiguous(candidates) => LanguageChoice::Ambiguous(candidates.clone()),
|
||||
Detection::Empty => LanguageChoice::Ask(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// The license menu: the curated SPDX list plus the free-text entry.
|
||||
fn license_menu() -> Vec<String> {
|
||||
KNOWN_LICENSES
|
||||
@@ -1043,6 +1093,59 @@ mod tests {
|
||||
assert_eq!(unique.len(), menu.len());
|
||||
}
|
||||
|
||||
/// An explicit `--lang` wins over any detection (the flag outranks the
|
||||
/// detection and the defaults): the flag is kept and the question is
|
||||
/// never re-asked, whatever the detection found (regression: a
|
||||
/// confident detection used to overwrite the flag and an ambiguous one
|
||||
/// re-asked the question).
|
||||
#[test]
|
||||
fn language_choice_flag_wins_over_detection() {
|
||||
let detections = [
|
||||
Detection::Single(Tid::Rust),
|
||||
Detection::Ambiguous(vec![Tid::Rust, Tid::Python]),
|
||||
Detection::Empty,
|
||||
];
|
||||
for detection in &detections {
|
||||
for decides in [false, true] {
|
||||
assert_eq!(
|
||||
language_choice(Some("python"), detection, decides),
|
||||
LanguageChoice::Flag,
|
||||
"detection {detection:?}, decides {decides}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Without the flag the detection behaves exactly as before: it decides
|
||||
/// for a confident detection (packaging the detected directory), asks
|
||||
/// with a preselection for a skeleton run, lists the candidates first
|
||||
/// when ambiguous, and falls back to the plain menu otherwise.
|
||||
#[test]
|
||||
fn language_choice_without_flag_follows_detection() {
|
||||
assert_eq!(
|
||||
language_choice(None, &Detection::Single(Tid::Rust), true),
|
||||
LanguageChoice::Detected(Tid::Rust)
|
||||
);
|
||||
// Skeleton run: ask, preselecting the detected ecosystem.
|
||||
assert_eq!(
|
||||
language_choice(None, &Detection::Single(Tid::Rust), false),
|
||||
LanguageChoice::Ask(Some(Tid::Rust))
|
||||
);
|
||||
assert_eq!(
|
||||
language_choice(
|
||||
None,
|
||||
&Detection::Ambiguous(vec![Tid::Go, Tid::Python]),
|
||||
true
|
||||
),
|
||||
LanguageChoice::Ambiguous(vec![Tid::Go, Tid::Python])
|
||||
);
|
||||
// Nothing detected: plain menu, the empty template preselected.
|
||||
assert_eq!(
|
||||
language_choice(None, &Detection::Empty, false),
|
||||
LanguageChoice::Ask(None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn license_menu_and_defaults() {
|
||||
let menu = license_menu();
|
||||
|
||||
Reference in New Issue
Block a user