use crate::generator::{GeneratedFile, Generator};
use crate::generators::{to_snake, to_table_name};
use doido_core::{anyhow::anyhow, Result};
pub struct GeneratorGenerator;
impl Generator for GeneratorGenerator {
fn name(&self) -> &str {
"generator"
}
fn generate(&self, args: &[&str]) -> Result<Vec<GeneratedFile>> {
let name = args
.first()
.copied()
.ok_or_else(|| anyhow!("generator generator requires a name argument"))?;
let snake = to_snake(name);
let plural = to_table_name(name);
let base = format!("lib/generators/{snake}");
Ok(vec![
GeneratedFile {
path: format!("{base}/app/{plural}/{{snake}}.rs.template"),
content: SAMPLE.to_string(),
},
GeneratedFile {
path: format!("{base}/README.md"),
content: readme(&snake),
},
])
}
}
const SAMPLE: &str = r#"// Generated by the `{snake}` project generator.
// Tokens available: {name} {snake} {singular} {pascal} {Model} {plural} {Controller}
pub struct {pascal} {
// TODO: fields
}
"#;
fn readme(snake: &str) -> String {
format!(
r#"# `{snake}` generator
A project-local generator. Every file under this directory is a template; the
trailing `.template` extension (if present) is stripped from the output path.
## Usage
```
doido generate {snake} <Name>
```
## Tokens
Substituted in both file paths and file contents:
| Token | Example for `BlogPost` |
|-------|------------------------|
| `{{name}}` | `BlogPost` (as typed) |
| `{{snake}}` / `{{singular}}` | `blog_post` |
| `{{pascal}}` / `{{Model}}` | `BlogPost` |
| `{{plural}}` | `blog_posts` |
| `{{Controller}}` | `BlogPostsController` |
"#
)
}