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