pub trait CodegenBackend: Send + Sync {
Show 24 methods
// Required methods
fn name(&self) -> &str;
fn manifest(&self) -> &BackendManifest;
fn manifest_mut(&mut self) -> &mut BackendManifest;
fn generate_query_fn(
&self,
analyzed: &AnalyzedQuery,
struct_name: &str,
columns: &[ResolvedColumn],
params: &[ResolvedParam],
) -> Result<String, ScytheError>;
fn generate_enum_def(
&self,
enum_info: &EnumInfo,
) -> Result<String, ScytheError>;
fn generate_composite_def(
&self,
composite: &CompositeInfo,
) -> Result<String, ScytheError>;
// Provided methods
fn output_extension(&self) -> &str { ... }
fn generate_struct_decl(
&self,
struct_name: &str,
doc_name: &str,
columns: &[ResolvedColumn],
) -> Result<String, ScytheError> { ... }
fn generate_row_struct(
&self,
query_name: &str,
columns: &[ResolvedColumn],
) -> Result<String, ScytheError> { ... }
fn generate_model_struct(
&self,
table_name: &str,
columns: &[ResolvedColumn],
) -> Result<String, ScytheError> { ... }
fn file_preamble(&self) -> String { ... }
fn generate_nested_struct_def(
&self,
_nested: &NestedStructInfo,
) -> Result<Option<String>, ScytheError> { ... }
fn generate_enum_def_for_nested(
&self,
enum_info: &EnumInfo,
) -> Result<String, ScytheError> { ... }
fn generate_composite_def_for_nested(
&self,
composite: &CompositeInfo,
) -> Result<String, ScytheError> { ... }
fn file_header(&self) -> String { ... }
fn file_header_for_results(&self, _generated: &[GeneratedCode]) -> String { ... }
fn file_footer(&self) -> String { ... }
fn query_class_header(&self) -> String { ... }
fn post_footer(&self) -> String { ... }
fn generate_rbs_file(
&self,
_context: &RbsGenerationContext,
) -> Option<String> { ... }
fn generate_grouped_structs(
&self,
_parent_struct_name: &str,
_child_struct_name: &str,
_parent_columns: &[ResolvedColumn],
_child_columns: &[ResolvedColumn],
_key_column: &str,
) -> Result<String, ScytheError> { ... }
fn generate_grouped_query_fn(
&self,
_request: &GroupedQueryFn<'_>,
) -> Result<String, ScytheError> { ... }
fn apply_options(
&mut self,
options: &HashMap<String, String>,
) -> Result<(), ScytheError> { ... }
fn supported_engines(&self) -> &[&str] { ... }
}Expand description
Trait that all codegen backends must implement.
Required Methods§
Sourcefn manifest(&self) -> &BackendManifest
fn manifest(&self) -> &BackendManifest
The backend’s manifest (type mappings, naming conventions, etc).
Sourcefn manifest_mut(&mut self) -> &mut BackendManifest
fn manifest_mut(&mut self) -> &mut BackendManifest
Mutable access to the backend’s own manifest, so a per-target manifest
overlay (manifest = "..." in [[sql.gen]]) can be merged in right
after construction.
Deliberately required rather than defaulted: backends read
self.manifest directly for naming and type resolution, so a default
that silently did nothing would make manifest = "..." a no-op for
whichever backends forgot to override it — the exact class of silent,
per-backend divergence that #82 was filed for. Making it required
turns that into a compile error.
Sourcefn generate_query_fn(
&self,
analyzed: &AnalyzedQuery,
struct_name: &str,
columns: &[ResolvedColumn],
params: &[ResolvedParam],
) -> Result<String, ScytheError>
fn generate_query_fn( &self, analyzed: &AnalyzedQuery, struct_name: &str, columns: &[ResolvedColumn], params: &[ResolvedParam], ) -> Result<String, ScytheError>
Generate a query function.
Sourcefn generate_enum_def(&self, enum_info: &EnumInfo) -> Result<String, ScytheError>
fn generate_enum_def(&self, enum_info: &EnumInfo) -> Result<String, ScytheError>
Generate an enum definition.
Sourcefn generate_composite_def(
&self,
composite: &CompositeInfo,
) -> Result<String, ScytheError>
fn generate_composite_def( &self, composite: &CompositeInfo, ) -> Result<String, ScytheError>
Generate a composite type definition.
Provided Methods§
Sourcefn output_extension(&self) -> &str
fn output_extension(&self) -> &str
The file extension for this backend’s generated output, without a leading dot.
Defaults to the manifest’s [backend] file_extension, which is right
for every backend that owns its manifest. It is overridable because the
javascript-* backends deliberately reuse the TypeScript manifests —
they differ only in emit mode, so giving them their own manifests would
duplicate every type mapping to change one field. [backend] is not
overridable through a manifest overlay (it is identity, not
configuration), so the extension has to be expressible here instead.
Sourcefn generate_struct_decl(
&self,
struct_name: &str,
doc_name: &str,
columns: &[ResolvedColumn],
) -> Result<String, ScytheError>
fn generate_struct_decl( &self, struct_name: &str, doc_name: &str, columns: &[ResolvedColumn], ) -> Result<String, ScytheError>
Emit a row-shaped type declaration under an already final name.
struct_name is the exact identifier to declare — this method must
not case-convert it, and must not append
NamingConfig::row_suffix
or anything else to it. doc_name is prose only: the SQL-side name
(“GetActiveUsers”, “User”) that backends interpolate into a
docstring or moduledoc. Nothing may derive an identifier from it.
This split is the fix for #164. Both callers below used to compute a
name each and hand it to one method that suffixed whatever it got,
so generate_model_struct declared UserRow while the query
function referenced User. Now the name is decided once, above the
backend, and the backend only renders it.
§Provided, but not optional
The default returns ErrorCode::InternalError rather than
falling back to something plausible: a fallback that quietly
re-derived the name is precisely the defect this method exists to
remove. It is provided rather than required only so that a backend
which emits no types at all can override
generate_row_struct and
generate_model_struct directly and
never reach this. Any backend that does emit row types must
override this one and let both callers below default.
Sourcefn generate_row_struct(
&self,
query_name: &str,
columns: &[ResolvedColumn],
) -> Result<String, ScytheError>
fn generate_row_struct( &self, query_name: &str, columns: &[ResolvedColumn], ) -> Result<String, ScytheError>
Generate a row struct for a query result.
Applies the manifest’s struct_case and row_suffix to the query
name and renders through
generate_struct_decl.
Sourcefn generate_model_struct(
&self,
table_name: &str,
columns: &[ResolvedColumn],
) -> Result<String, ScytheError>
fn generate_model_struct( &self, table_name: &str, columns: &[ResolvedColumn], ) -> Result<String, ScytheError>
Generate a model struct for a table — the type a SELECT * shares
across every query over that table.
Names it through crate::model_struct_name, the same function
determine_struct_name uses to decide what the generated query
functions reference, so declaration and reference cannot disagree
(#164). Backends whose model type is not a row type at all (Exposed
emits a table object) override this.
Sourcefn file_preamble(&self) -> String
fn file_preamble(&self) -> String
Generate text that must be the literal first bytes of the generated
file — before any comment, including the provenance header that
assembly prepends ahead of file_header.
Some languages have syntax that loses its meaning, or becomes a
syntax error, if anything precedes it: PHP’s <?php tag must open
the file, and Ruby’s # frozen_string_literal: true magic comment is
only recognized on line 1 (or line 2 after a shebang) — a comment
placed above it makes Ruby silently ignore the pragma. Backends with
such a constraint override this method; everything else that may
legally follow a leading comment (imports, docstrings, #!-style
banners) stays in file_header.
Returns an empty string by default; backends may override.
Sourcefn generate_nested_struct_def(
&self,
_nested: &NestedStructInfo,
) -> Result<Option<String>, ScytheError>
fn generate_nested_struct_def( &self, _nested: &NestedStructInfo, ) -> Result<Option<String>, ScytheError>
Generate a struct definition for a nested-aggregate result shape
(json_agg(o.*), row_to_json(u.*), …). PostgreSQL only; see
scythe_core::analyzer::AnalyzedQuery::nested_structs.
§Opt-in, not opt-out
Returns Ok(None) by default — “I do not support this” — so a
backend is only at risk from this feature if it explicitly
overrides the method. crates/scythe-codegen/src/lib.rs rewrites
any column referencing a struct this returns Ok(None) for to a safe
fallback before type resolution. The default is plain json, so a
backend stays byte-identical to its pre-inference output; a manifest
may explicitly define the distinct json_array scalar when its
driver exposes an array-shaped JSON document as a structural value.
json_array is not the SQL-array container array<json>.
Deliberately not the same shape as generate_composite_def, which
always returns a definition (CompositeInfo only ever exists because
a column already referenced a real catalog composite; there is
nothing to opt out of). Overriding to return Ok(Some(_)) is safe
only when the backend’s row-decoding path actually deserializes the
resulting JSON into the generated type — a backend whose
json_nested<T> container merely names the type without decoding it
would produce code that compiles and is wrong, which is worse than
not supporting it. Err(_) is reserved for a genuine failure (e.g. a
field’s neutral type doesn’t resolve) and must propagate rather than
degrade.
An override must also gate on the engine it was constructed for,
not only on being a PostgreSQL backend: rust-tokio-postgres,
go-pgx and python-psycopg3 all list redshift in
Self::supported_engines, and Redshift has no json_agg.
Sourcefn generate_enum_def_for_nested(
&self,
enum_info: &EnumInfo,
) -> Result<String, ScytheError>
fn generate_enum_def_for_nested( &self, enum_info: &EnumInfo, ) -> Result<String, ScytheError>
Generate an enum definition for an enum reachable from a nested aggregate’s field list.
Same enum, different requirements: a value inside a json_agg result
arrives as JSON and is decoded by the language’s JSON library, not by
the database driver, so a backend whose ordinary
Self::generate_enum_def emits only driver traits (sqlx::Type,
postgres_types) must add the JSON ones here or the nested struct
will not satisfy its own Deserialize bound. Defaults to
generate_enum_def, which is correct for every backend that already
decodes enums as plain strings.
Sourcefn generate_composite_def_for_nested(
&self,
composite: &CompositeInfo,
) -> Result<String, ScytheError>
fn generate_composite_def_for_nested( &self, composite: &CompositeInfo, ) -> Result<String, ScytheError>
Composite counterpart of Self::generate_enum_def_for_nested, for a
composite type reachable from a nested aggregate’s field list.
Sourcefn file_header(&self) -> String
fn file_header(&self) -> String
Generate a file-level header (imports, docstring, etc). Returns an empty string by default; backends may override.
Sourcefn file_header_for_results(&self, _generated: &[GeneratedCode]) -> String
fn file_header_for_results(&self, _generated: &[GeneratedCode]) -> String
Generate a file-level header using already generated code as context.
Backends with conditional imports can inspect generated fragments and avoid broad engine-level import guesses. The default preserves existing behavior.
Generate a file-level footer (closing braces, etc). Returns an empty string by default; backends may override.
Sourcefn query_class_header(&self) -> String
fn query_class_header(&self) -> String
Generate a class header that wraps query functions only. When non-empty, the assembly will emit all type definitions (enums, row structs, model structs) first, then this class header, then all query functions, then the file footer. Returns an empty string by default (no class wrapper).
Generate code that should be emitted after the file footer. This is useful for backends that need top-level code after a class wrapper. For example, C# extension methods must be top-level, not nested. Returns an empty string by default.
Sourcefn generate_rbs_file(&self, _context: &RbsGenerationContext) -> Option<String>
fn generate_rbs_file(&self, _context: &RbsGenerationContext) -> Option<String>
Generate an RBS type signature file for Ruby backends.
Returns None by default; Ruby backends override this.
Sourcefn generate_grouped_structs(
&self,
_parent_struct_name: &str,
_child_struct_name: &str,
_parent_columns: &[ResolvedColumn],
_child_columns: &[ResolvedColumn],
_key_column: &str,
) -> Result<String, ScytheError>
fn generate_grouped_structs( &self, _parent_struct_name: &str, _child_struct_name: &str, _parent_columns: &[ResolvedColumn], _child_columns: &[ResolvedColumn], _key_column: &str, ) -> Result<String, ScytheError>
Generate parent and child structs for a :grouped query.
A :grouped query folds flat rows from a normal SQL SELECT into a nested
parent/child structure entirely on the client side — the SQL itself is
unchanged from a regular :many query.
§Struct layout
- Child struct (
child_struct_name): contains allchild_columns. Defined first in the output to avoid forward references. - Parent struct (
parent_struct_name): contains allparent_columnsplus one extra fieldchildren: Vec<child_struct_name>(or the language-native equivalent collection type).
§Grouping semantics
The generated query function fetches flat rows and folds them into an
order-preserving list of parent structs, appending each row’s child
fields to the matching parent’s collection. Equality on key_column is
the fold predicate.
§Parameters
parent_struct_name– fully qualified struct/class name for the parent (e.g."GetUsersWithOrdersRow").child_struct_name– fully qualified struct/class name for the child (e.g."GetUsersWithOrdersChildRow").parent_columns– resolved columns belonging to the parent table.child_columns– resolved columns belonging to child table(s).key_column– SQL column name used as the grouping key; identifies the boundary between parent groups (matches [GroupByConfig::key_column]).
§Return value
A string containing both struct definitions (child first, parent second),
stored in GeneratedCode::row_struct.
§Default implementation
Returns an error: “grouped queries are not yet supported by the ‘<name>’ backend”. Backends opt in by overriding this method.
Sourcefn generate_grouped_query_fn(
&self,
_request: &GroupedQueryFn<'_>,
) -> Result<String, ScytheError>
fn generate_grouped_query_fn( &self, _request: &GroupedQueryFn<'_>, ) -> Result<String, ScytheError>
Generate the query function for a :grouped query.
The function runs the flat SQL from analyzed.sql, decodes each row,
and folds the rows into an order-preserving Vec<parent_struct_name>
(or the language-native equivalent), grouping by key_column and
appending each row’s child fields to the matching parent’s children
collection.
§Parameters
All inputs are bundled in GroupedQueryFn: the analyzed query, both
generated struct names, the flat and split (parent/child) column sets,
the resolved params, and the grouping key column.
§Return value
A string containing the full query function definition, stored in
GeneratedCode::query_fn.
§Default implementation
Returns an error: “grouped queries are not yet supported by the ‘<name>’ backend”. Backends opt in by overriding this method.
Sourcefn apply_options(
&mut self,
options: &HashMap<String, String>,
) -> Result<(), ScytheError>
fn apply_options( &mut self, options: &HashMap<String, String>, ) -> Result<(), ScytheError>
Apply per-backend configuration options from [[sql.gen]].
Backends that take options override this: parse each one, and reject
anything else via crate::backend_options::reject_unknown_options
against that backend’s own known-key list – see any TypeScript
backend’s apply_options for the established shape.
The default rejects every key (an empty known-key list), which is the
correct behavior for the majority of backends that take no options at
all – and, just as importantly, is what a brand-new backend gets for
free if its author forgets to override this. Before #103 the default
was Ok(()), so any unrecognized key – a typo, or a real key that
TOML happily parsed but no override read – was silently discarded
everywhere except the 11 TypeScript backends, which called
reject_unknown_options explicitly. The same typo behaving
differently depending on target language was itself a trap; inverting
the default closes it for every backend at once instead of requiring
each one to opt in.
Sourcefn supported_engines(&self) -> &[&str]
fn supported_engines(&self) -> &[&str]
Database engines this backend supports. Defaults to PostgreSQL only. Multi-DB backends override this.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".