Skip to main content

hyperlane_cli/template/
fn.rs

1use crate::*;
2
3/// Get directory name for template type
4///
5/// # Arguments
6///
7/// - `&TemplateType`: The template type
8///
9/// # Returns
10///
11/// - `String`: Directory name
12fn get_directory_name(template_type: &TemplateType) -> String {
13    match template_type {
14        TemplateType::Controller => "controller".to_string(),
15        TemplateType::Domain => "domain".to_string(),
16        TemplateType::Exception => "exception".to_string(),
17        TemplateType::Mapper => "mapper".to_string(),
18        TemplateType::Model => "model".to_string(),
19        TemplateType::Repository => "repository".to_string(),
20        TemplateType::Service => "service".to_string(),
21        TemplateType::Utils => "utils".to_string(),
22        TemplateType::View => "view".to_string(),
23    }
24}
25
26/// Get model subtype directory name
27///
28/// # Arguments
29///
30/// - `&ModelSubType`: The model subtype
31///
32/// # Returns
33///
34/// - `String`: Directory name
35fn get_model_sub_type_name(sub_type: &ModelSubType) -> String {
36    match sub_type {
37        ModelSubType::Application => "application".to_string(),
38        ModelSubType::Request => "request".to_string(),
39        ModelSubType::Response => "response".to_string(),
40    }
41}
42
43/// Create directory if it does not exist
44///
45/// # Arguments
46///
47/// - `&Path`: Path to the directory
48///
49/// # Returns
50///
51/// - `Result<(), TemplateError>`: Success or error
52async fn ensure_directory(path: &Path) -> Result<(), TemplateError> {
53    if !path.exists() {
54        create_dir_all(path).await?;
55    }
56    Ok(())
57}
58
59/// Write mod.rs content with module declarations
60///
61/// # Arguments
62///
63/// - `&Path`: Path to mod.rs file
64/// - `&[&str]`: List of modules to include
65///
66/// # Returns
67///
68/// - `Result<(), TemplateError>`: Success or error
69async fn write_mod_rs(path: &Path, modules: &[&str]) -> Result<(), TemplateError> {
70    let mut content: String = String::new();
71    for module in modules {
72        let mod_name: String = if module.starts_with("r#") {
73            module.to_string()
74        } else {
75            format!("r#{module}")
76        };
77        content.push_str(&format!("mod {mod_name};\n"));
78    }
79    content.push('\n');
80    let mut pub_use_parts: Vec<String> = Vec::new();
81    for module in modules {
82        let raw_name: &str = if let Some(stripped) = module.strip_prefix("r#") {
83            stripped
84        } else {
85            module
86        };
87        let mod_name: String = if module.starts_with("r#") {
88            module.to_string()
89        } else {
90            format!("r#{module}")
91        };
92        if raw_name == "const" || raw_name == "static" {
93            pub_use_parts.push(mod_name);
94        } else if raw_name == "enum" || raw_name == "fn" {
95            pub_use_parts.push(format!("{mod_name}::*"));
96        } else if raw_name == "struct" {
97            pub_use_parts.push(mod_name);
98        }
99    }
100    if !pub_use_parts.is_empty() {
101        content.push_str("pub use {");
102        content.push_str(&pub_use_parts.join(", "));
103        content.push_str("};\n");
104    }
105    content.push('\n');
106    content.push_str("use super::*;\n");
107    write(path, content).await?;
108    Ok(())
109}
110
111/// Write empty mod.rs
112///
113/// # Arguments
114///
115/// - `&Path`: Path to mod.rs file
116///
117/// # Returns
118///
119/// - `Result<(), TemplateError>`: Success or error
120async fn write_empty_mod_rs(path: &Path) -> Result<(), TemplateError> {
121    write(path, "\n").await?;
122    Ok(())
123}
124
125/// Create controller template files
126///
127/// # Arguments
128///
129/// - `&Path`: Target directory path
130/// - `&str`: Name of the component
131///
132/// # Returns
133///
134/// - `Result<(), TemplateError>`: Success or error
135async fn create_controller_template(
136    target_dir: &Path,
137    _component_name: &str,
138) -> Result<(), TemplateError> {
139    ensure_directory(target_dir).await?;
140    let mod_rs: PathBuf = target_dir.join("mod.rs");
141    write_mod_rs(&mod_rs, &["fn", "impl", "struct"]).await?;
142    let fn_rs: PathBuf = target_dir.join("fn.rs");
143    write(&fn_rs, "use super::*;\n").await?;
144    let impl_rs: PathBuf = target_dir.join("impl.rs");
145    write(&impl_rs, "use super::*;\n").await?;
146    let struct_rs: PathBuf = target_dir.join("struct.rs");
147    write(&struct_rs, "use super::*;\n").await?;
148    Ok(())
149}
150
151/// Create view template files
152///
153/// # Arguments
154///
155/// - `&Path`: Target directory path
156/// - `&str`: Name of the component
157///
158/// # Returns
159///
160/// - `Result<(), TemplateError>`: Success or error
161async fn create_view_template(
162    target_dir: &Path,
163    _component_name: &str,
164) -> Result<(), TemplateError> {
165    ensure_directory(target_dir).await?;
166    let mod_rs: PathBuf = target_dir.join("mod.rs");
167    write_mod_rs(&mod_rs, &["fn", "impl", "struct"]).await?;
168    let fn_rs: PathBuf = target_dir.join("fn.rs");
169    write(&fn_rs, "use super::*;\n").await?;
170    let impl_rs: PathBuf = target_dir.join("impl.rs");
171    write(&impl_rs, "use super::*;\n").await?;
172    let struct_rs: PathBuf = target_dir.join("struct.rs");
173    write(&struct_rs, "use super::*;\n").await?;
174    Ok(())
175}
176
177/// Create service template files
178///
179/// # Arguments
180///
181/// - `&Path`: Target directory path
182/// - `&str`: Name of the component
183///
184/// # Returns
185///
186/// - `Result<(), TemplateError>`: Success or error
187async fn create_service_template(
188    target_dir: &Path,
189    _component_name: &str,
190) -> Result<(), TemplateError> {
191    ensure_directory(target_dir).await?;
192    let mod_rs: PathBuf = target_dir.join("mod.rs");
193    write_mod_rs(&mod_rs, &["impl", "struct"]).await?;
194    let impl_rs: PathBuf = target_dir.join("impl.rs");
195    write(&impl_rs, "use super::*;\n").await?;
196    let struct_rs: PathBuf = target_dir.join("struct.rs");
197    write(&struct_rs, "use super::*;\n").await?;
198    Ok(())
199}
200
201/// Create domain template files
202///
203/// # Arguments
204///
205/// - `&Path`: Target directory path
206/// - `&str`: Name of the component
207///
208/// # Returns
209///
210/// - `Result<(), TemplateError>`: Success or error
211async fn create_domain_template(
212    target_dir: &Path,
213    _component_name: &str,
214) -> Result<(), TemplateError> {
215    ensure_directory(target_dir).await?;
216    let mod_rs: PathBuf = target_dir.join("mod.rs");
217    write_mod_rs(&mod_rs, &["impl", "struct"]).await?;
218    let impl_rs: PathBuf = target_dir.join("impl.rs");
219    write(&impl_rs, "use super::*;\n").await?;
220    let struct_rs: PathBuf = target_dir.join("struct.rs");
221    write(&struct_rs, "use super::*;\n").await?;
222    Ok(())
223}
224
225/// Create mapper template files
226///
227/// # Arguments
228///
229/// - `&Path`: Target directory path
230/// - `&str`: Name of the component
231///
232/// # Returns
233///
234/// - `Result<(), TemplateError>`: Success or error
235async fn create_mapper_template(
236    target_dir: &Path,
237    _component_name: &str,
238) -> Result<(), TemplateError> {
239    ensure_directory(target_dir).await?;
240    let mod_rs: PathBuf = target_dir.join("mod.rs");
241    write_mod_rs(
242        &mod_rs,
243        &["const", "enum", "fn", "impl", "static", "struct"],
244    )
245    .await?;
246    let const_rs: PathBuf = target_dir.join("const.rs");
247    write(&const_rs, "use super::*;\n").await?;
248    let enum_rs: PathBuf = target_dir.join("enum.rs");
249    write(&enum_rs, "use super::*;\n").await?;
250    let fn_rs: PathBuf = target_dir.join("fn.rs");
251    write(&fn_rs, "use super::*;\n").await?;
252    let impl_rs: PathBuf = target_dir.join("impl.rs");
253    write(&impl_rs, "use super::*;\n").await?;
254    let static_rs: PathBuf = target_dir.join("static.rs");
255    write(&static_rs, "use super::*;\n").await?;
256    let struct_rs: PathBuf = target_dir.join("struct.rs");
257    write(&struct_rs, "use super::*;\n").await?;
258    Ok(())
259}
260
261/// Create utils template files
262///
263/// # Arguments
264///
265/// - `&Path`: Target directory path
266/// - `&str`: Name of the component
267///
268/// # Returns
269///
270/// - `Result<(), TemplateError>`: Success or error
271async fn create_utils_template(
272    target_dir: &Path,
273    _component_name: &str,
274) -> Result<(), TemplateError> {
275    ensure_directory(target_dir).await?;
276    let mod_rs: PathBuf = target_dir.join("mod.rs");
277    write_mod_rs(&mod_rs, &["fn"]).await?;
278    let fn_rs: PathBuf = target_dir.join("fn.rs");
279    write(&fn_rs, "use super::*;\n").await?;
280    Ok(())
281}
282
283/// Create exception template files
284///
285/// # Arguments
286///
287/// - `&Path`: Target directory path
288/// - `&str`: Name of the component
289///
290/// # Returns
291///
292/// - `Result<(), TemplateError>`: Success or error
293async fn create_exception_template(
294    target_dir: &Path,
295    _component_name: &str,
296) -> Result<(), TemplateError> {
297    ensure_directory(target_dir).await?;
298    let mod_rs: PathBuf = target_dir.join("mod.rs");
299    write_empty_mod_rs(&mod_rs).await?;
300    Ok(())
301}
302
303/// Create repository template files
304///
305/// # Arguments
306///
307/// - `&Path`: Target directory path
308/// - `&str`: Name of the component
309///
310/// # Returns
311///
312/// - `Result<(), TemplateError>`: Success or error
313async fn create_repository_template(
314    target_dir: &Path,
315    _component_name: &str,
316) -> Result<(), TemplateError> {
317    ensure_directory(target_dir).await?;
318    let mod_rs: PathBuf = target_dir.join("mod.rs");
319    write_mod_rs(&mod_rs, &["impl", "struct"]).await?;
320    let impl_rs: PathBuf = target_dir.join("impl.rs");
321    write(&impl_rs, "use super::*;\n").await?;
322    let struct_rs: PathBuf = target_dir.join("struct.rs");
323    write(&struct_rs, "use super::*;\n").await?;
324    Ok(())
325}
326
327/// Create model template files
328///
329/// # Arguments
330///
331/// - `&Path`: Target directory path
332/// - `&str`: Name of the component
333/// - `&ModelSubType`: Model subtype
334///
335/// # Returns
336///
337/// - `Result<(), TemplateError>`: Success or error
338async fn create_model_template(
339    target_dir: &Path,
340    _component_name: &str,
341    sub_type: &ModelSubType,
342) -> Result<(), TemplateError> {
343    let sub_type_name: String = get_model_sub_type_name(sub_type);
344    let model_dir: PathBuf = target_dir.join(&sub_type_name);
345    ensure_directory(&model_dir).await?;
346    let mod_rs: PathBuf = model_dir.join("mod.rs");
347    write_mod_rs(&mod_rs, &["struct"]).await?;
348    let struct_rs: PathBuf = model_dir.join("struct.rs");
349    write(&struct_rs, "use super::*;\n").await?;
350    Ok(())
351}
352
353/// Execute template generation
354///
355/// # Arguments
356///
357/// - `&TemplateType`: Type of template component
358/// - `&str`: Name of the component
359/// - `model_sub_type`: Optional model subtype
360///
361/// # Returns
362///
363/// - `Result<(), TemplateError>`: Success or error
364pub async fn execute_template(
365    template_type: TemplateType,
366    component_name: &str,
367    model_sub_type: Option<ModelSubType>,
368) -> Result<(), TemplateError> {
369    let config: TemplateConfig =
370        TemplateConfig::new(template_type, component_name.to_string(), model_sub_type);
371    let base_path: PathBuf = PathBuf::from(&config.base_directory);
372    let dir_name: String = get_directory_name(&config.template_type);
373    let type_dir: PathBuf = base_path.join(&dir_name);
374    let target_dir: PathBuf = type_dir.join(&config.component_name);
375    if target_dir.exists() {
376        return Err(TemplateError::DirectoryExists(
377            target_dir.to_string_lossy().to_string(),
378        ));
379    }
380    ensure_directory(&type_dir).await?;
381    match config.template_type {
382        TemplateType::Controller => {
383            create_controller_template(&target_dir, &config.component_name).await?
384        }
385        TemplateType::View => create_view_template(&target_dir, &config.component_name).await?,
386        TemplateType::Service => {
387            create_service_template(&target_dir, &config.component_name).await?
388        }
389        TemplateType::Domain => create_domain_template(&target_dir, &config.component_name).await?,
390        TemplateType::Mapper => create_mapper_template(&target_dir, &config.component_name).await?,
391        TemplateType::Utils => create_utils_template(&target_dir, &config.component_name).await?,
392        TemplateType::Exception => {
393            create_exception_template(&target_dir, &config.component_name).await?
394        }
395        TemplateType::Repository => {
396            create_repository_template(&target_dir, &config.component_name).await?
397        }
398        TemplateType::Model => {
399            let sub_type: ModelSubType = config.model_sub_type.ok_or_else(|| {
400                TemplateError::InvalidModelSubType("Missing model subtype".to_string())
401            })?;
402            create_model_template(&target_dir, &config.component_name, &sub_type).await?;
403        }
404    }
405    let _: Result<(), io::Error> = crate::fmt::format_path(&target_dir).await;
406    log::info!(
407        "Created {dir_name} '{}' at {}",
408        config.component_name,
409        target_dir.display()
410    );
411    Ok(())
412}