Skip to main content

doido_auth/generators/
mod.rs

1//! Shared helpers for auth generators — migration rendering, name inflection,
2//! and route/module injection.
3
4pub mod field;
5pub mod migration_support;
6pub mod names;
7pub mod route_injector;
8
9pub use field::Field;
10pub use names::{to_pascal, to_snake, to_table_name};
11
12pub mod controller;
13pub mod install;
14pub mod scaffold;
15
16pub use controller::AuthControllerGenerator;
17pub use install::AuthInstallGenerator;
18pub use scaffold::AuthScaffoldGenerator;
19
20use doido_core::Result;
21
22/// A file emitted by an auth generator.
23#[derive(Debug, Clone)]
24pub struct GeneratedFile {
25    pub path: String,
26    pub content: String,
27}
28
29/// Auth generator contract — mirrors `doido_generators::Generator` without a
30/// crate dependency (avoids circular deps with `doido-generators`).
31pub trait AuthGenerator: Send + Sync {
32    fn name(&self) -> &str;
33    fn generate(&self, args: &[&str]) -> Result<Vec<GeneratedFile>>;
34}
35
36/// Implemented by `doido_generators::GeneratorRegistry` to merge auth generators.
37pub trait AuthGeneratorRegistry {
38    fn register_auth(&mut self, generator: Box<dyn AuthGenerator>);
39}
40
41/// Register all auth generators into `reg`.
42pub fn register(reg: &mut impl AuthGeneratorRegistry) {
43    reg.register_auth(Box::new(install::AuthInstallGenerator));
44    reg.register_auth(Box::new(controller::AuthControllerGenerator));
45    reg.register_auth(Box::new(scaffold::AuthScaffoldGenerator));
46}
47
48/// Load an embedded template from `doido-auth/templates/`.
49pub(crate) fn template(rel: &str) -> &'static str {
50    match rel {
51        "migration.rs.template" => include_str!("../../templates/migration.rs.template"),
52        "user.rs.template" => include_str!("../../templates/user.rs.template"),
53        "user_entity.rs.template" => include_str!("../../templates/user_entity.rs.template"),
54        "auth/mod.rs.template" => include_str!("../../templates/auth/mod.rs.template"),
55        "auth/sessions_controller_html.rs.template" => {
56            include_str!("../../templates/auth/sessions_controller_html.rs.template")
57        }
58        "auth/sessions_controller_api.rs.template" => {
59            include_str!("../../templates/auth/sessions_controller_api.rs.template")
60        }
61        "auth/registrations_controller_html.rs.template" => {
62            include_str!("../../templates/auth/registrations_controller_html.rs.template")
63        }
64        "auth/registrations_controller_api.rs.template" => {
65            include_str!("../../templates/auth/registrations_controller_api.rs.template")
66        }
67        "auth/passwords_controller_html.rs.template" => {
68            include_str!("../../templates/auth/passwords_controller_html.rs.template")
69        }
70        "auth/passwords_controller_api.rs.template" => {
71            include_str!("../../templates/auth/passwords_controller_api.rs.template")
72        }
73        "auth/oauth_controller.rs.template" => {
74            include_str!("../../templates/auth/oauth_controller.rs.template")
75        }
76        "auth/two_factor_controller_html.rs.template" => {
77            include_str!("../../templates/auth/two_factor_controller_html.rs.template")
78        }
79        "auth/two_factor_controller_api.rs.template" => {
80            include_str!("../../templates/auth/two_factor_controller_api.rs.template")
81        }
82        "auth/views/sign_in.html.tera" => {
83            include_str!("../../templates/auth/views/sign_in.html.tera")
84        }
85        "auth/views/sign_up.html.tera" => {
86            include_str!("../../templates/auth/views/sign_up.html.tera")
87        }
88        "auth/views/password_new.html.tera" => {
89            include_str!("../../templates/auth/views/password_new.html.tera")
90        }
91        "auth/views/password_edit.html.tera" => {
92            include_str!("../../templates/auth/views/password_edit.html.tera")
93        }
94        "auth/views/two_factor.html.tera" => {
95            include_str!("../../templates/auth/views/two_factor.html.tera")
96        }
97        "auth_controller.rs.template" => {
98            include_str!("../../templates/auth_controller.rs.template")
99        }
100        "scaffold/controller_html.rs.template" => {
101            include_str!("../../templates/scaffold/controller_html.rs.template")
102        }
103        "scaffold/controller_api.rs.template" => {
104            include_str!("../../templates/scaffold/controller_api.rs.template")
105        }
106        "scaffold/model.rs.template" => include_str!("../../templates/scaffold/model.rs.template"),
107        "scaffold/entity.rs.template" => {
108            include_str!("../../templates/scaffold/entity.rs.template")
109        }
110        "scaffold/views/index.html.tera" => {
111            include_str!("../../templates/scaffold/views/index.html.tera")
112        }
113        "scaffold/views/show.html.tera" => {
114            include_str!("../../templates/scaffold/views/show.html.tera")
115        }
116        "scaffold/views/new.html.tera" => {
117            include_str!("../../templates/scaffold/views/new.html.tera")
118        }
119        "scaffold/views/edit.html.tera" => {
120            include_str!("../../templates/scaffold/views/edit.html.tera")
121        }
122        "scaffold/views/_form.html.tera" => {
123            include_str!("../../templates/scaffold/views/_form.html.tera")
124        }
125        other => panic!("unknown auth generator template: {other}"),
126    }
127}
128
129/// Append `pub mod <module>;` into a directory `mod.rs`, just above `marker`.
130pub(crate) fn register_module(existing: &str, module: &str, marker: &str) -> String {
131    let decl = format!("pub mod {module};");
132    if existing.lines().any(|l| l.trim() == decl) {
133        return existing.to_string();
134    }
135    let mut lines: Vec<String> = existing.lines().map(String::from).collect();
136    match lines.iter().position(|l| l.contains(marker)) {
137        Some(i) => lines.insert(i, decl),
138        None => lines.push(decl),
139    }
140    let mut out = lines.join("\n");
141    out.push('\n');
142    out
143}