sqlcx_core/
param_naming.rs1use std::collections::{HashMap, HashSet};
2
3pub struct RawParam {
4 pub index: u32,
5 pub column: Option<String>,
6 pub r#override: Option<String>,
7}
8
9pub fn resolve_param_names(params: &[RawParam]) -> Vec<String> {
10 let mut freq: HashMap<&str, u32> = HashMap::new();
12 for p in params {
13 if p.r#override.is_none()
14 && let Some(col) = &p.column
15 {
16 *freq.entry(col.as_str()).or_insert(0) += 1;
17 }
18 }
19
20 let mut counters: HashMap<&str, u32> = HashMap::new();
22 let mut seen: HashSet<String> = HashSet::new();
23 let mut result: Vec<String> = Vec::with_capacity(params.len());
24
25 for p in params {
26 let mut name = if let Some(ov) = &p.r#override {
27 ov.clone()
28 } else if let Some(col) = &p.column {
29 if freq.get(col.as_str()).copied().unwrap_or(0) > 1 {
30 let n = counters.entry(col.as_str()).or_insert(0);
31 *n += 1;
32 format!("{}_{}", col, n)
33 } else {
34 col.clone()
35 }
36 } else {
37 format!("param_{}", p.index)
38 };
39
40 let base = name.clone();
42 let mut suffix = 1u32;
43 while seen.contains(&name) {
44 name = format!("{}_{}", base, suffix);
45 suffix += 1;
46 }
47
48 seen.insert(name.clone());
49 result.push(name);
50 }
51
52 result
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn simple_column_name() {
61 let params = vec![RawParam {
62 index: 1,
63 column: Some("id".to_string()),
64 r#override: None,
65 }];
66 assert_eq!(resolve_param_names(¶ms), vec!["id"]);
67 }
68
69 #[test]
70 fn collision_adds_suffix() {
71 let params = vec![
72 RawParam {
73 index: 1,
74 column: Some("created_at".to_string()),
75 r#override: None,
76 },
77 RawParam {
78 index: 2,
79 column: Some("created_at".to_string()),
80 r#override: None,
81 },
82 ];
83 assert_eq!(
84 resolve_param_names(¶ms),
85 vec!["created_at_1", "created_at_2"]
86 );
87 }
88
89 #[test]
90 fn null_column_falls_back() {
91 let params = vec![RawParam {
92 index: 1,
93 column: None,
94 r#override: None,
95 }];
96 assert_eq!(resolve_param_names(¶ms), vec!["param_1"]);
97 }
98
99 #[test]
100 fn override_takes_precedence() {
101 let params = vec![RawParam {
102 index: 1,
103 column: Some("created_at".to_string()),
104 r#override: Some("start_date".to_string()),
105 }];
106 assert_eq!(resolve_param_names(¶ms), vec!["start_date"]);
107 }
108
109 #[test]
110 fn dedup_override_vs_inferred() {
111 let params = vec![
112 RawParam {
113 index: 1,
114 column: Some("id".to_string()),
115 r#override: Some("id".to_string()),
116 },
117 RawParam {
118 index: 2,
119 column: Some("id".to_string()),
120 r#override: None,
121 },
122 ];
123 let result = resolve_param_names(¶ms);
124 assert_eq!(result[0], "id");
125 assert_eq!(result[1], "id_1");
126 }
127}