1pub mod addresses;
6pub mod credit_cards;
7pub mod models;
8pub mod passports;
9pub mod schema;
10pub mod store;
11
12use crate::error::*;
13
14use interrupt_support::{SqlInterruptHandle, SqlInterruptScope};
15use rusqlite::{Connection, OpenFlags};
16use sql_support::open_database;
17use std::sync::Arc;
18use std::{
19 ops::{Deref, DerefMut},
20 path::{Path, PathBuf},
21};
22use url::Url;
23
24pub struct AutofillDb {
25 pub writer: Connection,
26 interrupt_handle: Arc<SqlInterruptHandle>,
27}
28
29impl AutofillDb {
30 pub fn new(db_path: impl AsRef<Path>) -> Result<Self> {
31 let db_path = normalize_path(db_path)?;
32 Self::new_named(db_path)
33 }
34
35 pub fn new_memory(db_path: &str) -> Result<Self> {
36 let name = PathBuf::from(format!("file:{}?mode=memory&cache=shared", db_path));
37 Self::new_named(name)
38 }
39
40 fn new_named(db_path: PathBuf) -> Result<Self> {
41 let flags = OpenFlags::SQLITE_OPEN_NO_MUTEX
44 | OpenFlags::SQLITE_OPEN_URI
45 | OpenFlags::SQLITE_OPEN_CREATE
46 | OpenFlags::SQLITE_OPEN_READ_WRITE;
47
48 let conn = open_database::open_database_with_flags(
49 db_path,
50 flags,
51 &schema::AutofillConnectionInitializer,
52 )?;
53
54 Ok(Self {
55 interrupt_handle: Arc::new(SqlInterruptHandle::new(&conn)),
56 writer: conn,
57 })
58 }
59
60 #[inline]
61 pub fn begin_interrupt_scope(&self) -> Result<SqlInterruptScope> {
62 Ok(self.interrupt_handle.begin_interrupt_scope()?)
63 }
64}
65
66impl Deref for AutofillDb {
67 type Target = Connection;
68
69 fn deref(&self) -> &Self::Target {
70 &self.writer
71 }
72}
73
74impl DerefMut for AutofillDb {
75 fn deref_mut(&mut self) -> &mut Self::Target {
76 &mut self.writer
77 }
78}
79
80fn unurl_path(p: impl AsRef<Path>) -> PathBuf {
81 p.as_ref()
82 .to_str()
83 .and_then(|s| Url::parse(s).ok())
84 .and_then(|u| {
85 if u.scheme() == "file" {
86 u.to_file_path().ok()
87 } else {
88 None
89 }
90 })
91 .unwrap_or_else(|| p.as_ref().to_owned())
92}
93
94fn normalize_path(p: impl AsRef<Path>) -> Result<PathBuf> {
95 let path = unurl_path(p);
96 if let Ok(canonical) = path.canonicalize() {
97 return Ok(canonical);
98 }
99 let file_name = path
109 .file_name()
110 .ok_or_else(|| Error::IllegalDatabasePath(path.clone()))?;
111
112 let parent = path
113 .parent()
114 .ok_or_else(|| Error::IllegalDatabasePath(path.clone()))?;
115
116 let mut canonical = parent.canonicalize()?;
117 canonical.push(file_name);
118 Ok(canonical)
119}
120
121pub(crate) mod sql_fns {
122 use rusqlite::{functions::Context, Result};
123 use sync_guid::Guid as SyncGuid;
124 use types::Timestamp;
125
126 #[inline(never)]
127 #[allow(dead_code)]
128 pub fn generate_guid(_ctx: &Context<'_>) -> Result<SyncGuid> {
129 Ok(SyncGuid::random())
130 }
131
132 #[inline(never)]
133 pub fn now(_ctx: &Context<'_>) -> Result<Timestamp> {
134 Ok(Timestamp::now())
135 }
136}
137
138#[cfg(test)]
140pub mod test {
141 use super::*;
142 use std::sync::atomic::{AtomicUsize, Ordering};
143
144 static ATOMIC_COUNTER: AtomicUsize = AtomicUsize::new(0);
146
147 pub fn new_mem_db() -> AutofillDb {
148 error_support::init_for_tests();
149 let counter = ATOMIC_COUNTER.fetch_add(1, Ordering::Relaxed);
150 AutofillDb::new_memory(&format!("test_autofill-api-{}", counter))
151 .expect("should get an API")
152 }
153}