lmrc_cli/generator/
mod.rs

1pub mod apps;
2pub mod documentation;
3pub mod gitlab_ci;
4pub mod pipeline;
5pub mod workspace;
6
7use colored::Colorize;
8use lmrc_config_validator::LmrcConfig;
9use std::fs;
10use std::path::{Path, PathBuf};
11
12use crate::error::Result;
13
14pub struct ProjectGenerator {
15    config: LmrcConfig,
16    project_path: PathBuf,
17}
18
19impl ProjectGenerator {
20    pub fn new(config: LmrcConfig, project_path: PathBuf) -> Self {
21        Self {
22            config,
23            project_path,
24        }
25    }
26
27    pub fn project_path(&self) -> &Path {
28        &self.project_path
29    }
30
31    pub async fn generate(&self) -> Result<()> {
32        println!("{}", "Generating project structure...".yellow());
33
34        // Create root directory
35        fs::create_dir_all(&self.project_path)?;
36
37        // Create directory structure
38        self.create_directory_structure()?;
39
40        // Generate workspace Cargo.toml
41        workspace::generate_workspace_toml(&self.project_path, &self.config)?;
42
43        // Generate configuration file
44        self.generate_config_file()?;
45
46        // Generate applications
47        self.generate_applications()?;
48
49        // Generate libs directory with placeholders
50        self.generate_libs()?;
51
52        // Generate pipeline application
53        pipeline::generate_pipeline_app(&self.project_path, &self.config)?;
54
55        // Generate GitLab CI configuration
56        gitlab_ci::generate_gitlab_ci(&self.project_path, &self.config)?;
57
58        // Generate documentation
59        documentation::generate_docs(&self.project_path, &self.config)?;
60
61        // Generate Docker files
62        self.generate_docker_files()?;
63
64        // Generate .gitignore
65        self.generate_gitignore()?;
66
67        Ok(())
68    }
69
70    pub async fn generate_minimal(&self) -> Result<()> {
71        println!("{}", "Generating minimal project structure...".yellow());
72
73        // Create root directory
74        fs::create_dir_all(&self.project_path)?;
75
76        // Create directory structure
77        self.create_directory_structure()?;
78
79        // Generate workspace Cargo.toml
80        workspace::generate_workspace_toml(&self.project_path, &self.config)?;
81
82        // Generate configuration file
83        self.generate_config_file()?;
84
85        // Generate libs directory with placeholders
86        self.generate_libs()?;
87
88        // Generate Docker files
89        self.generate_docker_files()?;
90
91        // Generate .gitignore
92        self.generate_gitignore()?;
93
94        Ok(())
95    }
96
97    fn create_directory_structure(&self) -> Result<()> {
98        let dirs = vec!["apps", "libs", "docs", "docker", "infra"];
99
100        for dir in dirs {
101            let path = self.project_path.join(dir);
102            fs::create_dir_all(&path)?;
103            println!("  {} {}", "Created:".green(), path.display());
104        }
105
106        Ok(())
107    }
108
109    fn generate_config_file(&self) -> Result<()> {
110        let config_content = self.config.to_toml_string()?;
111        let config_path = self.project_path.join("lmrc.toml");
112        fs::write(&config_path, config_content)?;
113        println!("  {} {}", "Created:".green(), config_path.display());
114        Ok(())
115    }
116
117    fn generate_applications(&self) -> Result<()> {
118        // Use the new apps module to generate applications from templates
119        apps::generate_applications(&self.project_path, &self.config)?;
120        Ok(())
121    }
122
123    fn generate_libs(&self) -> Result<()> {
124        let libs_path = self.project_path.join("libs");
125        let readme = r#"# Shared Libraries
126
127This directory contains shared libraries used across multiple applications.
128
129Place reusable code, utilities, and shared logic here.
130"#;
131        fs::write(libs_path.join("README.md"), readme)?;
132        println!("  {} libs/README.md", "Created:".green());
133        Ok(())
134    }
135
136    fn generate_docker_files(&self) -> Result<()> {
137        let docker_path = self.project_path.join("docker");
138
139        // Generate Dockerfile template for applications
140        let dockerfile = r#"# Build stage
141FROM rust:1.75-slim as builder
142
143WORKDIR /app
144COPY . .
145
146RUN cargo build --release
147
148# Runtime stage
149FROM debian:bookworm-slim
150
151RUN apt-get update && \
152    apt-get install -y ca-certificates && \
153    rm -rf /var/lib/apt/lists/*
154
155WORKDIR /app
156
157COPY --from=builder /app/target/release/app /app/app
158
159ENTRYPOINT ["/app/app"]
160"#;
161
162        fs::write(docker_path.join("Dockerfile"), dockerfile)?;
163        println!("  {} docker/Dockerfile", "Created:".green());
164
165        // Generate docker-compose.yml for local development
166        let docker_compose = format!(
167            r#"version: '3.8'
168
169services:
170  postgres:
171    image: postgres:{}
172    environment:
173      POSTGRES_DB: {}
174      POSTGRES_USER: postgres
175      POSTGRES_PASSWORD: postgres
176    ports:
177      - "5432:5432"
178    volumes:
179      - postgres_data:/var/lib/postgresql/data
180
181volumes:
182  postgres_data:
183"#,
184            self.config
185                .infrastructure
186                .postgres
187                .as_ref()
188                .map(|p| p.version.as_str())
189                .unwrap_or("16"),
190            self.config
191                .infrastructure
192                .postgres
193                .as_ref()
194                .map(|p| p.database_name.as_str())
195                .unwrap_or("myapp")
196        );
197
198        fs::write(docker_path.join("docker-compose.yml"), docker_compose)?;
199        println!("  {} docker/docker-compose.yml", "Created:".green());
200
201        Ok(())
202    }
203
204    fn generate_gitignore(&self) -> Result<()> {
205        let gitignore = r#"# Rust
206/target/
207**/*.rs.bk
208*.pdb
209Cargo.lock
210
211# IDE
212.idea/
213.vscode/
214*.swp
215*.swo
216*~
217
218# OS
219.DS_Store
220Thumbs.db
221
222# Environment
223.env
224.env.local
225
226# Secrets
227secrets/
228*.pem
229*.key
230
231# SSH Keys (project-local)
232.ssh/
233*.pub
234"#;
235
236        let gitignore_path = self.project_path.join(".gitignore");
237        fs::write(&gitignore_path, gitignore)?;
238        println!("  {} .gitignore", "Created:".green());
239
240        Ok(())
241    }
242}