Skip to main content

sqlcx_core/generator/rust_lang/
mod.rs

1pub mod common;
2pub mod serde_structs;
3pub mod sqlx_driver;
4pub mod tokio_postgres;
5
6use crate::config::TargetConfig;
7use crate::error::{Result, SqlcxError};
8use crate::generator::{DriverGenerator, GeneratedFile, LanguagePlugin, SchemaGenerator};
9use crate::ir::SqlcxIR;
10
11use self::serde_structs::SerdeStructGenerator;
12use self::sqlx_driver::SqlxGenerator;
13
14pub struct RustPlugin {
15    pub schema_name: String,
16    pub driver_name: String,
17}
18
19impl RustPlugin {
20    pub fn new(schema: &str, driver: &str) -> Result<Self> {
21        resolve_schema(schema)?;
22        resolve_driver(driver)?;
23        Ok(Self {
24            schema_name: schema.to_string(),
25            driver_name: driver.to_string(),
26        })
27    }
28}
29
30fn resolve_schema(name: &str) -> Result<Box<dyn SchemaGenerator>> {
31    match name {
32        "serde" => Ok(Box::new(SerdeStructGenerator)),
33        _ => Err(SqlcxError::UnknownSchema(name.to_string())),
34    }
35}
36
37fn resolve_driver(name: &str) -> Result<Box<dyn DriverGenerator>> {
38    match name {
39        "sqlx" | "sqlx-postgres" => Ok(Box::new(SqlxGenerator::postgres())),
40        "sqlx-mysql" => Ok(Box::new(SqlxGenerator::mysql())),
41        "sqlx-sqlite" => Ok(Box::new(SqlxGenerator::sqlite())),
42        "tokio-postgres" => Ok(Box::new(tokio_postgres::TokioPostgresGenerator)),
43        _ => Err(SqlcxError::UnknownDriver(name.to_string())),
44    }
45}
46
47impl LanguagePlugin for RustPlugin {
48    fn generate(&self, ir: &SqlcxIR, config: &TargetConfig) -> Result<Vec<GeneratedFile>> {
49        let schema_gen = resolve_schema(&self.schema_name)?;
50        let driver_gen = resolve_driver(&self.driver_name)?;
51        let overrides = &config.overrides;
52
53        let mut files = Vec::new();
54
55        // Schema file (models.rs)
56        files.push(schema_gen.generate(ir, overrides)?);
57
58        // Driver files (client.rs + *_queries.rs)
59        files.extend(driver_gen.generate(ir)?);
60
61        Ok(files)
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use crate::config::TargetConfig;
69    use crate::generator::LanguagePlugin;
70    use crate::parser::DatabaseParser;
71    use crate::parser::postgres::PostgresParser;
72    use std::collections::HashMap;
73
74    fn parse_fixture_ir() -> SqlcxIR {
75        let schema_sql = include_str!("../../../../../tests/fixtures/schema.sql");
76        let queries_sql = include_str!("../../../../../tests/fixtures/queries/users.sql");
77        let parser = PostgresParser::new();
78        let (tables, enums) = parser.parse_schema(schema_sql).unwrap();
79        let queries = parser
80            .parse_queries(queries_sql, &tables, &enums, "queries/users.sql")
81            .unwrap();
82        SqlcxIR {
83            tables,
84            queries,
85            enums,
86        }
87    }
88
89    #[test]
90    fn generates_three_files() {
91        let ir = parse_fixture_ir();
92        let plugin = RustPlugin::new("serde", "sqlx").unwrap();
93        let config = TargetConfig {
94            language: "rust".to_string(),
95            out: "./src/db".to_string(),
96            schema: "serde".to_string(),
97            driver: "sqlx".to_string(),
98            overrides: HashMap::new(),
99        };
100        let files = plugin.generate(&ir, &config).unwrap();
101        assert_eq!(files.len(), 3);
102        assert!(files.iter().any(|f| f.path == "models.rs"));
103        assert!(files.iter().any(|f| f.path == "client.rs"));
104        assert!(files.iter().any(|f| f.path == "users_queries.rs"));
105    }
106}