Skip to main content

drizzle_cli/
codegen.rs

1//! Rust schema source from a snapshot, shared by `introspect` / `pull` and
2//! `import`.
3
4use crate::config::IntrospectCasing;
5use crate::error::CliError;
6use drizzle_migrations::schema::Snapshot;
7
8/// Generated schema source plus counts for the command summary.
9#[derive(Debug, Clone, Default)]
10pub struct SchemaCode {
11    /// The Rust source.
12    pub code: String,
13    /// Number of tables written.
14    pub table_count: usize,
15    /// Number of indexes written.
16    pub index_count: usize,
17    /// Number of views written.
18    pub view_count: usize,
19    /// Objects the schema could not express, and similar notes.
20    pub warnings: Vec<String>,
21}
22
23/// Options for [`schema_code`].
24#[derive(Debug, Clone)]
25pub struct SchemaCodeOptions<'a> {
26    /// Field casing; `None` writes snake_case fields.
27    pub casing: Option<IntrospectCasing>,
28    /// Line added to the module doc comment.
29    pub module_doc: &'a str,
30    /// Name of the generated `#[derive(...Schema)]` struct.
31    pub schema_name: &'a str,
32}
33
34/// Generates the Rust schema for `snapshot` with the dialect's codegen.
35///
36/// # Errors
37///
38/// Returns [`CliError::Other`] if the MySQL codegen refuses a snapshot it
39/// cannot write without losing information.
40pub fn schema_code(
41    snapshot: &Snapshot,
42    options: &SchemaCodeOptions<'_>,
43) -> Result<SchemaCode, CliError> {
44    // Each dialect's codegen has its own (identical) `FieldCasing` enum.
45    macro_rules! field_casing {
46        ($casing:ty) => {
47            match options.casing {
48                Some(IntrospectCasing::Camel) => <$casing>::Camel,
49                Some(IntrospectCasing::Preserve) => <$casing>::Preserve,
50                None => <$casing>::Snake,
51            }
52        };
53    }
54
55    match snapshot {
56        Snapshot::Sqlite(snap) => {
57            use drizzle_migrations::sqlite::SQLiteDDL;
58            use drizzle_migrations::sqlite::codegen::{CodegenOptions, generate_rust_schema};
59
60            let ddl = SQLiteDDL::from_entities(snap.ddl.clone());
61            let generated = generate_rust_schema(
62                &ddl,
63                &CodegenOptions {
64                    module_doc: Some(options.module_doc.into()),
65                    include_schema: true,
66                    schema_name: options.schema_name.into(),
67                    use_pub: true,
68                    field_casing: field_casing!(drizzle_migrations::sqlite::codegen::FieldCasing),
69                },
70            );
71            Ok(SchemaCode {
72                code: generated.code,
73                table_count: generated.tables.len(),
74                index_count: generated.indexes.len(),
75                view_count: ddl.views.list().len(),
76                warnings: generated.warnings,
77            })
78        }
79        Snapshot::Postgres(snap) => {
80            use drizzle_migrations::postgres::PostgresDDL;
81            use drizzle_migrations::postgres::codegen::{CodegenOptions, generate_rust_schema};
82
83            let ddl = PostgresDDL::from_entities(snap.ddl.clone());
84            let generated = generate_rust_schema(
85                &ddl,
86                &CodegenOptions {
87                    module_doc: Some(options.module_doc.into()),
88                    include_schema: true,
89                    schema_name: options.schema_name.into(),
90                    use_pub: true,
91                    field_casing: field_casing!(drizzle_migrations::postgres::codegen::FieldCasing),
92                },
93            );
94            Ok(SchemaCode {
95                code: generated.code,
96                table_count: generated.tables.len(),
97                index_count: generated.indexes.len(),
98                view_count: generated.views.len(),
99                warnings: generated.warnings,
100            })
101        }
102        Snapshot::MySQL(snap) => {
103            use drizzle_migrations::mysql::MySQLDDL;
104            use drizzle_migrations::mysql::codegen::{CodegenOptions, generate_rust_schema};
105
106            let ddl = MySQLDDL::from_entities(snap.ddl.clone());
107            let generated = generate_rust_schema(
108                &ddl,
109                &CodegenOptions {
110                    module_doc: Some(options.module_doc.into()),
111                    include_schema: true,
112                    schema_name: options.schema_name.into(),
113                    use_pub: true,
114                    field_casing: field_casing!(drizzle_migrations::mysql::codegen::FieldCasing),
115                },
116            )
117            .map_err(|e| CliError::Other(format!("Cannot generate the MySQL schema: {e}")))?;
118            Ok(SchemaCode {
119                code: generated.code,
120                table_count: generated.tables.len(),
121                index_count: generated.indexes.len(),
122                view_count: generated.views.len(),
123                warnings: generated.warnings,
124            })
125        }
126    }
127}