Skip to main content

doido_auth/generators/
migration_support.rs

1//! Migration file rendering and registration — mirrors `doido-generators` helpers
2//! without depending on that crate.
3
4use super::template;
5
6/// Directory holding the SeaORM migration crate's sources.
7pub const MIGRATION_SRC_DIR: &str = "db/migration/src";
8
9/// Fallback migration `lib.rs` used when the app doesn't have one on disk yet.
10pub const MIGRATION_LIB_BASE: &str = include_str!("../../templates/new/db/migration/src/lib.rs");
11
12/// Renders a full migration file from the imports line and the `up`/`down` bodies.
13pub fn render_migration_file(
14    migration_name: &str,
15    imports: &str,
16    up_body: &str,
17    down_body: &str,
18) -> String {
19    template("migration.rs.template")
20        .replace("{migration_name}", migration_name)
21        .replace("{migration_imports}", imports)
22        .replace("{up_body}", up_body)
23        .replace("{down_body}", down_body)
24}
25
26/// Inserts a `mod <module>;` declaration and a `Box::new(<module>::Migration)`
27/// registration into the migration crate's `lib.rs`, just above the generator markers.
28pub fn register_migration(lib: &str, module: &str) -> String {
29    let mut lines: Vec<String> = lib.lines().map(String::from).collect();
30
31    if let Some(i) = lines
32        .iter()
33        .position(|l| l.contains("@generated-migrations-mod"))
34    {
35        let decl = format!("mod {module};");
36        if !lines.iter().any(|l| l.trim() == decl) {
37            lines.insert(i, decl);
38        }
39    }
40
41    if let Some(i) = lines
42        .iter()
43        .position(|l| l.contains("@generated-migrations-list"))
44    {
45        let indent: String = lines[i].chars().take_while(|c| c.is_whitespace()).collect();
46        let entry = format!("{indent}Box::new({module}::Migration),");
47        if !lines.iter().any(|l| l.trim() == entry.trim()) {
48            lines.insert(i, entry);
49        }
50    }
51
52    let mut out = lines.join("\n");
53    out.push('\n');
54    out
55}
56
57/// The `doido_model::migration` import line for a `create_table` migration.
58pub fn create_table_imports(fields: &[super::Field]) -> String {
59    if fields.iter().any(super::Field::wants_index) {
60        "use doido::model::migration::{add_index, create_table, drop_table};".to_string()
61    } else {
62        "use doido::model::migration::{create_table, drop_table};".to_string()
63    }
64}
65
66/// Builds the `up()` body for a `create_table` migration.
67pub fn create_table_up(table_name: &str, fields: &[super::Field]) -> String {
68    if fields.is_empty() {
69        return format!(
70            "        // `create_table` adds an auto-incrementing `id` primary key for you.\n\
71             \x20       create_table(manager, \"{table_name}\", |_t| {{}}).await\n"
72        );
73    }
74
75    let columns: String = fields
76        .iter()
77        .map(|f| format!("            {}\n", f.migration_line()))
78        .collect();
79
80    let indexes: Vec<&super::Field> = fields.iter().filter(|f| f.wants_index()).collect();
81
82    let mut body = String::new();
83    body.push_str(
84        "        // `create_table` adds an auto-incrementing `id` primary key for you.\n",
85    );
86    body.push_str(&format!(
87        "        create_table(manager, \"{table_name}\", |t| {{\n{columns}        }})\n"
88    ));
89
90    if indexes.is_empty() {
91        body.push_str("        .await\n");
92    } else {
93        body.push_str("        .await?;\n");
94        for f in indexes {
95            body.push_str(&format!(
96                "        add_index(manager, \"{table_name}\", &[\"{}\"]).await?;\n",
97                f.column_name()
98            ));
99        }
100        body.push_str("        Ok(())\n");
101    }
102
103    body
104}
105
106/// The `down()` body that drops a table.
107pub fn drop_table_down(table_name: &str) -> String {
108    format!("        drop_table(manager, \"{table_name}\").await\n")
109}