use crate::cli::generator::error::{GeneratorError, GeneratorResult};
use crate::cli::generator::git::initialize_git;
use crate::cli::generator::template::{render_templates, TemplateContext};
use crate::cli::generator::validator::{
validate_output_path, validate_project_directory_does_not_exist, validate_project_name,
validate_template_directory,
};
use std::fs;
pub(crate) fn determine_features(protocol: &str, additional_features: &str) -> String {
let base = match protocol {
"http" | "mcp" | "both" => protocol.to_string(),
_ => "http".to_string(),
};
if additional_features.is_empty() {
base
} else {
format!("{},{}", base, additional_features)
}
}
pub fn generate_project(
project_name: &str,
protocol: &str,
features: &str,
template: &str,
) -> GeneratorResult<()> {
validate_project_name(project_name)?;
let features_str = determine_features(protocol, features);
let context = TemplateContext::new(project_name, protocol, &features_str);
let current_dir =
std::env::current_dir().map_err(|e| GeneratorError::CurrentDirError(e.to_string()))?;
let template_dir = current_dir
.join("sdforge-cli")
.join("templates")
.join(template);
validate_template_directory(&template_dir)?;
let output_dir = current_dir.join(project_name);
validate_output_path(&output_dir)?;
validate_project_directory_does_not_exist(&output_dir)?;
fs::create_dir_all(&output_dir)
.map_err(|_e| GeneratorError::CreateDirectoryError(output_dir.to_path_buf()))?;
render_templates(&template_dir, &output_dir, &context)?;
initialize_git(&output_dir)?;
println!("✓ Project '{}' created successfully!", project_name);
println!(" Template: {}", template);
println!(" Protocol: {}", protocol);
println!(" Features: {}", features_str);
println!("\nNext steps:");
println!(" cd {}", project_name);
println!(" cargo build");
println!(" cargo run");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_determine_features_http() {
assert_eq!(determine_features("http", ""), "http");
assert_eq!(determine_features("http", "database"), "http,database");
}
#[test]
fn test_determine_features_mcp() {
assert_eq!(determine_features("mcp", ""), "mcp");
assert_eq!(determine_features("mcp", "auth"), "mcp,auth");
}
#[test]
fn test_determine_features_both() {
assert_eq!(determine_features("both", ""), "both");
assert_eq!(determine_features("both", "logging"), "both,logging");
}
#[test]
fn test_determine_features_unknown() {
assert_eq!(determine_features("unknown", ""), "http");
assert_eq!(determine_features("unknown", "test"), "http,test");
}
#[test]
fn test_generate_project_invalid_name() {
assert!(generate_project("", "http", "", "basic").is_err());
assert!(generate_project("../malicious", "http", "", "basic").is_err());
assert!(generate_project("project/name", "http", "", "basic").is_err());
}
#[test]
fn test_generate_project_name_too_long() {
let long_name = "a".repeat(65);
assert!(generate_project(&long_name, "http", "", "basic").is_err());
}
}