easy_sqlx_core/sql/utils/
quote.rs

1pub enum QuotePolicy {
2    QuotePolicyAlways,
3    QuotePolicyNone,
4    QuotePolicyReserved,
5}
6
7const COMMAN_QUOTE_MARK: u8 = b'`';
8
9pub fn always_no_reserve(_str: &String) -> bool {
10    false
11}
12
13pub fn always_reserve(_str: &String) -> bool {
14    true
15}
16
17#[derive(Clone)]
18pub struct Quoter {
19    prefix: u8,
20    suffix: u8,
21    is_reserved: fn(str: &String) -> bool,
22}
23
24impl Quoter {
25    pub fn new(prefix: u8, suffix: u8, is_reserved: fn(str: &String) -> bool) -> Self {
26        Self {
27            prefix,
28            suffix,
29            is_reserved,
30        }
31    }
32
33    pub fn common_quoter() -> Self {
34        Self {
35            prefix: COMMAN_QUOTE_MARK,
36            suffix: COMMAN_QUOTE_MARK,
37            is_reserved: always_reserve,
38        }
39    }
40
41    pub fn is_empty(&self) -> bool {
42        return self.prefix == 0 && self.suffix == 0;
43    }
44
45    pub fn quote<'a>(&self, s: &String) -> String {
46        // String
47        // var buf strings.Builder
48        let mut buf = String::new();
49        self.quote_to(&mut buf, s);
50        return buf;
51        // s
52    }
53
54    pub fn join(&self, a: Vec<String>, sep: &String) -> String {
55        let mut buf = String::new();
56        self.join_write(&mut buf, a, sep);
57        return buf;
58    }
59
60    pub fn join_write(&self, b: &mut String, a: Vec<String>, sep: &String) {
61        if a.is_empty() {
62            return;
63        }
64
65        for (n, str) in a.iter().enumerate() {
66            if n > 0 {
67                b.push_str(sep.as_str());
68            }
69            self.quote_to(b, &str.trim().to_string());
70        }
71    }
72
73    // Trim removes quotes from s
74    pub fn trim(&self, s: String) -> String {
75        if s.len() < 2 {
76            return s;
77        }
78
79        let trim_start =
80            s.trim_start_matches(String::from_utf8(vec![self.prefix]).unwrap().as_str());
81        let trim_end =
82            trim_start.trim_end_matches(String::from_utf8(vec![self.suffix]).unwrap().as_str());
83        trim_end.to_string()
84        // // var buf strings.Builder
85        // for i := 0; i < len(s); i++ {
86        // 	switch {
87        // 	case i == 0 && s[i] == q.Prefix:
88        // 	case i == len(s)-1 && s[i] == q.Suffix:
89        // 	case s[i] == q.Suffix && s[i+1] == '.':
90        // 	case s[i] == q.Prefix && s[i-1] == '.':
91        // 	default:
92        // 		buf.WriteByte(s[i])
93        // 	}
94        // }
95        // return buf.String()
96    }
97
98    // QuoteTo quotes the table or column names. i.e. if the quotes are [ and ]
99    //   name -> [name]
100    //   `name` -> [name]
101    //   [name] -> [name]
102    //   schema.name -> [schema].[name]
103    //   `schema`.`name` -> [schema].[name]
104    //   `schema`.name -> [schema].[name]
105    //   schema.`name` -> [schema].[name]
106    //   [schema].name -> [schema].[name]
107    //   schema.[name] -> [schema].[name]
108    //   name AS a  ->  [name] AS a
109    //   schema.name AS a  ->  [schema].[name] AS a
110    pub fn quote_to(&self, buf: &mut String, value: &String) {
111        // var i int
112        let mut n = 0;
113        while n < value.len() {
114            let start = Quoter::find_start(value, n);
115            if start > n {
116                buf.push_str(&value.as_str()[n..start]);
117                // if _, err := buf.WriteString(value[i:start]); err != nil {
118                // 	return err
119                // }
120            }
121            if start == value.len() {
122                return;
123            }
124
125            let next_end = Quoter::find_word(value, start);
126            self.quote_word_to(buf, &value.as_str()[start..next_end].to_string());
127            // if err := q.quoteWordTo(buf, value[start:nextEnd]); err != nil {
128            // 	return err
129            // }
130            // i = nextEnd
131            n = next_end;
132        }
133    }
134
135    fn find_word(v: &String, start: usize) -> usize {
136        for (n, v) in v.char_indices() {
137            if n >= start && (v == '.' || v == ' ') {
138                return n;
139            }
140        }
141        v.len()
142        // for j := start; j < len(v); j++ {
143        //     switch v[j] {
144        //     case '.', ' ':
145        //         return j
146        //     }
147        // }
148        // return len(v)
149    }
150
151    fn find_start(value: &String, start: usize) -> usize {
152        // let mut chars = value.chars();
153
154        if let Some(ch) = value.chars().nth(start) {
155            if ch == '.' {
156                return start + 1;
157            }
158            // if ch == '(' {
159            //     return start + 1;
160            // }
161            if ch != ' ' {
162                return start;
163            }
164
165            let mut k: i32 = -1;
166            for (n, v) in value.char_indices() {
167                if n >= start && v != ' ' {
168                    k = n as i32;
169                    break;
170                }
171            }
172
173            if k == -1 {
174                return value.len();
175            }
176
177            let ch_k = value.chars().nth(k as usize).unwrap();
178            let ch_k_1 = value.chars().nth((k + 1) as usize).unwrap();
179
180            if (ch_k == 'A' || ch_k == 'a') && (ch_k_1 == 'S' || ch_k_1 == 's') {
181                k += 2;
182            }
183
184            for (n, v) in value.char_indices() {
185                if n >= (k as usize) && v != ' ' {
186                    return n;
187                }
188            }
189        }
190        return value.len();
191    }
192
193    fn quote_word_to(&self, buf: &mut String, word: &String) {
194        // buf.p
195        let mut real_word = word.clone();
196        let word_bytes = word.as_bytes();
197        if (word_bytes[0] == COMMAN_QUOTE_MARK
198            && word_bytes[word_bytes.len() - 1] == COMMAN_QUOTE_MARK)
199            || (word_bytes[0] == self.prefix && word_bytes[word_bytes.len() - 1] == self.suffix)
200        {
201            real_word = word.as_str()[1..word.len() - 1].to_string();
202        }
203
204        if self.is_empty() {
205            buf.push_str(&real_word);
206            return;
207        }
208
209        let is_reserved = (self.is_reserved)(&real_word);
210        if is_reserved && real_word != "*" {
211            buf.push(self.prefix.into());
212            // if err := buf.WriteByte(q.Prefix); err != nil {
213            //     return err
214            // }
215        }
216        buf.push_str(&real_word);
217        // if _, err := buf.WriteString(realWord); err != nil {
218        //     return err
219        // }
220        if is_reserved && real_word != "*" {
221            buf.push(self.suffix.into());
222        }
223    }
224
225    // Replace replaces common quote(`) as the quotes on the sql
226    pub fn replace(&self, sql: &String) -> String {
227        if self.is_empty() {
228            return sql.clone();
229        }
230
231        // var buf strings.Builder
232        // buf.Grow(len(sql))
233        let mark: char = COMMAN_QUOTE_MARK.into();
234
235        let mut buf = String::new();
236
237        let mut begin_single_quote: bool = false;
238        let chars = &mut sql.chars();
239        let len = chars.count();
240        let mut n: usize = 0;
241        loop {
242            if n >= len {
243                break;
244            }
245            let ch = chars.nth(n).unwrap();
246
247            if !begin_single_quote && ch == mark {
248                // var j = i + 1
249                let mut j = n + 1;
250                for (m, ch) in sql.char_indices() {
251                    if m >= j && ch == mark {
252                        break;
253                    }
254                    j += 1;
255                }
256                // for ; j < len(sql); j++ {
257                // 	if sql[j] == CommanQuoteMark {
258                // 		break
259                // 	}
260                // }
261                let word = sql.as_str()[n + 1..j].to_string();
262                let is_reserved = (self.is_reserved)(&word);
263
264                if is_reserved {
265                    buf.push(self.prefix.into());
266                    // buf.WriteByte(q.Prefix)
267                }
268                buf.push_str(&word);
269                // buf.WriteString(word)
270                if is_reserved {
271                    // buf.WriteByte(q.Suffix)
272                    buf.push(self.suffix.into());
273                }
274                n = j
275            } else {
276                if ch == '\'' {
277                    begin_single_quote = !begin_single_quote
278                }
279                buf.push(ch);
280                // buf.WriteByte(sql[i])
281            }
282
283            n += 1;
284        }
285        return buf;
286    }
287}