//! Template creation domain logic
use ggen_utils::error::Result;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
/// Generate template content based on template type
pub fn generate_template_content(name: &str, template_type: &str) -> Result<String> {
let timestamp = chrono::Utc::now().to_rfc3339();
let content = match template_type {
"rust" => format!(
"---\nto: src/{{{{ name }}}}.rs\nvars:\n name: \"{name}\"\n author: \"{{{{ author }}}}\"\nrdf:\n sources: []\nsparql:\n queries: {{}}\ndeterminism:\n seed: \"{timestamp}\"\n---\n\n// Generated Rust module: {name}\n// Author: {{{{ author }}}}\n// Generated at: {timestamp}\n\npub struct {{{{ name | pascal_case }}}} {{\n pub id: String,\n pub name: String,\n}}\n\nimpl {{{{ name | pascal_case }}}} {{\n pub fn new(name: String) -> Self {{\n Self {{\n id: uuid::Uuid::new_v4().to_string(),\n name,\n }}\n }}\n}}\n"
),
"python" => format!(
"---\nto: src/{{{{ name }}}}.py\nvars:\n name: \"{name}\"\n author: \"{{{{ author }}}}\"\nrdf:\n sources: []\nsparql:\n queries: {{}}\ndeterminism:\n seed: \"{timestamp}\"\n---\n\n# Generated Python module: {name}\n# Author: {{{{ author }}}}\n# Generated at: {timestamp}\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\n@dataclass\nclass {{{{ name | pascal_case }}}}:\n id: str\n name: str\n\n def __init__(self, name: str):\n self.id = str(uuid.uuid4())\n self.name = name\n"
),
"typescript" => format!(
"---\nto: src/{{{{ name }}}}.ts\nvars:\n name: \"{name}\"\n author: \"{{{{ author }}}}\"\nrdf:\n sources: []\nsparql:\n queries: {{}}\ndeterminism:\n seed: \"{timestamp}\"\n---\n\n// Generated TypeScript module: {name}\n// Author: {{{{ author }}}}\n// Generated at: {timestamp}\n\nexport interface {{{{ name | pascal_case }}}} {{\n id: string;\n name: string;\n}}\n\nexport class {{{{ name | pascal_case }}}}Class implements {{{{ name | pascal_case }}}} {{\n id: string;\n name: string;\n\n constructor(name: string) {{\n this.id = crypto.randomUUID();\n this.name = name;\n }}\n}}\n"
),
"generic" => format!(
"---\nto: output/{{{{ name }}}}.txt\nvars:\n name: \"{name}\"\n author: \"{{{{ author }}}}\"\nrdf:\n sources: []\nsparql:\n queries: {{}}\ndeterminism:\n seed: \"{timestamp}\"\n---\n\nGenerated file: {name}\nAuthor: {{{{ author }}}}\nGenerated at: {timestamp}\n\nThis is a generic template. Customize the content below:\n\nHello, {{{{ name }}}}!\n\nYour template content goes here.\n"
),
_ => format!(
"---\nto: output/{{{{ name }}}}.txt\nvars:\n name: \"{name}\"\n author: \"{{{{ author }}}}\"\nrdf:\n sources: []\nsparql:\n queries: {{}}\ndeterminism:\n seed: \"{timestamp}\"\n---\n\nGenerated file: {name}\nAuthor: {{{{ author }}}}\nGenerated at: {timestamp}\n\nThis is a generic template. Customize the content below:\n\nHello, {{{{ name }}}}!\n\nYour template content goes here.\n"
),
};
Ok(content)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_rust_template() {
let content = generate_template_content("my_module", "rust").unwrap();
assert!(content.contains("to: src/{{ name }}.rs"));
assert!(content.contains("pub struct {{ name | pascal_case }}"));
assert!(content.contains("name: \"my_module\""));
}
#[test]
fn test_generate_python_template() {
let content = generate_template_content("my_module", "python").unwrap();
assert!(content.contains("to: src/{{ name }}.py"));
assert!(content.contains("class {{ name | pascal_case }}"));
assert!(content.contains("name: \"my_module\""));
}
#[test]
fn test_generate_typescript_template() {
let content = generate_template_content("my_module", "typescript").unwrap();
assert!(content.contains("to: src/{{ name }}.ts"));
assert!(content.contains("interface {{ name | pascal_case }}"));
assert!(content.contains("name: \"my_module\""));
}
#[test]
fn test_generate_generic_template() {
let content = generate_template_content("test", "generic").unwrap();
assert!(content.contains("to: output/{{ name }}.txt"));
assert!(content.contains("Hello, {{ name }}!"));
}
#[test]
fn test_generate_unknown_type_fallback() {
let content = generate_template_content("test", "unknown").unwrap();
assert!(content.contains("to: output/{{ name }}.txt"));
assert!(content.contains("generic template"));
}
}
/// CLI Arguments for new command
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct NewInput {
/// Template name
pub name: String,
/// Template type (rust, python, typescript, generic)
pub template_type: String,
/// Output directory
pub output_dir: PathBuf,
}
/// New template output
#[derive(Debug, Clone, Serialize)]
pub struct NewOutput {
pub template_path: String,
pub template_name: String,
pub template_type: String,
}
/// Execute template creation - full implementation
pub fn execute_new(input: NewInput) -> Result<NewOutput> {
// Generate template content
let content = generate_template_content(&input.name, &input.template_type)?;
// Create filename
let filename = format!("{}.tmpl", input.name);
let output_path = input.output_dir.join(&filename);
// Create output directory if it doesn't exist
std::fs::create_dir_all(&input.output_dir).map_err(|e| {
ggen_utils::error::Error::new(&format!("Failed to create output directory: {}", e))
})?;
// Write template file
std::fs::write(&output_path, content)
.map_err(|e| ggen_utils::error::Error::new(&format!("Failed to write template: {}", e)))?;
// Return output
Ok(NewOutput {
template_path: output_path.display().to_string(),
template_name: input.name,
template_type: input.template_type,
})
}
/// CLI run function - bridges sync CLI to async domain logic
pub fn run(args: &NewInput) -> Result<()> {
let output = execute_new(args.clone())?;
ggen_utils::alert_success!(
"Created new {} template: {}",
output.template_type,
output.template_path
);
ggen_utils::alert_info!("📝 Template name: {}", output.template_name);
Ok(())
}