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