1use std::{fmt, iter::repeat};
2
3#[inline]
9pub(crate) fn format_list<T: fmt::Display>(items: &[T], separator: &str) -> String {
10 items
11 .iter()
12 .map(|item| format!("{}", item).to_string())
13 .collect::<Vec<String>>()
14 .join(separator)
15}
16
17#[inline]
23pub(crate) fn format_iter<T: fmt::Display, I: IntoIterator<Item = T>>(
24 items: I,
25 separator: &str,
26) -> String {
27 items
28 .into_iter()
29 .map(|item| format!("{}", item).to_string())
30 .collect::<Vec<String>>()
31 .join(separator)
32}
33
34#[inline]
38pub(crate) fn ordered_keys(object: &serde_json::Map<String, serde_json::Value>) -> Vec<String> {
39 object.keys().map(|key| (*key).clone()).collect()
40}
41
42#[inline]
44pub(crate) fn to_numbered_placeholders(query: &str) -> String {
45 let mut result = String::new();
46 let mut counter = 1;
47
48 for c in query.chars() {
49 if c == '?' {
50 result.push_str(&format!("${counter}"));
51 counter += 1;
52 } else {
53 result.push(c);
54 }
55 }
56
57 result
58}
59
60#[inline]
63pub(crate) fn placeholders(count: usize) -> String {
64 let str_placeholders = repeat("?".to_string())
65 .take(count)
66 .collect::<Vec<String>>()
67 .join(", ");
68
69 format!("({str_placeholders})")
70}
71
72#[inline]
75pub(crate) fn repeat_placeholders(count: usize, n_repeat: usize) -> String {
76 repeat(placeholders(count))
77 .take(n_repeat)
78 .collect::<Vec<String>>()
79 .join(", ")
80}
81
82#[inline]
85pub(crate) fn sanitize_identifier(str: &str) -> String {
86 str.replace(|c: char| !c.is_alphanumeric() && c != '_', "")
87}
88
89#[inline]
91pub(crate) fn update_statement(table: &str, keys: &[String]) -> String {
92 let table = sanitize_identifier(table);
93 let columns = keys
94 .iter()
95 .map(|key| format!("\"{}\" = ?", sanitize_identifier(key)))
96 .collect::<Vec<String>>()
97 .join(", ");
98
99 format!("UPDATE {table} SET {columns} WHERE id = ? RETURNING *")
100}
101
102#[inline]
104pub(crate) fn insert_statement(table: &str, keys: &[String]) -> String {
105 let table = sanitize_identifier(table);
106 let values_placeholders = placeholders(keys.len());
107 let columns = format_iter(keys.iter().map(|s| sanitize_identifier(s)), ", ");
108
109 format!("INSERT INTO {table} ({columns}) VALUES {values_placeholders} RETURNING *")
110}
111
112#[inline]
115pub(crate) fn insert_many_statement(table: &str, keys: &[String], n_rows: usize) -> String {
116 let table = sanitize_identifier(table);
117 let values_placeholders = repeat_placeholders(keys.len(), n_rows);
118 let columns = format_iter(keys.iter().map(|s| sanitize_identifier(s)), ", ");
119
120 format!("INSERT INTO {table} ({columns}) VALUES {values_placeholders} RETURNING *")
121}
122
123#[inline]
125pub(crate) fn delete_statement(table: &str) -> String {
126 let table = sanitize_identifier(table);
127
128 format!("DELETE FROM {table} WHERE id = ? RETURNING *")
129}
130
131pub(crate) fn sql_like(filter: &str, value: &str) -> bool {
135 fn match_helper(f: &[char], v: &[char]) -> bool {
137 match (f, v) {
138 ([], []) => true,
140
141 ([first, rest @ ..], value) if *first == '%' => {
143 match_helper(rest, value) || (!value.is_empty() && match_helper(f, &value[1..]))
145 }
146
147 ([first, rest @ ..], [_, v_rest @ ..]) if *first == '_' => match_helper(rest, v_rest),
149
150 ([first, rest @ ..], [v_first, v_rest @ ..]) if first == v_first => {
152 match_helper(rest, v_rest)
153 }
154
155 _ => false,
157 }
158 }
159
160 match_helper(
162 &filter.chars().collect::<Vec<_>>(),
163 &value.chars().collect::<Vec<_>>(),
164 )
165}
166
167pub(crate) fn sql_ilike(filter: &str, value: &str) -> bool {
169 sql_like(&filter.to_lowercase(), &value.to_lowercase())
170}
171
172#[cfg(test)]
173mod test_utils {
174 use super::sql_like;
175
176 #[test]
177 fn test_sql_like() {
180 assert!(sql_like("he_lo", "hello"));
181 assert!(sql_like("h%o", "hello"));
182 assert!(!sql_like("h%o", "hi"));
183 assert!(sql_like("%", "anything"));
184 assert!(sql_like("_____", "12345"));
185 assert!(sql_like("_%_", "abc"));
186 assert!(sql_like("h_llo", "hello"));
187 assert!(!sql_like("he_lo", "heeeelo"));
188 }
189}