Skip to main content

serverust_cli/
scaffold.rs

1//! Geração de scaffolding em disco.
2//!
3//! Todas as funções recebem o diretório base por argumento (em vez de
4//! `current_dir()`) para permitirem testes em diretórios temporários sem
5//! mutar o estado global do processo.
6
7use std::fs;
8use std::path::{Path, PathBuf};
9
10use anyhow::{Context, Result, bail};
11
12use crate::cli::GenerateKind;
13use crate::templates;
14
15/// Cria um novo projeto serverust em `base/<name>`.
16pub fn new_project(base: &Path, name: &str) -> Result<()> {
17    let root = base.join(name);
18    if root.exists() {
19        bail!("target directory '{}' already exists", root.display());
20    }
21    fs::create_dir_all(root.join("src/modules"))
22        .with_context(|| format!("creating src/modules in {}", root.display()))?;
23    fs::create_dir_all(root.join("src/shared"))
24        .with_context(|| format!("creating src/shared in {}", root.display()))?;
25
26    write_file(
27        &root.join("Cargo.toml"),
28        &templates::project_cargo_toml(name),
29    )?;
30    write_file(
31        &root.join("serverust.toml"),
32        &templates::project_serverust_toml(name),
33    )?;
34    write_file(&root.join("src/main.rs"), &templates::project_main_rs())?;
35    write_file(
36        &root.join("src/modules/mod.rs"),
37        &templates::project_modules_mod_rs(),
38    )?;
39    write_file(
40        &root.join("src/shared/mod.rs"),
41        &templates::project_shared_mod_rs(),
42    )?;
43    Ok(())
44}
45
46/// Gera scaffolding para um artefato (`controller`, `service`, etc.) em
47/// `base/src/...`.
48pub fn generate(base: &Path, kind: GenerateKind, name: &str) -> Result<()> {
49    match kind {
50        GenerateKind::Resource => generate_resource(base, name),
51        GenerateKind::Module => generate_module(base, name, false),
52        GenerateKind::Controller => generate_single_module_file(
53            base,
54            name,
55            format!("{name}.controller.rs"),
56            templates::controller(name),
57        ),
58        GenerateKind::Service => generate_single_module_file(
59            base,
60            name,
61            format!("{name}.service.rs"),
62            templates::service(name),
63        ),
64        GenerateKind::Pipe => generate_shared(
65            base,
66            "pipes",
67            format!("{name}.pipe.rs"),
68            templates::pipe(name),
69        ),
70        GenerateKind::Guard => generate_shared(
71            base,
72            "guards",
73            format!("{name}.guard.rs"),
74            templates::guard(name),
75        ),
76        GenerateKind::Interceptor => generate_shared(
77            base,
78            "interceptors",
79            format!("{name}.interceptor.rs"),
80            templates::interceptor(name),
81        ),
82        GenerateKind::Filter => generate_shared(
83            base,
84            "filters",
85            format!("{name}.filter.rs"),
86            templates::filter(name),
87        ),
88    }
89}
90
91fn generate_module(base: &Path, name: &str, with_dto: bool) -> Result<()> {
92    let dir = base.join("src/modules").join(name);
93    fs::create_dir_all(&dir).with_context(|| format!("creating {}", dir.display()))?;
94    write_file(
95        &dir.join("mod.rs"),
96        &if with_dto {
97            templates::resource_mod_rs(name)
98        } else {
99            templates::module_mod_rs(name)
100        },
101    )?;
102    write_file(
103        &dir.join(format!("{name}.controller.rs")),
104        &templates::controller(name),
105    )?;
106    write_file(
107        &dir.join(format!("{name}.service.rs")),
108        &templates::service(name),
109    )?;
110    if with_dto {
111        write_file(&dir.join(format!("{name}.dto.rs")), &templates::dto(name))?;
112    }
113    Ok(())
114}
115
116fn generate_resource(base: &Path, name: &str) -> Result<()> {
117    generate_module(base, name, true)
118}
119
120fn generate_single_module_file(
121    base: &Path,
122    name: &str,
123    filename: String,
124    body: String,
125) -> Result<()> {
126    let dir = base.join("src/modules").join(name);
127    fs::create_dir_all(&dir).with_context(|| format!("creating {}", dir.display()))?;
128    write_file(&dir.join(filename), &body)
129}
130
131fn generate_shared(base: &Path, kind_dir: &str, filename: String, body: String) -> Result<()> {
132    let dir = base.join("src/shared").join(kind_dir);
133    fs::create_dir_all(&dir).with_context(|| format!("creating {}", dir.display()))?;
134    write_file(&dir.join(filename), &body)
135}
136
137fn write_file(path: &PathBuf, body: &str) -> Result<()> {
138    fs::write(path, body).with_context(|| format!("writing {}", path.display()))
139}