Skip to main content

scythe_codegen/
backend_trait.rs

1use scythe_core::analyzer::{AnalyzedQuery, CompositeInfo, EnumInfo};
2use scythe_core::errors::ScytheError;
3
4/// Information needed to generate an RBS type signature file.
5#[derive(Debug, Clone)]
6pub struct RbsGenerationContext {
7    pub queries: Vec<RbsQueryInfo>,
8    pub enums: Vec<RbsEnumInfo>,
9}
10
11/// Per-query info for RBS generation.
12#[derive(Debug, Clone)]
13pub struct RbsQueryInfo {
14    pub func_name: String,
15    pub struct_name: Option<String>,
16    pub columns: Vec<ResolvedColumn>,
17    pub params: Vec<ResolvedParam>,
18    pub command: scythe_core::parser::QueryCommand,
19}
20
21/// Per-enum info for RBS generation.
22#[derive(Debug, Clone)]
23pub struct RbsEnumInfo {
24    pub type_name: String,
25    pub values: Vec<String>,
26}
27
28/// A column with its type resolved to the target language.
29#[derive(Debug, Clone)]
30pub struct ResolvedColumn {
31    pub name: String,
32    pub field_name: String,
33    pub lang_type: String,
34    pub full_type: String,
35    pub neutral_type: String,
36    pub nullable: bool,
37}
38
39/// A parameter with its type resolved to the target language.
40#[derive(Debug, Clone)]
41pub struct ResolvedParam {
42    pub name: String,
43    pub field_name: String,
44    pub lang_type: String,
45    pub full_type: String,
46    pub borrowed_type: String,
47    pub neutral_type: String,
48    pub nullable: bool,
49}
50
51/// Trait that all codegen backends must implement.
52pub trait CodegenBackend: Send + Sync {
53    /// The backend's name (e.g. "rust-sqlx", "rust-tokio-postgres").
54    fn name(&self) -> &str;
55
56    /// The backend's manifest (type mappings, naming conventions, etc).
57    fn manifest(&self) -> &scythe_backend::manifest::BackendManifest;
58
59    /// Generate a row struct for a query result.
60    fn generate_row_struct(
61        &self,
62        query_name: &str,
63        columns: &[ResolvedColumn],
64    ) -> Result<String, ScytheError>;
65
66    /// Generate a model struct for a table.
67    fn generate_model_struct(
68        &self,
69        table_name: &str,
70        columns: &[ResolvedColumn],
71    ) -> Result<String, ScytheError>;
72
73    /// Generate a query function.
74    fn generate_query_fn(
75        &self,
76        analyzed: &AnalyzedQuery,
77        struct_name: &str,
78        columns: &[ResolvedColumn],
79        params: &[ResolvedParam],
80    ) -> Result<String, ScytheError>;
81
82    /// Generate an enum definition.
83    fn generate_enum_def(&self, enum_info: &EnumInfo) -> Result<String, ScytheError>;
84
85    /// Generate a composite type definition.
86    fn generate_composite_def(&self, composite: &CompositeInfo) -> Result<String, ScytheError>;
87
88    /// Generate a file-level header (imports, docstring, etc).
89    /// Returns an empty string by default; backends may override.
90    fn file_header(&self) -> String {
91        String::new()
92    }
93
94    /// Generate a file-level footer (closing braces, etc).
95    /// Returns an empty string by default; backends may override.
96    fn file_footer(&self) -> String {
97        String::new()
98    }
99
100    /// Generate a class header that wraps query functions only.
101    /// When non-empty, the assembly will emit all type definitions (enums,
102    /// row structs, model structs) first, then this class header, then all
103    /// query functions, then the file footer.
104    /// Returns an empty string by default (no class wrapper).
105    fn query_class_header(&self) -> String {
106        String::new()
107    }
108
109    /// Generate code that should be emitted after the file footer.
110    /// This is useful for backends that need top-level code after a class wrapper.
111    /// For example, C# extension methods must be top-level, not nested.
112    /// Returns an empty string by default.
113    fn post_footer(&self) -> String {
114        String::new()
115    }
116
117    /// Generate an RBS type signature file for Ruby backends.
118    /// Returns `None` by default; Ruby backends override this.
119    fn generate_rbs_file(&self, _context: &RbsGenerationContext) -> Option<String> {
120        None
121    }
122
123    /// Apply per-backend configuration options from [[sql.gen]].
124    /// Backends override this to handle options like `row_type = "pydantic"`.
125    fn apply_options(
126        &mut self,
127        _options: &std::collections::HashMap<String, String>,
128    ) -> Result<(), ScytheError> {
129        Ok(())
130    }
131
132    /// Database engines this backend supports.
133    /// Defaults to PostgreSQL only. Multi-DB backends override this.
134    fn supported_engines(&self) -> &[&str] {
135        &["postgresql"]
136    }
137}