1pub mod kvp;
2
3// Re-export
4pub use anyhow;
5pub use indoc::indoc;
6pub use lazy_static;
7pub use sqlez;
8
9#[cfg(any(test, feature = "test-support"))]
10use anyhow::Result;
11#[cfg(any(test, feature = "test-support"))]
12use sqlez::connection::Connection;
13#[cfg(any(test, feature = "test-support"))]
14use sqlez::domain::Domain;
15
16use sqlez::domain::Migrator;
17use sqlez::thread_safe_connection::ThreadSafeConnection;
18use std::fs::{create_dir_all, remove_dir_all};
19use std::path::Path;
20use util::channel::{ReleaseChannel, RELEASE_CHANNEL, RELEASE_CHANNEL_NAME};
21use util::paths::DB_DIR;
22
23const INITIALIZE_QUERY: &'static str = indoc! {"
24 PRAGMA journal_mode=WAL;
25 PRAGMA synchronous=NORMAL;
26 PRAGMA busy_timeout=1;
27 PRAGMA foreign_keys=TRUE;
28 PRAGMA case_sensitive_like=TRUE;
29"};
30
31/// Open or create a database at the given directory path.
32pub fn open_file_db<M: Migrator>() -> ThreadSafeConnection<M> {
33 // Use 0 for now. Will implement incrementing and clearing of old db files soon TM
34 let current_db_dir = (*DB_DIR).join(Path::new(&format!("0-{}", *RELEASE_CHANNEL_NAME)));
35
36 if *RELEASE_CHANNEL == ReleaseChannel::Dev && std::env::var("WIPE_DB").is_ok() {
37 remove_dir_all(¤t_db_dir).ok();
38 }
39
40 create_dir_all(¤t_db_dir).expect("Should be able to create the database directory");
41 let db_path = current_db_dir.join(Path::new("db.sqlite"));
42
43 ThreadSafeConnection::new(Some(db_path.to_string_lossy().as_ref()), true)
44 .with_initialize_query(INITIALIZE_QUERY)
45}
46
47pub fn open_memory_db<M: Migrator>(db_name: Option<&str>) -> ThreadSafeConnection<M> {
48 ThreadSafeConnection::new(db_name, false).with_initialize_query(INITIALIZE_QUERY)
49}
50
51#[cfg(any(test, feature = "test-support"))]
52pub fn write_db_to<D: Domain, P: AsRef<Path>>(
53 conn: &ThreadSafeConnection<D>,
54 dest: P,
55) -> Result<()> {
56 let destination = Connection::open_file(dest.as_ref().to_string_lossy().as_ref());
57 conn.backup_main(&destination)
58}
59
60/// Implements a basic DB wrapper for a given domain
61#[macro_export]
62macro_rules! connection {
63 ($id:ident: $t:ident<$d:ty>) => {
64 pub struct $t(::db::sqlez::thread_safe_connection::ThreadSafeConnection<$d>);
65
66 impl ::std::ops::Deref for $t {
67 type Target = ::db::sqlez::thread_safe_connection::ThreadSafeConnection<$d>;
68
69 fn deref(&self) -> &Self::Target {
70 &self.0
71 }
72 }
73
74 ::db::lazy_static::lazy_static! {
75 pub static ref $id: $t = $t(if cfg!(any(test, feature = "test-support")) {
76 ::db::open_memory_db(None)
77 } else {
78 ::db::open_file_db()
79 });
80 }
81 };
82}
83
84#[macro_export]
85macro_rules! sql_method {
86 ($id:ident() -> Result<()>: $sql:expr) => {
87 pub fn $id(&self) -> $crate::sqlez::anyhow::Result<()> {
88 use $crate::anyhow::Context;
89
90 self.exec($sql)?().context(::std::format!(
91 "Error in {}, exec failed to execute or parse for: {}",
92 ::std::stringify!($id),
93 ::std::stringify!($sql),
94 ))
95 }
96 };
97 ($id:ident($($arg:ident: $arg_type:ty),+) -> Result<()>: $sql:expr) => {
98 pub fn $id(&self, $($arg: $arg_type),+) -> $crate::sqlez::anyhow::Result<()> {
99 use $crate::anyhow::Context;
100
101 self.exec_bound::<($($arg_type),+)>($sql)?(($($arg),+))
102 .context(::std::format!(
103 "Error in {}, exec_bound failed to execute or parse for: {}",
104 ::std::stringify!($id),
105 ::std::stringify!($sql),
106 ))
107 }
108 };
109 ($id:ident() -> Result<Vec<$return_type:ty>>: $sql:expr) => {
110 pub fn $id(&self) -> $crate::sqlez::anyhow::Result<Vec<$return_type>> {
111 use $crate::anyhow::Context;
112
113 self.select::<$return_type>($sql)?(())
114 .context(::std::format!(
115 "Error in {}, select_row failed to execute or parse for: {}",
116 ::std::stringify!($id),
117 ::std::stringify!($sql),
118 ))
119 }
120 };
121 ($id:ident($($arg:ident: $arg_type:ty),+) -> Result<Vec<$return_type:ty>>: $sql:expr) => {
122 pub fn $id(&self, $($arg: $arg_type),+) -> $crate::sqlez::anyhow::Result<Vec<$return_type>> {
123 use $crate::anyhow::Context;
124
125 self.select_bound::<($($arg_type),+), $return_type>($sql)?(($($arg),+))
126 .context(::std::format!(
127 "Error in {}, exec_bound failed to execute or parse for: {}",
128 ::std::stringify!($id),
129 ::std::stringify!($sql),
130 ))
131 }
132 };
133 ($id:ident() -> Result<Option<$return_type:ty>>: $sql:expr) => {
134 pub fn $id(&self) -> $crate::sqlez::anyhow::Result<Option<$return_type>> {
135 use $crate::anyhow::Context;
136
137 self.select_row::<$return_type>($sql)?()
138 .context(::std::format!(
139 "Error in {}, select_row failed to execute or parse for: {}",
140 ::std::stringify!($id),
141 ::std::stringify!($sql),
142 ))
143 }
144 };
145 ($id:ident($($arg:ident: $arg_type:ty),+) -> Result<Option<$return_type:ty>>: $sql:expr) => {
146 pub fn $id(&self, $($arg: $arg_type),+) -> $crate::sqlez::anyhow::Result<Option<$return_type>> {
147 use $crate::anyhow::Context;
148
149 self.select_row_bound::<($($arg_type),+), $return_type>($sql)?(($($arg),+))
150 .context(::std::format!(
151 "Error in {}, select_row_bound failed to execute or parse for: {}",
152 ::std::stringify!($id),
153 ::std::stringify!($sql),
154 ))
155
156 }
157 };
158 ($id:ident() -> Result<$return_type:ty>>: $sql:expr) => {
159 pub fn $id(&self) -> $crate::sqlez::anyhow::Result<$return_type> {
160 use $crate::anyhow::Context;
161
162 self.select_row::<$return_type>($sql)?(($($arg),+))
163 .context(::std::format!(
164 "Error in {}, select_row_bound failed to execute or parse for: {}",
165 ::std::stringify!($id),
166 ::std::stringify!($sql),
167 ))?
168 .context(::std::format!(
169 "Error in {}, select_row_bound expected single row result but found none for: {}",
170 ::std::stringify!($id),
171 ::std::stringify!($sql),
172 ))
173 }
174 };
175 ($id:ident($($arg:ident: $arg_type:ty),+) -> Result<$return_type:ty>>: $sql:expr) => {
176 pub fn $id(&self, $($arg: $arg_type),+) -> $crate::sqlez::anyhow::Result<$return_type> {
177 use $crate::anyhow::Context;
178
179 self.select_row_bound::<($($arg_type),+), $return_type>($sql)?(($($arg),+))
180 .context(::std::format!(
181 "Error in {}, select_row_bound failed to execute or parse for: {}",
182 ::std::stringify!($id),
183 ::std::stringify!($sql),
184 ))?
185 .context(::std::format!(
186 "Error in {}, select_row_bound expected single row result but found none for: {}",
187 ::std::stringify!($id),
188 ::std::stringify!($sql),
189 ))
190 }
191 };
192}