use std::path::Path;
use serde_json::Value;
use handlebars::Handlebars;
use crate::project::handlers::traits::ProjectHandler;
use crate::core::Result;
pub struct TemplateProjectHandler {
name: String,
description: String,
templates: Vec<String>,
}
impl TemplateProjectHandler {
pub fn new(name: &str, description: &str, templates: Vec<String>) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
templates,
}
}
}
impl ProjectHandler for TemplateProjectHandler {
fn name(&self) -> &str {
&self.name
}
fn description(&self) -> &str {
&self.description
}
fn can_handle(&self, template_name: &str, _variables: &Value) -> bool {
self.templates.contains(&template_name.to_string())
}
fn initialize_project(&self, project_name: &str, target_dir: &Path, variables: &Value) -> Result<()> {
println!("📝 Creating new {} project using {} template...", project_name, self.name);
let template_name = if let Some(template) = variables.get("template").and_then(|t| t.as_str()) {
template
} else {
return Err("No template specified".into());
};
crate::project::templates::apply_template(
template_name,
target_dir,
project_name,
Some(variables.clone())
)?;
println!("✅ {} project created successfully!", project_name);
Ok(())
}
fn get_next_steps(&self, project_name: &str, variables: &Value) -> Vec<String> {
if let Some(template) = variables.get("template").and_then(|t| t.as_str()) {
let project_dir = Path::new(project_name);
let next_steps_file = project_dir.join(".ferrisup_next_steps.json");
if next_steps_file.exists() {
if let Ok(content) = std::fs::read_to_string(&next_steps_file) {
if let Ok(json) = serde_json::from_str::<Value>(&content) {
if let Some(steps) = json.get("next_steps").and_then(|s| s.as_array()) {
let next_steps: Vec<String> = steps
.iter()
.filter_map(|s| s.as_str().map(|s| s.to_string()))
.collect();
if !next_steps.is_empty() {
let _ = std::fs::remove_file(&next_steps_file);
return next_steps;
}
}
}
}
}
if let Ok(template_config) = crate::project::templates::get_template_config(template) {
if let Some(next_steps) = template_config.get("next_steps") {
if let Some(steps) = next_steps.as_array() {
let mut handlebars = Handlebars::new();
handlebars.register_escape_fn(handlebars::no_escape);
let mut result = Vec::new();
for step in steps {
if let Some(step_str) = step.as_str() {
match handlebars.render_template(step_str, variables) {
Ok(rendered) => {
let final_step = rendered.replace("{{project_name}}", project_name);
result.push(final_step);
},
Err(_) => {
let step_text = step_str.replace("{{project_name}}", project_name);
result.push(step_text);
}
}
}
}
if !result.is_empty() {
return result;
}
}
if let Some(steps_obj) = next_steps.as_object() {
if let Some(data_format) = variables.get("data_format").and_then(|f| f.as_str()) {
if let Some(format_steps) = steps_obj.get(data_format).and_then(|s| s.as_array()) {
let mut result = Vec::new();
for step in format_steps {
if let Some(step_str) = step.as_str() {
let step_text = step_str.replace("{{project_name}}", project_name);
result.push(step_text);
}
}
if !result.is_empty() {
return result;
}
}
}
}
}
}
if let Some(next_steps) = crate::project::templates::get_template_next_steps(
template,
project_name,
Some(variables.clone())
) {
return next_steps;
}
}
vec![
format!("🚀 Navigate to your project: cd {}", project_name),
"📝 Review the generated code".to_string(),
"🔧 Build the project: cargo build".to_string(),
"▶️ Run the project: cargo run".to_string(),
]
}
}