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