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 = find_override(overrides, &column_match, &col.neutral_type)
31 .unwrap_or(&col.neutral_type);
32
33 let (full_type, lang_type) =
34 resolve_type_pair(effective_neutral_type, manifest, col.nullable)
35 .map(|(f, l)| (f.into_owned(), l.into_owned()))
36 .map_err(|e| {
37 ScytheError::new(
38 ErrorCode::InternalError,
39 format!("type resolution failed for column '{}': {}", col.name, e),
40 )
41 })?;
42 Ok(ResolvedColumn {
43 name: col.name.clone(),
44 field_name: to_snake_case(&col.name).into_owned(),
45 lang_type,
46 full_type,
47 neutral_type: effective_neutral_type.to_string(),
48 nullable: col.nullable,
49 })
50 })
51 .collect()
52}
53
54pub fn resolve_params(
59 params: &[AnalyzedParam],
60 manifest: &BackendManifest,
61 overrides: &[TypeOverride],
62 source_table: &str,
63) -> Result<Vec<ResolvedParam>, ScytheError> {
64 params
65 .iter()
66 .map(|param| {
67 let column_match = if source_table.is_empty() {
68 String::new()
69 } else {
70 format!("{}.{}", source_table, param.name)
71 };
72 let effective_neutral_type =
73 find_override(overrides, &column_match, ¶m.neutral_type)
74 .unwrap_or(¶m.neutral_type);
75
76 let (full_type, lang_type) =
77 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"];
104 if copy_types.contains(&rust_type) {
105 return rust_type.to_string();
106 }
107 if rust_type == "String" {
109 return "&str".to_string();
110 }
111 if let Some(inner) = rust_type
113 .strip_prefix("Option<")
114 .and_then(|s| s.strip_suffix('>'))
115 {
116 let borrowed_inner = param_type_to_borrowed(inner);
117 return format!("Option<{}>", borrowed_inner);
118 }
119 if let Some(inner) = rust_type
121 .strip_prefix("Vec<")
122 .and_then(|s| s.strip_suffix('>'))
123 {
124 return format!("&[{}]", inner);
125 }
126 format!("&{}", rust_type)
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_param_type_to_borrowed_string() {
136 assert_eq!(param_type_to_borrowed("String"), "&str");
137 }
138
139 #[test]
140 fn test_param_type_to_borrowed_vec() {
141 assert_eq!(param_type_to_borrowed("Vec<i32>"), "&[i32]");
142 assert_eq!(param_type_to_borrowed("Vec<String>"), "&[String]");
143 }
144
145 #[test]
146 fn test_param_type_to_borrowed_passthrough() {
147 assert_eq!(param_type_to_borrowed("i32"), "i32");
148 assert_eq!(param_type_to_borrowed("i64"), "i64");
149 assert_eq!(param_type_to_borrowed("bool"), "bool");
150 assert_eq!(param_type_to_borrowed("f64"), "f64");
151 }
152
153 #[test]
154 fn test_param_type_to_borrowed_option_string() {
155 assert_eq!(param_type_to_borrowed("Option<String>"), "Option<&str>");
156 }
157
158 #[test]
159 fn test_param_type_to_borrowed_option_copy() {
160 assert_eq!(param_type_to_borrowed("Option<i32>"), "Option<i32>");
161 }
162
163 #[test]
164 fn test_param_type_to_borrowed_other() {
165 assert_eq!(param_type_to_borrowed("Uuid"), "&Uuid");
166 assert_eq!(param_type_to_borrowed("NaiveDateTime"), "&NaiveDateTime");
167 }
168}