scythe_codegen/
resolve.rs1use scythe_backend::manifest::BackendManifest;
2use scythe_backend::naming::to_snake_case;
3use scythe_backend::types::resolve_type_pair;
4
5use scythe_core::analyzer::{AnalyzedColumn, AnalyzedParam};
6use scythe_core::errors::{ErrorCode, ScytheError};
7
8use crate::backend_trait::{ResolvedColumn, ResolvedParam};
9use crate::overrides::{TypeOverride, find_override};
10
11pub fn resolve_columns(
17 columns: &[AnalyzedColumn],
18 manifest: &BackendManifest,
19 overrides: &[TypeOverride],
20 source_table: &str,
21) -> Result<Vec<ResolvedColumn>, ScytheError> {
22 columns
23 .iter()
24 .map(|col| {
25 let column_match = if source_table.is_empty() {
26 String::new()
27 } else {
28 format!("{}.{}", source_table, col.name)
29 };
30 let effective_neutral_type =
31 find_override(overrides, &column_match, &col.neutral_type).unwrap_or(&col.neutral_type);
32
33 let (full_type, lang_type) = resolve_type_pair(effective_neutral_type, manifest, col.nullable)
34 .map(|(f, l)| (f.into_owned(), l.into_owned()))
35 .map_err(|e| {
36 ScytheError::new(
37 ErrorCode::InternalError,
38 format!("type resolution failed for column '{}': {}", col.name, e),
39 )
40 })?;
41 Ok(ResolvedColumn {
42 name: col.name.clone(),
43 field_name: to_snake_case(&col.name).into_owned(),
44 lang_type,
45 full_type,
46 neutral_type: effective_neutral_type.to_string(),
47 nullable: col.nullable,
48 join_group: col.join_group.clone(),
49 nullable_before_join: col.nullable_before_join,
50 sql_type: col.sql_type.clone(),
51 })
52 })
53 .collect()
54}
55
56pub fn resolve_params(
61 params: &[AnalyzedParam],
62 manifest: &BackendManifest,
63 overrides: &[TypeOverride],
64 source_table: &str,
65) -> Result<Vec<ResolvedParam>, ScytheError> {
66 params
67 .iter()
68 .map(|param| {
69 let column_match = if source_table.is_empty() {
70 String::new()
71 } else {
72 format!("{}.{}", source_table, param.name)
73 };
74 let effective_neutral_type =
75 find_override(overrides, &column_match, ¶m.neutral_type).unwrap_or(¶m.neutral_type);
76
77 let (full_type, lang_type) = resolve_type_pair(effective_neutral_type, manifest, param.nullable)
78 .map(|(f, l)| (f.into_owned(), l.into_owned()))
79 .map_err(|e| {
80 ScytheError::new(
81 ErrorCode::InternalError,
82 format!("type resolution failed for param '{}': {}", param.name, e),
83 )
84 })?;
85 let borrowed_type = param_type_to_borrowed(&full_type);
86 Ok(ResolvedParam {
87 name: param.name.clone(),
88 field_name: to_snake_case(¶m.name).into_owned(),
89 lang_type,
90 full_type,
91 borrowed_type,
92 neutral_type: effective_neutral_type.to_string(),
93 nullable: param.nullable,
94 })
95 })
96 .collect()
97}
98
99pub fn param_type_to_borrowed(rust_type: &str) -> String {
102 let copy_types = ["bool", "i16", "i32", "i64", "f32", "f64", "u64"];
103 if copy_types.contains(&rust_type) {
104 return rust_type.to_string();
105 }
106 if rust_type == "String" {
107 return "&str".to_string();
108 }
109 if let Some(inner) = rust_type.strip_prefix("Option<").and_then(|s| s.strip_suffix('>')) {
110 let borrowed_inner = param_type_to_borrowed(inner);
111 return format!("Option<{}>", borrowed_inner);
112 }
113 if let Some(inner) = rust_type.strip_prefix("Vec<").and_then(|s| s.strip_suffix('>')) {
114 return format!("&[{}]", inner);
115 }
116 format!("&{}", rust_type)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_param_type_to_borrowed_string() {
125 assert_eq!(param_type_to_borrowed("String"), "&str");
126 }
127
128 #[test]
129 fn test_param_type_to_borrowed_vec() {
130 assert_eq!(param_type_to_borrowed("Vec<i32>"), "&[i32]");
131 assert_eq!(param_type_to_borrowed("Vec<String>"), "&[String]");
132 }
133
134 #[test]
135 fn test_param_type_to_borrowed_passthrough() {
136 assert_eq!(param_type_to_borrowed("i32"), "i32");
137 assert_eq!(param_type_to_borrowed("i64"), "i64");
138 assert_eq!(param_type_to_borrowed("bool"), "bool");
139 assert_eq!(param_type_to_borrowed("f64"), "f64");
140 }
141
142 #[test]
143 fn test_param_type_to_borrowed_option_string() {
144 assert_eq!(param_type_to_borrowed("Option<String>"), "Option<&str>");
145 }
146
147 #[test]
148 fn test_param_type_to_borrowed_option_copy() {
149 assert_eq!(param_type_to_borrowed("Option<i32>"), "Option<i32>");
150 }
151
152 #[test]
153 fn test_param_type_to_borrowed_other() {
154 assert_eq!(param_type_to_borrowed("Uuid"), "&Uuid");
155 assert_eq!(param_type_to_borrowed("NaiveDateTime"), "&NaiveDateTime");
156 }
157}