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