Skip to main content

doido_generators/generators/
generator_gen.rs

1use crate::generator::{GeneratedFile, Generator};
2use crate::generators::{to_snake, to_table_name};
3use doido_core::{anyhow::anyhow, Result};
4
5/// `doido generate generator <name>` — scaffolds a project-local custom
6/// generator under `lib/generators/<name>/`. It is a folder of template files;
7/// running `doido generate <name> <Thing>` substitutes tokens into each file's
8/// path and contents (see [`crate::project_generator`]).
9pub 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            // A sample template whose path + contents use generator tokens, so
27            // it adapts to whatever name the custom generator is later run with.
28            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}