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