1#[doc(inline)]
8pub use crate::sqlite::cflags::SqliteSyntaxFlag;
9
10#[derive(Debug, Clone, Copy, Default)]
20pub struct SqliteSyntaxFlags(pub(crate) ffi::CCflags);
21
22impl SqliteSyntaxFlags {
23 #[inline]
25 pub fn has(&self, flag: SqliteSyntaxFlag) -> bool {
26 self.0.has(flag as u32)
27 }
28
29 #[must_use]
31 pub fn with(mut self, flag: SqliteSyntaxFlag) -> Self {
32 self.0.set(flag as u32);
33 self
34 }
35}
36
37#[expect(missing_docs)]
42#[non_exhaustive]
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
44pub enum SqliteVersion {
45 V3_12,
46 V3_13,
47 V3_14,
48 V3_15,
49 V3_16,
50 V3_17,
51 V3_18,
52 V3_19,
53 V3_20,
54 V3_21,
55 V3_22,
56 V3_23,
57 V3_24,
58 V3_25,
59 V3_26,
60 V3_27,
61 V3_28,
62 V3_29,
63 V3_30,
64 V3_31,
65 V3_32,
66 V3_33,
67 V3_34,
68 V3_35,
69 V3_36,
70 V3_37,
71 V3_38,
72 V3_39,
73 V3_40,
74 V3_41,
75 V3_42,
76 V3_43,
77 V3_44,
78 V3_45,
79 V3_46,
80 V3_47,
81 V3_48,
82 V3_49,
83 V3_50,
84 V3_51,
85 Latest,
87}
88
89impl SqliteVersion {
90 pub fn parse_with_latest(s: &str) -> Result<Self, String> {
98 let s = s.trim();
99 if s.eq_ignore_ascii_case("latest") {
100 return Ok(Self::Latest);
101 }
102 Self::parse(s).ok_or_else(|| format!("unknown or unsupported SQLite version: '{s}'"))
103 }
104
105 pub fn parse(s: &str) -> Option<Self> {
110 let mut parts = s.splitn(3, '.');
111 let major: u32 = parts.next()?.parse().ok()?;
112 let minor: u32 = parts.next()?.parse().ok()?;
113 if major != 3 {
115 return None;
116 }
117 Some(match minor {
118 12 => Self::V3_12,
119 13 => Self::V3_13,
120 14 => Self::V3_14,
121 15 => Self::V3_15,
122 16 => Self::V3_16,
123 17 => Self::V3_17,
124 18 => Self::V3_18,
125 19 => Self::V3_19,
126 20 => Self::V3_20,
127 21 => Self::V3_21,
128 22 => Self::V3_22,
129 23 => Self::V3_23,
130 24 => Self::V3_24,
131 25 => Self::V3_25,
132 26 => Self::V3_26,
133 27 => Self::V3_27,
134 28 => Self::V3_28,
135 29 => Self::V3_29,
136 30 => Self::V3_30,
137 31 => Self::V3_31,
138 32 => Self::V3_32,
139 33 => Self::V3_33,
140 34 => Self::V3_34,
141 35 => Self::V3_35,
142 36 => Self::V3_36,
143 37 => Self::V3_37,
144 38 => Self::V3_38,
145 39 => Self::V3_39,
146 40 => Self::V3_40,
147 41 => Self::V3_41,
148 42 => Self::V3_42,
149 43 => Self::V3_43,
150 44 => Self::V3_44,
151 45 => Self::V3_45,
152 46 => Self::V3_46,
153 47 => Self::V3_47,
154 48 => Self::V3_48,
155 49 => Self::V3_49,
156 50 => Self::V3_50,
157 51 => Self::V3_51,
158 _ => return None,
159 })
160 }
161}
162
163impl SqliteVersion {
166 pub fn from_int(v: i32) -> Self {
171 match v {
172 3_012_000 => Self::V3_12,
173 3_013_000 => Self::V3_13,
174 3_014_000 => Self::V3_14,
175 3_015_000 => Self::V3_15,
176 3_016_000 => Self::V3_16,
177 3_017_000 => Self::V3_17,
178 3_018_000 => Self::V3_18,
179 3_019_000 => Self::V3_19,
180 3_020_000 => Self::V3_20,
181 3_021_000 => Self::V3_21,
182 3_022_000 => Self::V3_22,
183 3_023_000 => Self::V3_23,
184 3_024_000 => Self::V3_24,
185 3_025_000 => Self::V3_25,
186 3_026_000 => Self::V3_26,
187 3_027_000 => Self::V3_27,
188 3_028_000 => Self::V3_28,
189 3_029_000 => Self::V3_29,
190 3_030_000 => Self::V3_30,
191 3_031_000 => Self::V3_31,
192 3_032_000 => Self::V3_32,
193 3_033_000 => Self::V3_33,
194 3_034_000 => Self::V3_34,
195 3_035_000 => Self::V3_35,
196 3_036_000 => Self::V3_36,
197 3_037_000 => Self::V3_37,
198 3_038_000 => Self::V3_38,
199 3_039_000 => Self::V3_39,
200 3_040_000 => Self::V3_40,
201 3_041_000 => Self::V3_41,
202 3_042_000 => Self::V3_42,
203 3_043_000 => Self::V3_43,
204 3_044_000 => Self::V3_44,
205 3_045_000 => Self::V3_45,
206 3_046_000 => Self::V3_46,
207 3_047_000 => Self::V3_47,
208 3_048_000 => Self::V3_48,
209 3_049_000 => Self::V3_49,
210 3_050_000 => Self::V3_50,
211 3_051_000 => Self::V3_51,
212 _ => Self::Latest,
213 }
214 }
215
216 pub fn as_int(self) -> i32 {
221 match self {
222 Self::V3_12 => 3_012_000,
223 Self::V3_13 => 3_013_000,
224 Self::V3_14 => 3_014_000,
225 Self::V3_15 => 3_015_000,
226 Self::V3_16 => 3_016_000,
227 Self::V3_17 => 3_017_000,
228 Self::V3_18 => 3_018_000,
229 Self::V3_19 => 3_019_000,
230 Self::V3_20 => 3_020_000,
231 Self::V3_21 => 3_021_000,
232 Self::V3_22 => 3_022_000,
233 Self::V3_23 => 3_023_000,
234 Self::V3_24 => 3_024_000,
235 Self::V3_25 => 3_025_000,
236 Self::V3_26 => 3_026_000,
237 Self::V3_27 => 3_027_000,
238 Self::V3_28 => 3_028_000,
239 Self::V3_29 => 3_029_000,
240 Self::V3_30 => 3_030_000,
241 Self::V3_31 => 3_031_000,
242 Self::V3_32 => 3_032_000,
243 Self::V3_33 => 3_033_000,
244 Self::V3_34 => 3_034_000,
245 Self::V3_35 => 3_035_000,
246 Self::V3_36 => 3_036_000,
247 Self::V3_37 => 3_037_000,
248 Self::V3_38 => 3_038_000,
249 Self::V3_39 => 3_039_000,
250 Self::V3_40 => 3_040_000,
251 Self::V3_41 => 3_041_000,
252 Self::V3_42 => 3_042_000,
253 Self::V3_43 => 3_043_000,
254 Self::V3_44 => 3_044_000,
255 Self::V3_45 => 3_045_000,
256 Self::V3_46 => 3_046_000,
257 Self::V3_47 => 3_047_000,
258 Self::V3_48 => 3_048_000,
259 Self::V3_49 => 3_049_000,
260 Self::V3_50 => 3_050_000,
261 Self::V3_51 => 3_051_000,
262 Self::Latest => i32::MAX,
263 }
264 }
265}
266
267pub(crate) mod ffi {
270 #[repr(C)]
276 #[derive(Clone, Copy, Default)]
277 pub(crate) struct CCflags {
278 pub(super) bytes: [u8; 3],
279 }
280
281 #[expect(dead_code)]
282 impl CCflags {
283 pub(crate) const fn new() -> Self {
284 Self { bytes: [0; 3] }
285 }
286
287 #[inline]
288 pub(crate) fn has(self, idx: u32) -> bool {
289 let byte = idx / 8;
290 let bit = idx % 8;
291 (byte < 3) && (self.bytes[byte as usize] >> bit) & 1 != 0
292 }
293
294 #[inline]
295 pub(crate) fn set(&mut self, idx: u32) {
296 let byte = idx / 8;
297 let bit = idx % 8;
298 if byte < 3 {
299 self.bytes[byte as usize] |= 1 << bit;
300 }
301 }
302
303 #[inline]
304 pub(crate) fn clear(&mut self, idx: u32) {
305 let byte = idx / 8;
306 let bit = idx % 8;
307 if byte < 3 {
308 self.bytes[byte as usize] &= !(1 << bit);
309 }
310 }
311
312 #[inline]
313 pub(crate) fn clear_all(&mut self) {
314 self.bytes = [0; 3];
315 }
316 }
317
318 impl std::fmt::Debug for CCflags {
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 write!(f, "Cflags({:02x?})", &self.bytes)
321 }
322 }
323}
324
325pub fn is_suggestable_keyword(name: &str) -> bool {
333 !name.is_empty()
334 && name
335 .bytes()
336 .all(|b| b.is_ascii_uppercase() || b.is_ascii_digit() || b == b'_')
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn parse_major_minor() {
345 assert_eq!(SqliteVersion::parse("3.35"), Some(SqliteVersion::V3_35));
346 }
347
348 #[test]
349 fn parse_with_patch() {
350 assert_eq!(SqliteVersion::parse("3.35.5"), Some(SqliteVersion::V3_35));
351 }
352
353 #[test]
354 fn parse_unknown_minor() {
355 assert_eq!(SqliteVersion::parse("3.99"), None);
356 }
357
358 #[test]
359 fn as_int_spot_check() {
360 assert_eq!(SqliteVersion::V3_35.as_int(), 3_035_000);
361 assert_eq!(SqliteVersion::V3_51.as_int(), 3_051_000);
362 assert_eq!(SqliteVersion::Latest.as_int(), i32::MAX);
363 }
364}