1use 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), 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}