added auto discovery and bumped version

This commit is contained in:
2026-03-17 09:19:36 +01:00
parent 68cd6543b3
commit 7b2217886c
3 changed files with 141 additions and 12 deletions

View File

@@ -12,8 +12,8 @@ use std::path::PathBuf;
)]
pub struct Cli {
/// The input template file (e.g., .env.example, config.json.template, docker-compose.yml)
#[arg(required = true, value_name = "INPUT_FILE")]
pub input: PathBuf,
#[arg(required = false, value_name = "INPUT_FILE")]
pub input: Option<PathBuf>,
/// Optional output file. If not provided, it will be inferred.
#[arg(short, long, value_name = "OUTPUT_FILE")]

View File

@@ -26,7 +26,8 @@ use ratatui::{Terminal, backend::CrosstermBackend};
fn determine_output_path(input: &Path) -> PathBuf {
let file_name = input.file_name().unwrap_or_default().to_string_lossy();
if file_name == ".env.example" {
// Standard mappings
if file_name == ".env.example" || file_name == ".env.template" {
return input.with_file_name(".env");
}
@@ -37,11 +38,36 @@ fn determine_output_path(input: &Path) -> PathBuf {
return input.with_file_name("compose.override.yaml");
}
if file_name.ends_with(".example.json") {
return input.with_file_name(file_name.replace(".example.json", ".json"));
// Pattern-based mappings
if let Some(base) = file_name.strip_suffix(".env.example") {
return input.with_file_name(format!("{}.env", base));
}
if file_name.ends_with(".template.json") {
return input.with_file_name(file_name.replace(".template.json", ".json"));
if let Some(base) = file_name.strip_suffix(".env.template") {
return input.with_file_name(format!("{}.env", base));
}
if let Some(base) = file_name.strip_suffix(".example.json") {
return input.with_file_name(format!("{}.json", base));
}
if let Some(base) = file_name.strip_suffix(".template.json") {
return input.with_file_name(format!("{}.json", base));
}
if let Some(base) = file_name.strip_suffix(".example.yml") {
return input.with_file_name(format!("{}.yml", base));
}
if let Some(base) = file_name.strip_suffix(".template.yml") {
return input.with_file_name(format!("{}.yml", base));
}
if let Some(base) = file_name.strip_suffix(".example.yaml") {
return input.with_file_name(format!("{}.yaml", base));
}
if let Some(base) = file_name.strip_suffix(".template.yaml") {
return input.with_file_name(format!("{}.yaml", base));
}
if let Some(base) = file_name.strip_suffix(".example.toml") {
return input.with_file_name(format!("{}.toml", base));
}
if let Some(base) = file_name.strip_suffix(".template.toml") {
return input.with_file_name(format!("{}.toml", base));
}
input.with_extension(format!(
@@ -50,6 +76,60 @@ fn determine_output_path(input: &Path) -> PathBuf {
))
}
/// Discovers common configuration template files in the current directory.
fn find_input_file() -> Option<PathBuf> {
let candidates = [
".env.example",
"compose.yml",
"docker-compose.yml",
".env.template",
"compose.yaml",
"docker-compose.yaml",
];
// Priority 1: Exact matches for well-known defaults
for name in &candidates {
let path = PathBuf::from(name);
if path.exists() {
return Some(path);
}
}
// Priority 2: Pattern matches
if let Ok(entries) = std::fs::read_dir(".") {
let mut fallback = None;
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
if name_str.ends_with(".env.example")
|| name_str.ends_with(".env.template")
|| name_str.ends_with(".example.json")
|| name_str.ends_with(".template.json")
|| name_str.ends_with(".example.yml")
|| name_str.ends_with(".template.yml")
|| name_str.ends_with(".example.yaml")
|| name_str.ends_with(".template.yaml")
|| name_str.ends_with(".example.toml")
|| name_str.ends_with(".template.toml")
{
// Prefer .env.* or compose.* if multiple matches
if name_str.contains(".env") || name_str.contains("compose") {
return Some(entry.path());
}
if fallback.is_none() {
fallback = Some(entry.path());
}
}
}
if let Some(path) = fallback {
return Some(path);
}
}
None
}
fn main() -> anyhow::Result<()> {
let args = cli::parse();
@@ -64,11 +144,27 @@ fn main() -> anyhow::Result<()> {
.format_timestamp(None)
.init();
let input_path = args.input;
if !input_path.exists() {
error!("Input file not found: {}", input_path.display());
return Err(MouldError::FileNotFound(input_path.display().to_string()).into());
}
let input_path = match args.input {
Some(path) => {
if !path.exists() {
error!("Input file not found: {}", path.display());
return Err(MouldError::FileNotFound(path.display().to_string()).into());
}
path
}
None => match find_input_file() {
Some(path) => {
info!("Discovered template: {}", path.display());
path
}
None => {
error!("No template file provided and none discovered in current directory.");
println!("Usage: mould <INPUT_FILE>");
println!("Supported defaults: .env.example, compose.yml, docker-compose.yml, etc.");
return Err(MouldError::FileNotFound("None".to_string()).into());
}
},
};
info!("Input: {}", input_path.display());