Skip to content

Commit 0de8809

Browse files
authored
Merge pull request #3052 from itowlson/templates-choice
Allow "choose from list" parameters in templates
2 parents 1d68c9d + dba6f92 commit 0de8809

File tree

4 files changed

+35
-3
lines changed

4 files changed

+35
-3
lines changed

crates/templates/src/constraints.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use regex::Regex;
33
#[derive(Clone, Debug)]
44
pub(crate) struct StringConstraints {
55
pub regex: Option<Regex>,
6+
pub allowed_values: Option<Vec<String>>,
67
}
78

89
impl StringConstraints {
@@ -12,6 +13,15 @@ impl StringConstraints {
1213
anyhow::bail!("Input '{}' does not match pattern '{}'", text, regex);
1314
}
1415
}
16+
if let Some(allowed_values) = self.allowed_values.as_ref() {
17+
if !allowed_values.contains(&text) {
18+
anyhow::bail!(
19+
"Input '{}' is not one of the allowed values ({})",
20+
text,
21+
allowed_values.join(", ")
22+
);
23+
}
24+
}
1525
Ok(text)
1626
}
1727
}

crates/templates/src/interaction.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88

99
use anyhow::anyhow;
1010
// use console::style;
11-
use dialoguer::{Confirm, Input};
11+
use dialoguer::{Confirm, Input, Select};
1212

1313
pub(crate) trait InteractionStrategy {
1414
fn allow_generate_into(&self, target_dir: &Path) -> Cancellable<(), anyhow::Error>;
@@ -111,7 +111,10 @@ pub(crate) fn prompt_parameter(parameter: &TemplateParameter) -> Option<String>
111111

112112
loop {
113113
let input = match parameter.data_type() {
114-
TemplateParameterDataType::String(_) => ask_free_text(prompt, default_value),
114+
TemplateParameterDataType::String(constraints) => match &constraints.allowed_values {
115+
Some(allowed_values) => ask_choice(prompt, default_value, allowed_values),
116+
None => ask_free_text(prompt, default_value),
117+
},
115118
};
116119

117120
match input {
@@ -138,6 +141,21 @@ fn ask_free_text(prompt: &str, default_value: &Option<String>) -> anyhow::Result
138141
Ok(result)
139142
}
140143

144+
fn ask_choice(
145+
prompt: &str,
146+
default_value: &Option<String>,
147+
allowed_values: &[String],
148+
) -> anyhow::Result<String> {
149+
let mut select = Select::new().with_prompt(prompt).items(allowed_values);
150+
if let Some(s) = default_value {
151+
if let Some(default_index) = allowed_values.iter().position(|item| item == s) {
152+
select = select.default(default_index);
153+
}
154+
}
155+
let selected_index = select.interact()?;
156+
Ok(allowed_values[selected_index].clone())
157+
}
158+
141159
fn is_directory_empty(path: &Path) -> bool {
142160
if !path.exists() {
143161
return true;

crates/templates/src/reader.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ pub(crate) struct RawParameter {
8686
#[serde(rename = "default")]
8787
pub default_value: Option<String>,
8888
pub pattern: Option<String>,
89+
pub allowed_values: Option<Vec<String>>,
8990
}
9091

9192
#[derive(Debug, Deserialize)]

crates/templates/src/template.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,10 @@ impl Condition {
617617
fn parse_string_constraints(raw: &RawParameter) -> anyhow::Result<StringConstraints> {
618618
let regex = raw.pattern.as_ref().map(|re| Regex::new(re)).transpose()?;
619619

620-
Ok(StringConstraints { regex })
620+
Ok(StringConstraints {
621+
regex,
622+
allowed_values: raw.allowed_values.clone(),
623+
})
621624
}
622625

623626
fn read_install_record(layout: &TemplateLayout) -> InstalledFrom {

0 commit comments

Comments
 (0)