Skip to content

Commit

Permalink
new: Add custom template filters. (#309)
Browse files Browse the repository at this point in the history
* Add filters.

* Add tests.

* Add logging.

* Fill out docs.

* Get prism working.
  • Loading branch information
milesj committed Sep 14, 2022
1 parent 07615f5 commit c12d57c
Show file tree
Hide file tree
Showing 18 changed files with 387 additions and 34 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

115 changes: 89 additions & 26 deletions crates/cli/src/commands/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use dialoguer::{theme::Theme, Confirm, Input, MultiSelect, Select};
use moon_config::TemplateVariable;
use moon_error::MoonError;
use moon_generator::{FileState, Generator, GeneratorError, Template, TemplateContext};
use moon_logger::{color, warn};
use moon_logger::{color, debug, map_list, trace, warn};
use moon_terminal::create_theme;
use moon_utils::path;
use std::collections::HashMap;
use std::env;
use std::fmt::Display;
use std::path::PathBuf;

const LOG_TARGET: &str = "moon:generate";
Expand All @@ -23,21 +24,45 @@ pub struct GenerateOptions {
pub vars: Vec<String>,
}

fn format_var_name(name: &str) -> String {
if name.starts_with("no-") {
name.strip_prefix("no-").unwrap().to_owned()
} else {
name.to_owned()
}
fn log_var<T: Display>(name: &str, value: &T, comment: Option<&str>) {
trace!(
target: LOG_TARGET,
"Setting variable {} to \"{}\" {}",
color::id(name),
value,
comment.unwrap_or_default(),
);
}

fn parse_var_args(vars: &[String]) -> HashMap<String, String> {
let mut custom_vars = HashMap::new();

if vars.is_empty() {
return custom_vars;
}

debug!(
target: LOG_TARGET,
"Inheriting variable values from provided command line arguments"
);

let lexer = clap_lex::RawArgs::new(vars);
let mut cursor = lexer.cursor();
let mut previous_name: Option<String> = None;

let mut set_var = |name: &str, value: String| {
let name = if let Some(stripped_name) = name.strip_prefix("no-") {
stripped_name
} else {
name
};
let comment = color::muted_light(format!("(from --{})", name));

log_var(name, &value, Some(&comment));

custom_vars.insert(name.to_owned(), value);
};

while let Some(arg) = lexer.next(&mut cursor) {
// --name, --name=value
if let Some((long, maybe_value)) = arg.to_long() {
Expand All @@ -46,8 +71,8 @@ fn parse_var_args(vars: &[String]) -> HashMap<String, String> {
// If we found another long arg, but one previously exists,
// this must be a boolean value!
if let Some(name) = &previous_name {
custom_vars.insert(
format_var_name(name),
set_var(
name,
if name.starts_with("no-") {
"false".to_owned()
} else {
Expand All @@ -60,10 +85,7 @@ fn parse_var_args(vars: &[String]) -> HashMap<String, String> {
if let Some(value) = maybe_value {
previous_name = None;

custom_vars.insert(
format_var_name(name),
value.to_str().unwrap_or_default().to_owned(),
);
set_var(name, value.to_str().unwrap_or_default().to_owned());

// No value defined, so persist the name till the next iteration
} else {
Expand Down Expand Up @@ -91,8 +113,8 @@ fn parse_var_args(vars: &[String]) -> HashMap<String, String> {
} else if let Some(name) = previous_name {
previous_name = None;

custom_vars.insert(
format_var_name(&name),
set_var(
&name,
arg.to_value_os().to_str().unwrap_or_default().to_owned(),
);
}
Expand All @@ -109,6 +131,12 @@ fn gather_variables(
let mut context = TemplateContext::new();
let custom_vars = parse_var_args(&options.vars);
let error_handler = |e| GeneratorError::Moon(MoonError::Io(e));
let default_comment = color::muted_light("(defaults)");

debug!(
target: LOG_TARGET,
"Declaring variable values from defaults and user prompts"
);

for (name, config) in &template.config.variables {
match config {
Expand All @@ -119,6 +147,8 @@ fn gather_variables(
};

if options.defaults || var.prompt.is_none() {
log_var(name, &default, Some(&default_comment));

context.insert(name, &default);
} else {
let value = Confirm::with_theme(theme)
Expand All @@ -128,6 +158,8 @@ fn gather_variables(
.interact()
.map_err(error_handler)?;

log_var(name, &value, None);

context.insert(name, &value);
}
}
Expand All @@ -139,6 +171,10 @@ fn gather_variables(
.position(|i| i == default)
.unwrap_or_default();

if options.defaults {
log_var(name, &var.values[default_index], Some(&default_comment));
}

match (options.defaults, var.multiple.unwrap_or_default()) {
(true, true) => {
context.insert(name, &[&var.values[default_index]]);
Expand All @@ -159,14 +195,14 @@ fn gather_variables(
)
.interact()
.map_err(error_handler)?;
let value = indexes
.iter()
.map(|i| var.values[*i].clone())
.collect::<Vec<String>>();

context.insert(
name,
&indexes
.iter()
.map(|i| var.values[*i].clone())
.collect::<Vec<String>>(),
);
log_var(name, &map_list(&value, |f| f.to_string()), None);

context.insert(name, &value);
}
(false, false) => {
let index = Select::with_theme(theme)
Expand All @@ -176,6 +212,8 @@ fn gather_variables(
.interact()
.map_err(error_handler)?;

log_var(name, &var.values[index], None);

context.insert(name, &var.values[index]);
}
};
Expand All @@ -190,6 +228,8 @@ fn gather_variables(
};

if options.defaults || var.prompt.is_none() {
log_var(name, &default, Some(&default_comment));

context.insert(name, &default);
} else {
let value: i32 = Input::with_theme(theme)
Expand All @@ -207,6 +247,8 @@ fn gather_variables(
.interact_text()
.map_err(error_handler)?;

log_var(name, &value, None);

context.insert(name, &value);
}
}
Expand All @@ -215,6 +257,8 @@ fn gather_variables(
let default = custom_vars.get(name).unwrap_or(&var.default);

if options.defaults || var.prompt.is_none() {
log_var(name, &default, Some(&default_comment));

context.insert(name, &default);
} else {
let value: String = Input::with_theme(theme)
Expand All @@ -232,6 +276,8 @@ fn gather_variables(
.interact_text()
.map_err(error_handler)?;

log_var(name, &value, None);

context.insert(name, &value);
}
}
Expand Down Expand Up @@ -263,6 +309,10 @@ pub async fn generate(
return Ok(());
}

if options.dry_run {
debug!(target: LOG_TARGET, "Running in DRY MODE");
}

// Create the template instance
let mut template = generator.load_template(name).await?;
let term = Term::buffered_stdout();
Expand All @@ -284,13 +334,26 @@ pub async fn generate(
// Determine the destination path
let relative_dest = match &options.dest {
Some(d) => d.clone(),
None => Input::with_theme(&theme)
.with_prompt("Where to generate code to?")
.allow_empty(false)
.interact_text()?,
None => {
trace!(
target: LOG_TARGET,
"Destination path not provided, prompting the user"
);

Input::with_theme(&theme)
.with_prompt("Where to generate code to?")
.allow_empty(false)
.interact_text()?
}
};
let dest = path::normalize(cwd.join(&relative_dest));

debug!(
target: LOG_TARGET,
"Destination path set to {}",
color::path(&dest)
);

// Gather variables and build context
let context = gather_variables(&template, &theme, &options)?;

Expand Down
16 changes: 16 additions & 0 deletions crates/cli/tests/generate_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,19 @@ fn errors_when_parsing_custom_var_types() {

assert_snapshot!(get_path_safe_output(&assert));
}

#[test]
fn supports_custom_filters() {
let fixture = create_sandbox("generator");

let assert = create_moon_command(fixture.path())
.arg("generate")
.arg("vars")
.arg("./test")
.arg("--defaults")
.assert();

assert.success();

assert_snapshot!(fs::read_to_string(fixture.path().join("./test/filters.txt")).unwrap());
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: crates/cli/tests/generate_test.rs
assertion_line: 143
assertion_line: 174
expression: get_path_safe_output(&assert)
---

Expand All @@ -11,6 +11,7 @@ A template for testing all variable config combinations.
created --> ./test/control.txt
created --> ./test/expressions.txt
created --> ./test/file-default-0.txt
created --> ./test/filters.txt



Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ A template for testing all variable config combinations.
replaced -> ./test/control.txt
replaced -> ./test/expressions.txt
replaced -> ./test/file-default-0.txt
replaced -> ./test/filters.txt



Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
source: crates/cli/tests/generate_test.rs
assertion_line: 209
expression: "fs::read_to_string(fixture.path().join(\"./test/filters.txt\")).unwrap()"
---
camel_case = someRandomValue
kebab_case = some-random-value
pascal_case = SomeRandomValue
snake_case = some_random_value
upper_kebab_case = SOME-RANDOM-VALUE
upper_snake_case = SOME_RANDOM_VALUE

3 changes: 3 additions & 0 deletions crates/generator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ edition = "2021"
moon_config = { path = "../config" }
moon_constants = { path = "../constants" }
moon_error = { path = "../error" }
moon_logger = { path = "../logger" }
moon_utils = { path = "../utils" }
convert_case = "0.6.0"
futures = "0.3.24"
lazy_static = "1.4.0"
serde_json = "1.0.85"
tera = { version = "1.17.0", features = ["preserve_order"] }
thiserror = "1.0.33"

Expand Down
Loading

0 comments on commit c12d57c

Please sign in to comment.