Skip to main content

sqlcx_core/generator/go/common/
codegen.rs

1// Shared codegen helpers for Go drivers.
2//
3// Row struct, result struct, scan-field list, function signature, and SQL
4// escape are identical across database/sql and pgx. Client interfaces and
5// query-function bodies stay per-driver.
6
7use crate::generator::go::structs::{go_base_type, go_column_type};
8use crate::ir::{ColumnDef, QueryDef};
9use crate::utils::pascal_case;
10
11/// Generate the row struct for queries that return non-table columns.
12pub fn generate_row_struct(query: &QueryDef) -> Option<String> {
13    if query.returns.is_empty() {
14        return None;
15    }
16    let type_name = format!("{}Row", pascal_case(&query.name));
17    let fields: Vec<String> = query
18        .returns
19        .iter()
20        .map(|col| {
21            let field_name = pascal_case(col.alias.as_deref().unwrap_or(&col.name));
22            let field_type = go_column_type(col);
23            format!(
24                "\t{} {} `db:\"{}\" json:\"{}\"`",
25                field_name,
26                field_type,
27                col.alias.as_deref().unwrap_or(&col.name),
28                col.alias.as_deref().unwrap_or(&col.name),
29            )
30        })
31        .collect();
32    Some(format!(
33        "type {} struct {{\n{}\n}}",
34        type_name,
35        fields.join("\n")
36    ))
37}
38
39/// Generate a result struct for :execresult queries.
40pub fn generate_result_struct(query: &QueryDef) -> String {
41    let type_name = format!("{}Result", pascal_case(&query.name));
42    format!("type {} struct {{\n\tRowsAffected int64\n}}", type_name)
43}
44
45/// Generate scan fields (&i.FieldName) for a row.
46pub fn scan_fields(columns: &[ColumnDef]) -> String {
47    columns
48        .iter()
49        .map(|col| {
50            let field_name = pascal_case(col.alias.as_deref().unwrap_or(&col.name));
51            format!("&i.{}", field_name)
52        })
53        .collect::<Vec<_>>()
54        .join(", ")
55}
56
57/// Generate the function signature params for a query.
58pub fn func_params(query: &QueryDef) -> String {
59    if query.params.is_empty() {
60        return "ctx context.Context".to_string();
61    }
62    let params: Vec<String> = query
63        .params
64        .iter()
65        .map(|p| {
66            let col = ColumnDef {
67                name: p.name.clone(),
68                alias: None,
69                source_table: None,
70                sql_type: p.sql_type.clone(),
71                nullable: false,
72                has_default: false,
73            };
74            format!("{} {}", p.name, param_go_type(&col))
75        })
76        .collect();
77    format!("ctx context.Context, {}", params.join(", "))
78}
79
80/// Generate the args list for ExecContext/QueryContext/Exec/Query calls.
81pub fn query_args(query: &QueryDef) -> String {
82    if query.params.is_empty() {
83        return String::new();
84    }
85    let args: Vec<String> = query.params.iter().map(|p| p.name.clone()).collect();
86    format!(", {}", args.join(", "))
87}
88
89/// Escape a SQL string for embedding as a Go string literal.
90pub fn escape_sql(s: &str) -> String {
91    s.replace('\\', "\\\\")
92        .replace('"', "\\\"")
93        .replace('\n', "\\n")
94        .replace('\r', "\\r")
95        .replace('\t', "\\t")
96}
97
98/// Generate a Go function parameter type from a column (pointer if nullable).
99fn param_go_type(col: &ColumnDef) -> String {
100    if col.nullable {
101        format!("*{}", go_base_type(&col.sql_type))
102    } else {
103        go_base_type(&col.sql_type)
104    }
105}