diff --git a/Cargo.lock b/Cargo.lock index 52ae41c..5802eb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -95,6 +104,9 @@ name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +dependencies = [ + "backtrace", +] [[package]] name = "ascii" @@ -119,6 +131,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide 0.6.2", + "object", + "rustc-demangle", +] + [[package]] name = "base" version = "0.1.0" @@ -644,7 +671,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -787,6 +814,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "git2" version = "0.17.2" @@ -1244,6 +1277,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1303,6 +1345,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -1674,6 +1725,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" version = "0.37.19" diff --git a/cuddle_cli/Cargo.toml b/cuddle_cli/Cargo.toml index 8962ecb..0c46579 100644 --- a/cuddle_cli/Cargo.toml +++ b/cuddle_cli/Cargo.toml @@ -11,7 +11,7 @@ path = "src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.60" +anyhow = { version = "1.0.60", features = ["backtrace"] } serde = { version = "1.0.143", features = ["derive"] } serde_yaml = "0.9.4" walkdir = "2.3.2" diff --git a/cuddle_cli/src/cli/subcommands/init.rs b/cuddle_cli/src/cli/subcommands/init.rs index 6e64f6f..12848cb 100644 --- a/cuddle_cli/src/cli/subcommands/init.rs +++ b/cuddle_cli/src/cli/subcommands/init.rs @@ -1,7 +1,11 @@ +use std::collections::BTreeMap; use std::fs::{create_dir_all, read, read_dir}; use std::io::Write; +use std::mem::replace; +use std::path::PathBuf; use clap::{ArgMatches, Command}; +use walkdir::WalkDir; use crate::cli::CuddleCli; @@ -105,7 +109,7 @@ pub fn execute_init(exe_submatch: &ArgMatches, _cli: CuddleCli) -> anyhow::Resul } }; - let (_name, template_dir, _template) = template?; + let (_name, template_dir, mut template) = template?; let path = match path { Some(path) => path.clone(), @@ -128,21 +132,61 @@ pub fn execute_init(exe_submatch: &ArgMatches, _cli: CuddleCli) -> anyhow::Resul } } - for entry in read_dir(template_dir)? { + { + if let Some(ref mut prompt) = template.prompt { + for (name, prompt) in prompt { + let value = inquire::Text::new(&name) + .with_help_message(&prompt.description) + .prompt()?; + prompt.value = value; + } + } + } + + for entry in WalkDir::new(&template_dir).follow_links(false) { let entry = entry?; let entry_path = entry.path(); - let name = entry.file_name(); - if name == "cuddle-template.json" || name == "cuddle-templates.json" { - continue; + let new_path = PathBuf::from(&path).join(entry_path.strip_prefix(&template_dir)?); + let new_path = replace_with_variables(&new_path.to_string_lossy().to_string(), &template)?; + let new_path = PathBuf::from(new_path); + + if entry_path.is_dir() { + create_dir_all(&new_path)?; } - std::fs::rename(entry_path, std::path::PathBuf::from(&path).join(name))?; + if entry_path.is_file() { + let name = entry.file_name(); + if let Some(parent) = entry_path.parent() { + create_dir_all(parent)?; + } + + if name == "cuddle-template.json" || name == "cuddle-templates.json" { + continue; + } + + tracing::info!("writing to: {}", new_path.display()); + let new_content = + replace_with_variables(&std::fs::read_to_string(entry_path)?, &template)?; + + std::fs::write(new_path, new_content.as_bytes())?; + } } Ok(()) } +fn replace_with_variables(content: &str, template: &CuddleTemplate) -> anyhow::Result { + let mut content = content.to_string(); + if let Some(prompt) = &template.prompt { + for (name, value) in prompt { + content = content.replace(&format!("%%{}%%", name), &value.value); + } + } + + Ok(content) +} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] struct CuddleTemplates { pub templates: Vec, @@ -151,4 +195,12 @@ struct CuddleTemplates { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] struct CuddleTemplate { pub name: String, + pub prompt: Option>, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +struct CuddleTemplatePrompt { + pub description: String, + #[serde(skip)] + pub value: String, }