serverust_cli/
templates.rs1const NAME_PLACEHOLDER: &str = "{{NAME}}";
8const TYPE_PLACEHOLDER: &str = "{{TYPE}}";
9
10pub fn project_cargo_toml(name: &str) -> String {
11 format!(
12 r#"[package]
13name = "{name}"
14version = "0.1.0"
15edition = "2024"
16
17[dependencies]
18serverust-core = {{ path = "../serverust-core" }}
19serverust-lambda = {{ path = "../serverust-lambda" }}
20serverust-macros = {{ path = "../serverust-macros" }}
21tokio = {{ version = "1", features = ["macros", "rt-multi-thread"] }}
22serde = {{ version = "1", features = ["derive"] }}
23"#
24 )
25}
26
27pub fn project_serverust_toml(name: &str) -> String {
28 format!(
29 r#"# serverust.toml — configuração do projeto "{name}"
30# Perfis: default, dev, staging, prod
31# Selecione com SERVERUST_PROFILE=prod ou ServerustConfig::load_for_profile("prod")
32# Override por env: SERVERUST_SERVER__PORT=8080
33
34[default.server]
35host = "127.0.0.1"
36port = 3000
37
38[default.lambda]
39memory_size = 128
40timeout_seconds = 30
41
42[default.telemetry]
43log_level = "info"
44format = "json"
45
46[default.openapi]
47title = "{name}"
48version = "0.1.0"
49docs_path = "/docs"
50redoc_path = "/redoc"
51
52[dev.server]
53port = 3001
54
55[prod.server]
56host = "0.0.0.0"
57port = 8080
58"#
59 )
60}
61
62pub fn project_main_rs() -> String {
63 r#"use serverust_core::App;
64use serverust_lambda::AppRuntime;
65
66#[tokio::main]
67async fn main() -> Result<(), Box<dyn std::error::Error>> {
68 let app = App::new();
69 app.run().await?;
70 Ok(())
71}
72"#
73 .to_string()
74}
75
76pub fn project_modules_mod_rs() -> String {
77 "// declare seus módulos aqui: `pub mod users;`\n".to_string()
78}
79
80pub fn project_shared_mod_rs() -> String {
81 "// componentes compartilhados: guards, pipes, interceptors, filters\n".to_string()
82}
83
84pub fn controller(name: &str) -> String {
85 let type_name = pascal_case(name);
86 template_with_name_type(
87 r#"use serverust_core::extract::Path;
88use serverust_macros::get;
89
90#[get("/{{NAME}}/{id}")]
91pub async fn show_{{NAME}}(Path(id): Path<u64>) -> String {
92 format!("{{TYPE}}::show id={id}")
93}
94"#,
95 name,
96 &type_name,
97 )
98}
99
100pub fn service(name: &str) -> String {
101 let type_name = pascal_case(name);
102 template_with_name_type(
103 r#"use serverust_macros::injectable;
104
105#[injectable]
106pub struct {{TYPE}}Service;
107
108impl {{TYPE}}Service {
109 pub fn new() -> Self {
110 Self
111 }
112}
113
114impl Default for {{TYPE}}Service {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119"#,
120 name,
121 &type_name,
122 )
123}
124
125pub fn dto(name: &str) -> String {
126 let type_name = pascal_case(name);
127 template_with_name_type(
128 r#"use serde::{Deserialize, Serialize};
129use validator::Validate;
130
131#[derive(Debug, Deserialize, Serialize, Validate)]
132pub struct Create{{TYPE}}Dto {
133 #[validate(length(min = 1))]
134 pub name: String,
135}
136"#,
137 name,
138 &type_name,
139 )
140}
141
142pub fn module_mod_rs(name: &str) -> String {
143 format!(
144 "#[path = \"{name}.controller.rs\"]\npub mod controller;\n\
145 #[path = \"{name}.service.rs\"]\npub mod service;\n"
146 )
147}
148
149pub fn resource_mod_rs(name: &str) -> String {
150 format!(
151 "{base}#[path = \"{name}.dto.rs\"]\npub mod dto;\n",
152 base = module_mod_rs(name),
153 )
154}
155
156pub fn pipe(name: &str) -> String {
157 let type_name = pascal_case(name);
158 template_with_name_type(
159 r#"use serverust_core::pipeline::Pipe;
160
161pub struct {{TYPE}}Pipe;
162
163impl Pipe<String> for {{TYPE}}Pipe {
164 type Output = String;
165
166 fn transform(input: String) -> Result<Self::Output, axum::response::Response> {
167 Ok(input)
168 }
169}
170"#,
171 name,
172 &type_name,
173 )
174}
175
176pub fn guard(name: &str) -> String {
177 let type_name = pascal_case(name);
178 template_with_name_type(
179 r#"use axum::http::request::Parts;
180use axum::response::Response;
181use serverust_core::pipeline::Guard;
182
183pub struct {{TYPE}}Guard;
184
185impl Guard for {{TYPE}}Guard {
186 async fn check(_parts: &Parts) -> Result<(), Response> {
187 Ok(())
188 }
189}
190"#,
191 name,
192 &type_name,
193 )
194}
195
196pub fn interceptor(name: &str) -> String {
197 let type_name = pascal_case(name);
198 template_with_name_type(
199 r#"use axum::extract::Request;
200use axum::middleware::Next;
201use axum::response::Response;
202use serverust_core::pipeline::Interceptor;
203
204pub struct {{TYPE}}Interceptor;
205
206impl Interceptor for {{TYPE}}Interceptor {
207 async fn intercept(&self, req: Request, next: Next) -> Response {
208 next.run(req).await
209 }
210}
211"#,
212 name,
213 &type_name,
214 )
215}
216
217pub fn filter(name: &str) -> String {
218 let type_name = pascal_case(name);
219 template_with_name_type(
220 r#"use axum::response::{IntoResponse, Response};
221
222/// Filter (mapeador de erros) inspirado em ExceptionFilter do NestJS.
223pub struct {{TYPE}}Filter;
224
225impl {{TYPE}}Filter {
226 pub fn handle<E: std::error::Error>(err: E) -> Response {
227 (axum::http::StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
228 }
229}
230"#,
231 name,
232 &type_name,
233 )
234}
235
236fn template_with_name_type(template: &str, name: &str, type_name: &str) -> String {
237 template
238 .replace(NAME_PLACEHOLDER, name)
239 .replace(TYPE_PLACEHOLDER, type_name)
240}
241
242fn pascal_case(s: &str) -> String {
243 let mut out = String::with_capacity(s.len());
244 let mut up = true;
245 for ch in s.chars() {
246 if ch == '_' || ch == '-' {
247 up = true;
248 continue;
249 }
250 if up {
251 out.extend(ch.to_uppercase());
252 up = false;
253 } else {
254 out.push(ch);
255 }
256 }
257 out
258}