Skip to main content

actr_cli/commands/initialize/
mod.rs

1mod kotlin;
2mod python;
3mod rust;
4mod swift;
5pub mod traits;
6mod typescript;
7
8use crate::commands::SupportedLanguage;
9use crate::error::{ActrCliError, Result};
10use crate::template::{DEFAULT_MANUFACTURER, EchoRole, ProjectTemplateName, TemplateContext};
11use crate::utils::read_fixture_text;
12use handlebars::Handlebars;
13use kotlin::KotlinInitializer;
14use python::PythonInitializer;
15use rust::RustInitializer;
16use std::path::Path;
17use std::process::Command;
18use swift::SwiftInitializer;
19use typescript::TypeScriptInitializer;
20
21pub use traits::{InitContext, ProjectInitializer};
22
23pub fn init_git_repo(project_dir: &Path) -> Result<()> {
24    let output = Command::new("git")
25        .args(["init"])
26        .current_dir(project_dir)
27        .output()
28        .map_err(|error| ActrCliError::Command(format!("Failed to run git init: {error}")))?;
29
30    if !output.status.success() {
31        let stderr = String::from_utf8_lossy(&output.stderr);
32        return Err(ActrCliError::Command(format!("git init failed: {stderr}")));
33    }
34
35    tracing::info!("🔧 Initialized git repository");
36    Ok(())
37}
38
39/// Generate a local.proto file with the given package name.
40///
41/// For the echo service role, uses the full EchoService definition so that
42/// `actr gen` can produce `EchoHandler` / `EchoServiceActor` traits.
43/// For the echo app role and other templates, generates an empty skeleton.
44pub fn create_local_proto(
45    project_dir: &Path,
46    project_name: &str,
47    proto_dir: &str,
48    template: ProjectTemplateName,
49    echo_role: Option<EchoRole>,
50) -> Result<()> {
51    let proto_path = project_dir.join(proto_dir);
52    std::fs::create_dir_all(&proto_path)?;
53
54    // Load template file
55    let fixtures_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("fixtures");
56    let template_file_name = match (template, echo_role) {
57        (ProjectTemplateName::Echo, Some(EchoRole::Service)) => "echo_service.hbs",
58        (ProjectTemplateName::Echo, _) => "local.echo.hbs",
59        (ProjectTemplateName::Empty, _) => "local.empty.hbs",
60        (ProjectTemplateName::DataStream, _) => "local.empty.hbs",
61    };
62    let template_path = fixtures_root.join("protos").join(template_file_name);
63
64    let template_content = read_fixture_text(&template_path)?;
65
66    // Create template context
67    let template_context = TemplateContext::new(project_name, "", DEFAULT_MANUFACTURER, "", false);
68    let handlebars = Handlebars::new();
69
70    // Render template
71    let local_proto_content = handlebars
72        .render_template(&template_content, &template_context)
73        .map_err(|e| {
74            ActrCliError::Io(std::io::Error::new(
75                std::io::ErrorKind::InvalidData,
76                format!("Failed to render proto template: {}", e),
77            ))
78        })?;
79
80    let proto_output_name = match (template, echo_role) {
81        (ProjectTemplateName::Echo, Some(EchoRole::Service)) => "echo.proto",
82        _ => "local.proto",
83    };
84    let proto_output_path = proto_path.join(proto_output_name);
85    std::fs::write(&proto_output_path, local_proto_content)?;
86
87    tracing::info!("📄 Created {}", proto_output_path.display());
88    Ok(())
89}
90
91pub struct InitializerFactory;
92
93impl InitializerFactory {
94    pub fn get_initializer(language: SupportedLanguage) -> Result<Box<dyn ProjectInitializer>> {
95        match language {
96            SupportedLanguage::Rust => Ok(Box::new(RustInitializer)),
97            SupportedLanguage::Python => Ok(Box::new(PythonInitializer)),
98            SupportedLanguage::Swift => Ok(Box::new(SwiftInitializer)),
99            SupportedLanguage::Kotlin => Ok(Box::new(KotlinInitializer)),
100            SupportedLanguage::TypeScript => Ok(Box::new(TypeScriptInitializer)),
101        }
102    }
103}
104
105pub async fn execute_initialize(language: SupportedLanguage, context: &InitContext) -> Result<()> {
106    let initializer = InitializerFactory::get_initializer(language)?;
107    initializer.generate_project_structure(context).await?;
108    initializer.print_next_steps(context);
109    Ok(())
110}