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
52fn ensure_directory(path: &Path) -> Result<(), TemplateError> {
53    if !path.exists() {
54        create_dir_all(path)?;
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
69fn 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)?;
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
120fn write_empty_mod_rs(path: &Path) -> Result<(), TemplateError> {
121    write(path, "\n")?;
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
135fn create_controller_template(
136    target_dir: &Path,
137    _component_name: &str,
138) -> Result<(), TemplateError> {
139    ensure_directory(target_dir)?;
140    let mod_rs: PathBuf = target_dir.join("mod.rs");
141    write_mod_rs(&mod_rs, &["fn", "impl", "struct"])?;
142    let fn_rs: PathBuf = target_dir.join("fn.rs");
143    write(&fn_rs, "use super::*;\n")?;
144    let impl_rs: PathBuf = target_dir.join("impl.rs");
145    write(&impl_rs, "use super::*;\n")?;
146    let struct_rs: PathBuf = target_dir.join("struct.rs");
147    write(&struct_rs, "use super::*;\n")?;
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
161fn create_view_template(target_dir: &Path, _component_name: &str) -> Result<(), TemplateError> {
162    ensure_directory(target_dir)?;
163    let mod_rs: PathBuf = target_dir.join("mod.rs");
164    write_mod_rs(&mod_rs, &["fn", "impl", "struct"])?;
165    let fn_rs: PathBuf = target_dir.join("fn.rs");
166    write(&fn_rs, "use super::*;\n")?;
167    let impl_rs: PathBuf = target_dir.join("impl.rs");
168    write(&impl_rs, "use super::*;\n")?;
169    let struct_rs: PathBuf = target_dir.join("struct.rs");
170    write(&struct_rs, "use super::*;\n")?;
171    Ok(())
172}
173
174/// Create service template files
175///
176/// # Arguments
177///
178/// - `&Path`: Target directory path
179/// - `&str`: Name of the component
180///
181/// # Returns
182///
183/// - `Result<(), TemplateError>`: Success or error
184fn create_service_template(target_dir: &Path, _component_name: &str) -> Result<(), TemplateError> {
185    ensure_directory(target_dir)?;
186    let mod_rs: PathBuf = target_dir.join("mod.rs");
187    write_mod_rs(&mod_rs, &["impl", "struct"])?;
188    let impl_rs: PathBuf = target_dir.join("impl.rs");
189    write(&impl_rs, "use super::*;\n")?;
190    let struct_rs: PathBuf = target_dir.join("struct.rs");
191    write(&struct_rs, "use super::*;\n")?;
192    Ok(())
193}
194
195/// Create domain template files
196///
197/// # Arguments
198///
199/// - `&Path`: Target directory path
200/// - `&str`: Name of the component
201///
202/// # Returns
203///
204/// - `Result<(), TemplateError>`: Success or error
205fn create_domain_template(target_dir: &Path, _component_name: &str) -> Result<(), TemplateError> {
206    ensure_directory(target_dir)?;
207    let mod_rs: PathBuf = target_dir.join("mod.rs");
208    write_mod_rs(&mod_rs, &["impl", "struct"])?;
209    let impl_rs: PathBuf = target_dir.join("impl.rs");
210    write(&impl_rs, "use super::*;\n")?;
211    let struct_rs: PathBuf = target_dir.join("struct.rs");
212    write(&struct_rs, "use super::*;\n")?;
213    Ok(())
214}
215
216/// Create mapper template files
217///
218/// # Arguments
219///
220/// - `&Path`: Target directory path
221/// - `&str`: Name of the component
222///
223/// # Returns
224///
225/// - `Result<(), TemplateError>`: Success or error
226fn create_mapper_template(target_dir: &Path, _component_name: &str) -> Result<(), TemplateError> {
227    ensure_directory(target_dir)?;
228    let mod_rs: PathBuf = target_dir.join("mod.rs");
229    write_mod_rs(
230        &mod_rs,
231        &["const", "enum", "fn", "impl", "static", "struct"],
232    )?;
233    let const_rs: PathBuf = target_dir.join("const.rs");
234    write(&const_rs, "use super::*;\n")?;
235    let enum_rs: PathBuf = target_dir.join("enum.rs");
236    write(&enum_rs, "use super::*;\n")?;
237    let fn_rs: PathBuf = target_dir.join("fn.rs");
238    write(&fn_rs, "use super::*;\n")?;
239    let impl_rs: PathBuf = target_dir.join("impl.rs");
240    write(&impl_rs, "use super::*;\n")?;
241    let static_rs: PathBuf = target_dir.join("static.rs");
242    write(&static_rs, "use super::*;\n")?;
243    let struct_rs: PathBuf = target_dir.join("struct.rs");
244    write(&struct_rs, "use super::*;\n")?;
245    Ok(())
246}
247
248/// Create utils template files
249///
250/// # Arguments
251///
252/// - `&Path`: Target directory path
253/// - `&str`: Name of the component
254///
255/// # Returns
256///
257/// - `Result<(), TemplateError>`: Success or error
258fn create_utils_template(target_dir: &Path, _component_name: &str) -> Result<(), TemplateError> {
259    ensure_directory(target_dir)?;
260    let mod_rs: PathBuf = target_dir.join("mod.rs");
261    write_mod_rs(&mod_rs, &["fn"])?;
262    let fn_rs: PathBuf = target_dir.join("fn.rs");
263    write(&fn_rs, "use super::*;\n")?;
264    Ok(())
265}
266
267/// Create exception template files
268///
269/// # Arguments
270///
271/// - `&Path`: Target directory path
272/// - `&str`: Name of the component
273///
274/// # Returns
275///
276/// - `Result<(), TemplateError>`: Success or error
277fn create_exception_template(
278    target_dir: &Path,
279    _component_name: &str,
280) -> Result<(), TemplateError> {
281    ensure_directory(target_dir)?;
282    let mod_rs: PathBuf = target_dir.join("mod.rs");
283    write_empty_mod_rs(&mod_rs)?;
284    Ok(())
285}
286
287/// Create repository template files
288///
289/// # Arguments
290///
291/// - `&Path`: Target directory path
292/// - `&str`: Name of the component
293///
294/// # Returns
295///
296/// - `Result<(), TemplateError>`: Success or error
297fn create_repository_template(
298    target_dir: &Path,
299    _component_name: &str,
300) -> Result<(), TemplateError> {
301    ensure_directory(target_dir)?;
302    let mod_rs: PathBuf = target_dir.join("mod.rs");
303    write_mod_rs(&mod_rs, &["impl", "struct"])?;
304    let impl_rs: PathBuf = target_dir.join("impl.rs");
305    write(&impl_rs, "use super::*;\n")?;
306    let struct_rs: PathBuf = target_dir.join("struct.rs");
307    write(&struct_rs, "use super::*;\n")?;
308    Ok(())
309}
310
311/// Create model template files
312///
313/// # Arguments
314///
315/// - `&Path`: Target directory path
316/// - `&str`: Name of the component
317/// - `&ModelSubType`: Model subtype
318///
319/// # Returns
320///
321/// - `Result<(), TemplateError>`: Success or error
322fn create_model_template(
323    target_dir: &Path,
324    _component_name: &str,
325    sub_type: &ModelSubType,
326) -> Result<(), TemplateError> {
327    let sub_type_name: String = get_model_sub_type_name(sub_type);
328    let model_dir: PathBuf = target_dir.join(&sub_type_name);
329    ensure_directory(&model_dir)?;
330    let mod_rs: PathBuf = model_dir.join("mod.rs");
331    write_mod_rs(&mod_rs, &["struct"])?;
332    let struct_rs: PathBuf = model_dir.join("struct.rs");
333    write(&struct_rs, "use super::*;\n")?;
334    Ok(())
335}
336
337/// Execute template generation
338///
339/// # Arguments
340///
341/// - `&TemplateType`: Type of template component
342/// - `&str`: Name of the component
343/// - `model_sub_type`: Optional model subtype
344///
345/// # Returns
346///
347/// - `Result<(), TemplateError>`: Success or error
348pub async fn execute_template(
349    template_type: TemplateType,
350    component_name: &str,
351    model_sub_type: Option<ModelSubType>,
352) -> Result<(), TemplateError> {
353    let config: TemplateConfig =
354        TemplateConfig::new(template_type, component_name.to_string(), model_sub_type);
355    let base_path: PathBuf = PathBuf::from(&config.base_directory);
356    let dir_name: String = get_directory_name(&config.template_type);
357    let type_dir: PathBuf = base_path.join(&dir_name);
358    let target_dir: PathBuf = type_dir.join(&config.component_name);
359    if target_dir.exists() {
360        return Err(TemplateError::DirectoryExists(
361            target_dir.to_string_lossy().to_string(),
362        ));
363    }
364    ensure_directory(&type_dir)?;
365    match config.template_type {
366        TemplateType::Controller => {
367            create_controller_template(&target_dir, &config.component_name)?
368        }
369        TemplateType::View => create_view_template(&target_dir, &config.component_name)?,
370        TemplateType::Service => create_service_template(&target_dir, &config.component_name)?,
371        TemplateType::Domain => create_domain_template(&target_dir, &config.component_name)?,
372        TemplateType::Mapper => create_mapper_template(&target_dir, &config.component_name)?,
373        TemplateType::Utils => create_utils_template(&target_dir, &config.component_name)?,
374        TemplateType::Exception => create_exception_template(&target_dir, &config.component_name)?,
375        TemplateType::Repository => {
376            create_repository_template(&target_dir, &config.component_name)?
377        }
378        TemplateType::Model => {
379            let sub_type: ModelSubType = config.model_sub_type.ok_or_else(|| {
380                TemplateError::InvalidModelSubType("Missing model subtype".to_string())
381            })?;
382            create_model_template(&target_dir, &config.component_name, &sub_type)?;
383        }
384    }
385    let _: Result<(), std::io::Error> = crate::fmt::format_path(&target_dir).await;
386    log::info!(
387        "Created {} '{}' at {}",
388        dir_name,
389        config.component_name,
390        target_dir.display()
391    );
392    Ok(())
393}