squire_sqlite3_src/
lib.rs

1//! # squire-sqlite3-src
2//!
3//! This crate bundles the [SQLite][] source code for [Squire][]. When Squire’s
4//! `bundled` feature flag is enabled, SQLite is built from the `sqlite3.c` file
5//! included in this crate, and linked into the [`squire-sqlite3-sys`][sys]
6//! crate.
7//!
8//! Users of Squire don’t need to be aware of or interact with this crate. Cargo
9//! will automatically download and build it when you enable Squire’s `bundled`
10//! feature.
11//!
12//! [SQLite]: https://sqlite.org/
13//! [Squire]: https://github.com/silverlyra/squire
14//! [sys]: https://lib.rs/squire-sqlite3-sys
15
16use std::{collections::HashMap, env, iter, ops::Deref, path::PathBuf};
17
18use strum::{EnumDiscriminants, IntoDiscriminant};
19
20pub fn build(location: Location, config: impl AsRef<Config>) -> Build {
21    let config = config.as_ref();
22
23    let mut compiler = cc::Build::new();
24    compiler.file(location.input());
25
26    config.apply(&mut compiler);
27    compiler.warnings(false);
28
29    compiler.out_dir(&location.dest);
30    compiler.compile("sqlite3");
31
32    Build::new(location)
33}
34
35#[derive(PartialEq, Eq, Clone, Debug)]
36pub struct Build {
37    location: Location,
38}
39
40impl Build {
41    const fn new(location: Location) -> Build {
42        Self { location }
43    }
44
45    pub fn sources(&self) -> impl Iterator<Item = PathBuf> {
46        iter::once(self.input())
47    }
48}
49
50impl Deref for Build {
51    type Target = Location;
52
53    fn deref(&self) -> &Self::Target {
54        &self.location
55    }
56}
57
58#[derive(PartialEq, Eq, Clone, Debug)]
59pub struct Location {
60    src: PathBuf,
61    dest: PathBuf,
62}
63
64impl Location {
65    pub fn new(dest: impl Into<PathBuf>) -> Self {
66        Self {
67            src: PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("amalgamation"),
68            dest: dest.into(),
69        }
70    }
71
72    pub fn input(&self) -> PathBuf {
73        self.src.join("sqlite3.c")
74    }
75
76    pub fn header(&self) -> PathBuf {
77        self.src.join("sqlite3.h")
78    }
79
80    pub fn dest(&self) -> PathBuf {
81        self.dest.clone()
82    }
83
84    pub fn sources(&self) -> impl Iterator<Item = PathBuf> {
85        iter::once(self.input()).chain(iter::once(self.header()))
86    }
87}
88
89impl Default for Location {
90    fn default() -> Self {
91        Self::new(PathBuf::from(
92            env::var_os("OUT_DIR").expect("$OUT_DIR not set"),
93        ))
94    }
95}
96
97#[derive(PartialEq, Eq, Clone, Debug)]
98pub struct Config {
99    settings: HashMap<SettingKey, Setting>,
100}
101
102impl Config {
103    pub fn new(settings: impl IntoIterator<Item = Setting>) -> Self {
104        Self {
105            settings: settings
106                .into_iter()
107                .map(|setting| (setting.discriminant(), setting))
108                .collect(),
109        }
110    }
111
112    pub fn get(&self, key: SettingKey) -> Option<Setting> {
113        self.settings.get(&key).copied()
114    }
115
116    pub fn set(&mut self, setting: Setting) {
117        self.settings.insert(setting.discriminant(), setting);
118    }
119
120    fn apply(&self, build: &mut cc::Build) {
121        for setting in self.settings.values() {
122            setting.apply(build);
123        }
124    }
125}
126
127impl AsRef<Config> for Config {
128    fn as_ref(&self) -> &Config {
129        self
130    }
131}
132
133impl Default for Config {
134    fn default() -> Self {
135        Self::new(vec![
136            Setting::Sync(Synchronous::Full),
137            Setting::WalSync(Synchronous::Normal),
138            Setting::Threading(Threading::MultiThread),
139            Setting::DoubleQuotedStrings {
140                in_ddl: false,
141                in_dml: false,
142            },
143            Setting::DefaultForeignKeys(true),
144            Setting::DefaultMemoryStatus(false),
145            Setting::EnableAlloca(true),
146            Setting::EnableAutomaticIndex(true),
147            Setting::EnableAutomaticInitialize(true), // TODO
148            Setting::EnableColumnDeclaredType(false),
149            Setting::EnableDatabasePagesVirtualTable(false),
150            Setting::EnableDatabaseStatisticsVirtualTable(false),
151            Setting::EnableDatabaseUri(true),
152            Setting::EnableDeprecated(false),
153            Setting::EnableMemoryManagement(true),
154            Setting::EnableProgressCallback(false),
155            Setting::EnableSharedCache(false),
156            Setting::EnableTrace(false),
157            Setting::LikeOperatorMatchesBlob(false),
158            Setting::MaxExpressionDepth(0),
159            #[cfg(debug_assertions)]
160            Setting::EnableApiArmor(true),
161            #[cfg(debug_assertions)]
162            Setting::Debug(true),
163        ])
164    }
165}
166
167#[derive(EnumDiscriminants, PartialEq, Eq, Clone, Copy, Debug)]
168#[strum_discriminants(name(SettingKey))]
169#[strum_discriminants(derive(Hash))]
170pub enum Setting {
171    #[doc(alias = "SQLITE_DQS")]
172    DoubleQuotedStrings { in_ddl: bool, in_dml: bool },
173    #[doc(alias = "SQLITE_THREADSAFE")]
174    Threading(Threading),
175    #[doc(alias = "SQLITE_DEBUG")]
176    Debug(bool),
177    #[doc(alias = "SQLITE_DEFAULT_SYNCHRONOUS")]
178    Sync(Synchronous),
179    #[doc(alias = "SQLITE_DEFAULT_WAL_SYNCHRONOUS")]
180    WalSync(Synchronous),
181    #[doc(alias = "SQLITE_DEFAULT_AUTOMATIC_INDEX")]
182    DefaultAutomaticIndex(bool),
183    #[doc(alias = "SQLITE_DEFAULT_AUTOVACUUM")]
184    DefaultAutomaticVacuum(bool),
185    #[doc(alias = "SQLITE_DEFAULT_FOREIGN_KEYS")]
186    DefaultForeignKeys(bool),
187    #[doc(alias = "SQLITE_DEFAULT_MEMSTATUS")]
188    DefaultMemoryStatus(bool),
189    #[doc(alias = "SQLITE_USE_ALLOCA")]
190    EnableAlloca(bool),
191    #[doc(alias = "SQLITE_ENABLE_API_ARMOR")]
192    EnableApiArmor(bool),
193    #[doc(alias = "SQLITE_OMIT_AUTOMATIC_INDEX")]
194    EnableAutomaticIndex(bool),
195    #[doc(alias = "SQLITE_OMIT_AUTOINIT")]
196    EnableAutomaticInitialize(bool),
197    #[doc(alias = "SQLITE_OMIT_DECLTYPE")]
198    EnableColumnDeclaredType(bool),
199    #[doc(alias = "SQLITE_ENABLE_COLUMN_METADATA")]
200    EnableColumnMetadata(bool),
201    #[doc(alias = "SQLITE_ENABLE_DBPAGE_VTAB")]
202    EnableDatabasePagesVirtualTable(bool),
203    #[doc(alias = "SQLITE_ENABLE_DBSTAT_VTAB")]
204    EnableDatabaseStatisticsVirtualTable(bool),
205    #[doc(alias = "SQLITE_USE_URI")]
206    EnableDatabaseUri(bool),
207    #[doc(alias = "SQLITE_OMIT_DEPRECATED")]
208    EnableDeprecated(bool),
209    #[doc(alias = "SQLITE_ENABLE_GEOPOLY")]
210    EnableGeopoly(bool),
211    #[doc(alias = "SQLITE_ENABLE_FTS3")]
212    #[doc(alias = "SQLITE_ENABLE_FTS4")]
213    EnableFts3(bool),
214    #[doc(alias = "SQLITE_ENABLE_FTS5")]
215    EnableFts5(bool),
216    #[doc(alias = "SQLITE_OMIT_JSON")]
217    EnableJson(bool),
218    #[doc(alias = "SQLITE_OMIT_LOAD_EXTENSION")]
219    EnableLoadExtension(bool),
220    #[doc(alias = "SQLITE_ENABLE_MEMORY_MANAGEMENT")]
221    EnableMemoryManagement(bool),
222    #[doc(alias = "SQLITE_ENABLE_NORMALIZE")]
223    EnableNormalizeSql(bool),
224    #[doc(alias = "SQLITE_ENABLE_PREUPDATE_HOOK")]
225    EnablePreUpdateHook(bool),
226    #[doc(alias = "SQLITE_OMIT_PROGRESS_CALLBACK")]
227    EnableProgressCallback(bool),
228    #[doc(alias = "SQLITE_ENABLE_RTREE")]
229    EnableRtree(bool),
230    #[doc(alias = "SQLITE_ENABLE_STAT4")]
231    EnableStat4(bool),
232    #[doc(alias = "SQLITE_OMIT_DESERIALIZE")]
233    EnableSerialize(bool),
234    #[doc(alias = "SQLITE_ENABLE_SESSION")]
235    EnableSession(bool),
236    #[doc(alias = "SQLITE_ENABLE_SNAPSHOT")]
237    EnableSnapshot(bool),
238    #[doc(alias = "SQLITE_OMIT_SHARED_CACHE")]
239    EnableSharedCache(bool),
240    #[doc(alias = "SQLITE_SOUNDEX")]
241    EnableSoundex(bool),
242    #[doc(alias = "SQLITE_OMIT_TCL_VARIABLE")]
243    EnableTclVariables(bool),
244    #[doc(alias = "SQLITE_OMIT_TEMPDB")]
245    EnableTemporaryDatabase(bool),
246    #[doc(alias = "SQLITE_OMIT_TRACE")]
247    EnableTrace(bool),
248    #[doc(alias = "SQLITE_CASE_SENSITIVE_LIKE")]
249    LikeOperatorCaseSensitive(bool),
250    #[doc(alias = "SQLITE_LIKE_DOESNT_MATCH_BLOBS")]
251    LikeOperatorMatchesBlob(bool),
252    #[doc(alias = "SQLITE_MAX_ATTACHED")]
253    MaxAttachedDatabases(usize),
254    #[doc(alias = "SQLITE_MAX_COLUMN")]
255    MaxColumns(usize),
256    #[doc(alias = "SQLITE_MAX_EXPR_DEPTH")]
257    MaxExpressionDepth(usize),
258    #[doc(alias = "SQLITE_JSON_MAX_DEPTH")]
259    MaxJsonDepth(usize),
260    #[doc(alias = "SQLITE_MAX_VARIABLE_NUMBER")]
261    MaxVariables(usize),
262    #[doc(alias = "SQLITE_SECURE_DELETE")]
263    SecureDelete(bool),
264    #[doc(alias = "SQLITE_TEMP_STORE")]
265    TemporaryStorage(TemporaryStorage),
266    #[doc(alias = "SQLITE_TRUSTED_SCHEMA")]
267    TrustedSchema(bool),
268}
269
270impl Setting {
271    fn apply(&self, build: &mut cc::Build) {
272        match *self {
273            Setting::Debug(enable) => {
274                self.define(build, "SQLITE_DEBUG", enable);
275            }
276            Setting::DefaultAutomaticIndex(enable) => {
277                self.set(build, "SQLITE_DEFAULT_AUTOMATIC_INDEX", enable);
278            }
279            Setting::DefaultAutomaticVacuum(enable) => {
280                self.set(build, "SQLITE_DEFAULT_AUTOVACUUM", enable);
281            }
282            Setting::DefaultForeignKeys(enable) => {
283                self.set(build, "SQLITE_DEFAULT_FOREIGN_KEYS", enable);
284            }
285            Setting::DefaultMemoryStatus(enable) => {
286                self.set(build, "SQLITE_DEFAULT_MEMSTATUS", enable);
287            }
288            Setting::DoubleQuotedStrings { in_ddl, in_dml } => {
289                let value = match (in_ddl, in_dml) {
290                    (true, true) => 3,
291                    (true, false) => 2,
292                    (false, true) => 1,
293                    (false, false) => 0,
294                };
295
296                self.set(build, "SQLITE_DQS", value);
297            }
298            Setting::EnableAlloca(enable) => {
299                self.define(build, "SQLITE_USE_ALLOCA", enable);
300            }
301            Setting::EnableApiArmor(enable) => {
302                self.define(build, "SQLITE_ENABLE_API_ARMOR", enable);
303            }
304            Setting::EnableAutomaticIndex(enable) => {
305                self.define(build, "SQLITE_OMIT_AUTOMATIC_INDEX", !enable);
306            }
307            Setting::EnableAutomaticInitialize(enable) => {
308                self.define(build, "SQLITE_OMIT_AUTOINIT", !enable);
309            }
310            Setting::EnableColumnDeclaredType(enable) => {
311                self.define(build, "SQLITE_OMIT_DECLTYPE", !enable);
312            }
313            Setting::EnableColumnMetadata(enable) => {
314                self.define(build, "SQLITE_ENABLE_COLUMN_METADATA", enable);
315            }
316            Setting::EnableDatabasePagesVirtualTable(enable) => {
317                self.define(build, "SQLITE_ENABLE_DBPAGE_VTAB", enable);
318            }
319            Setting::EnableDatabaseStatisticsVirtualTable(enable) => {
320                self.define(build, "SQLITE_ENABLE_DBSTAT_VTAB", enable);
321            }
322            Setting::EnableDatabaseUri(enable) => {
323                self.set(build, "SQLITE_USE_URI", enable);
324            }
325            Setting::EnableDeprecated(enable) => {
326                self.define(build, "SQLITE_OMIT_DEPRECATED", !enable);
327            }
328            Setting::EnableFts3(enable) => {
329                self.define(build, "SQLITE_ENABLE_FTS3", enable);
330                self.define(build, "SQLITE_ENABLE_FTS3_PARENTHESIS", enable);
331            }
332            Setting::EnableFts5(enable) => {
333                self.define(build, "SQLITE_ENABLE_FTS5", enable);
334            }
335            Setting::EnableGeopoly(enable) => {
336                self.define(build, "SQLITE_ENABLE_GEOPOLY", enable);
337            }
338            Setting::EnableJson(enable) => {
339                self.define(build, "SQLITE_OMIT_JSON", !enable);
340            }
341            Setting::EnableLoadExtension(enable) => {
342                self.define(build, "SQLITE_OMIT_LOAD_EXTENSION", !enable);
343            }
344            Setting::EnableMemoryManagement(enable) => {
345                self.define(build, "SQLITE_ENABLE_MEMORY_MANAGEMENT", enable);
346            }
347            Setting::EnableNormalizeSql(enable) => {
348                self.define(build, "SQLITE_ENABLE_NORMALIZE", enable);
349            }
350            Setting::EnablePreUpdateHook(enable) => {
351                self.define(build, "SQLITE_ENABLE_PREUPDATE_HOOK", enable);
352            }
353            Setting::EnableProgressCallback(enable) => {
354                self.define(build, "SQLITE_OMIT_PROGRESS_CALLBACK", !enable);
355            }
356            Setting::EnableRtree(enable) => {
357                self.define(build, "SQLITE_ENABLE_RTREE", enable);
358            }
359            Setting::EnableSerialize(enable) => {
360                self.define(build, "SQLITE_OMIT_DESERIALIZE", !enable);
361            }
362            Setting::EnableSession(enable) => {
363                self.define(build, "SQLITE_ENABLE_SESSION", enable);
364            }
365            Setting::EnableSharedCache(enable) => {
366                self.define(build, "SQLITE_OMIT_SHARED_CACHE", !enable);
367            }
368            Setting::EnableSnapshot(enable) => {
369                self.define(build, "SQLITE_ENABLE_SNAPSHOT", enable);
370            }
371            Setting::EnableSoundex(enable) => {
372                self.define(build, "SQLITE_SOUNDEX", enable);
373            }
374            Setting::EnableStat4(enable) => {
375                self.define(build, "SQLITE_ENABLE_STAT4", enable);
376            }
377            Setting::EnableTclVariables(enable) => {
378                self.define(build, "SQLITE_OMIT_TCL_VARIABLE", !enable);
379            }
380            Setting::EnableTemporaryDatabase(enable) => {
381                self.define(build, "SQLITE_OMIT_TEMPDB", !enable);
382            }
383            Setting::EnableTrace(enable) => {
384                self.define(build, "SQLITE_OMIT_TRACE", !enable);
385            }
386            Setting::LikeOperatorCaseSensitive(enable) => {
387                self.define(build, "SQLITE_CASE_SENSITIVE_LIKE", enable);
388            }
389            Setting::LikeOperatorMatchesBlob(enable) => {
390                self.define(build, "SQLITE_LIKE_DOESNT_MATCH_BLOBS", !enable);
391            }
392            Setting::MaxAttachedDatabases(max) => {
393                self.set(build, "SQLITE_MAX_ATTACHED", max);
394            }
395            Setting::MaxColumns(max) => {
396                self.set(build, "SQLITE_MAX_COLUMN", max);
397            }
398            Setting::MaxExpressionDepth(max) => {
399                self.set(build, "SQLITE_MAX_EXPR_DEPTH", max);
400            }
401            Setting::MaxJsonDepth(max) => {
402                self.set(build, "SQLITE_JSON_MAX_DEPTH", max);
403            }
404            Setting::MaxVariables(max) => {
405                self.set(build, "SQLITE_MAX_VARIABLE_NUMBER", max);
406            }
407            Setting::SecureDelete(enable) => {
408                self.define(build, "SQLITE_SECURE_DELETE", enable);
409            }
410            Setting::Sync(synchronous) => {
411                self.set(build, "SQLITE_DEFAULT_SYNCHRONOUS", synchronous);
412            }
413            Setting::Threading(threading) => {
414                self.set(build, "SQLITE_THREADSAFE", threading);
415            }
416            Setting::TemporaryStorage(mode) => {
417                self.set(build, "SQLITE_TEMP_STORE", mode);
418            }
419            Setting::TrustedSchema(enable) => {
420                self.set(build, "SQLITE_TRUSTED_SCHEMA", enable);
421            }
422            Setting::WalSync(synchronous) => {
423                self.set(build, "SQLITE_DEFAULT_WAL_SYNCHRONOUS", synchronous);
424            }
425        }
426    }
427
428    fn define(&self, build: &mut cc::Build, name: &'static str, enable: bool) {
429        if enable {
430            build.define(name, None);
431        }
432    }
433
434    fn set(&self, build: &mut cc::Build, name: &'static str, value: impl SettingValue) {
435        value.apply(build, name);
436    }
437}
438
439#[derive(PartialEq, Eq, Clone, Copy, Debug)]
440#[repr(usize)]
441pub enum TemporaryStorage {
442    AlwaysFilesystem = 0,
443    DefaultFilesystem = 1,
444    DefaultMemory = 2,
445    AlwaysMemory = 3,
446}
447
448#[derive(PartialEq, Eq, Clone, Copy, Debug)]
449#[repr(usize)]
450pub enum Threading {
451    SingleThread = 0,
452    MultiThread = 1,
453    Serialized = 2,
454}
455
456#[derive(PartialEq, Eq, Clone, Copy, Debug)]
457#[repr(usize)]
458pub enum Synchronous {
459    Off = 0,
460    Normal = 1,
461    Full = 2,
462    Extra = 3,
463}
464
465trait SettingValue {
466    fn apply(&self, build: &mut cc::Build, name: &'static str);
467}
468
469impl SettingValue for bool {
470    fn apply(&self, build: &mut cc::Build, name: &'static str) {
471        let value = if *self { "1" } else { "0" };
472        build.define(name, value);
473    }
474}
475
476impl SettingValue for usize {
477    fn apply(&self, build: &mut cc::Build, name: &'static str) {
478        let value = self.to_string();
479        build.define(name, value.as_str());
480    }
481}
482
483impl SettingValue for TemporaryStorage {
484    fn apply(&self, build: &mut cc::Build, name: &'static str) {
485        (*self as usize).apply(build, name);
486    }
487}
488
489impl SettingValue for Threading {
490    fn apply(&self, build: &mut cc::Build, name: &'static str) {
491        (*self as usize).apply(build, name);
492    }
493}
494
495impl SettingValue for Synchronous {
496    fn apply(&self, build: &mut cc::Build, name: &'static str) {
497        (*self as usize).apply(build, name);
498    }
499}