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