doido_generators/generators/
generator_gen.rs1use crate::generator::{GeneratedFile, Generator};
2use crate::generators::{to_snake, to_table_name};
3use doido_core::{anyhow::anyhow, Result};
4
5pub struct GeneratorGenerator;
10
11impl Generator for GeneratorGenerator {
12 fn name(&self) -> &str {
13 "generator"
14 }
15
16 fn generate(&self, args: &[&str]) -> Result<Vec<GeneratedFile>> {
17 let name = args
18 .first()
19 .copied()
20 .ok_or_else(|| anyhow!("generator generator requires a name argument"))?;
21 let snake = to_snake(name);
22 let plural = to_table_name(name);
23 let base = format!("lib/generators/{snake}");
24
25 Ok(vec![
26 GeneratedFile {
29 path: format!("{base}/app/{plural}/{{snake}}.rs.template"),
30 content: SAMPLE.to_string(),
31 },
32 GeneratedFile {
33 path: format!("{base}/README.md"),
34 content: readme(&snake),
35 },
36 ])
37 }
38}
39
40const SAMPLE: &str = r#"// Generated by the `{snake}` project generator.
41// Tokens available: {name} {snake} {singular} {pascal} {Model} {plural} {Controller}
42pub struct {pascal} {
43 // TODO: fields
44}
45"#;
46
47fn readme(snake: &str) -> String {
48 format!(
49 r#"# `{snake}` generator
50
51A project-local generator. Every file under this directory is a template; the
52trailing `.template` extension (if present) is stripped from the output path.
53
54## Usage
55
56```
57doido generate {snake} <Name>
58```
59
60## Tokens
61
62Substituted in both file paths and file contents:
63
64| Token | Example for `BlogPost` |
65|-------|------------------------|
66| `{{name}}` | `BlogPost` (as typed) |
67| `{{snake}}` / `{{singular}}` | `blog_post` |
68| `{{pascal}}` / `{{Model}}` | `BlogPost` |
69| `{{plural}}` | `blog_posts` |
70| `{{Controller}}` | `BlogPostsController` |
71"#
72 )
73}