sos_database/entity/
preference.rs

1use crate::{Error, Result};
2use async_sqlite::rusqlite::{
3    Connection, Error as SqlError, OptionalExtension, Row,
4};
5use sos_core::UtcDateTime;
6use sos_preferences::Preference;
7use sql_query_builder as sql;
8use std::ops::Deref;
9
10/// Preference row from the database.
11#[doc(hidden)]
12#[derive(Debug, Default)]
13pub struct PreferenceRow {
14    pub row_id: i64,
15    created_at: String,
16    modified_at: String,
17    key: String,
18    json_data: String,
19}
20
21impl PreferenceRow {
22    /// Create a preference row to be inserted.
23    pub fn new_insert(key: &str, value: &Preference) -> Result<Self> {
24        Ok(Self {
25            created_at: UtcDateTime::default().to_rfc3339()?,
26            modified_at: UtcDateTime::default().to_rfc3339()?,
27            key: key.to_owned(),
28            json_data: serde_json::to_string(value)?,
29            ..Default::default()
30        })
31    }
32
33    /// Create a preference row to be updated.
34    pub fn new_update(key: &str, value: &Preference) -> Result<Self> {
35        Ok(Self {
36            modified_at: UtcDateTime::default().to_rfc3339()?,
37            key: key.to_owned(),
38            json_data: serde_json::to_string(value)?,
39            ..Default::default()
40        })
41    }
42}
43
44impl<'a> TryFrom<&Row<'a>> for PreferenceRow {
45    type Error = SqlError;
46    fn try_from(row: &Row<'a>) -> std::result::Result<Self, Self::Error> {
47        Ok(PreferenceRow {
48            row_id: row.get(0)?,
49            created_at: row.get(1)?,
50            modified_at: row.get(2)?,
51            key: row.get(3)?,
52            json_data: row.get(4)?,
53        })
54    }
55}
56
57impl TryFrom<PreferenceRow> for (String, Preference) {
58    type Error = Error;
59
60    fn try_from(value: PreferenceRow) -> Result<Self> {
61        let pref: Preference = serde_json::from_str(&value.json_data)?;
62        Ok((value.key, pref))
63    }
64}
65
66/// Preferences entity.
67pub struct PreferenceEntity<'conn, C>
68where
69    C: Deref<Target = Connection>,
70{
71    conn: &'conn C,
72}
73
74impl<'conn, C> PreferenceEntity<'conn, C>
75where
76    C: Deref<Target = Connection>,
77{
78    /// Create a new preference entity.
79    pub fn new(conn: &'conn C) -> Self {
80        Self { conn }
81    }
82
83    fn find_preference_select(&self, select_one: bool) -> sql::Select {
84        let mut query = sql::Select::new()
85            .select(
86                r#"
87                    preference_id,
88                    created_at,
89                    modified_at,
90                    key,
91                    json_data
92                "#,
93            )
94            .from("preferences")
95            .where_clause("account_id=?1");
96        if select_one {
97            query = query.where_and("key=?2");
98        }
99        query
100    }
101
102    /// Find a preference in the database.
103    pub fn find_optional(
104        &self,
105        account_id: Option<i64>,
106        key: &str,
107    ) -> std::result::Result<Option<PreferenceRow>, SqlError> {
108        let query = self.find_preference_select(true);
109        let mut stmt = self.conn.prepare_cached(&query.as_string())?;
110        Ok(stmt
111            .query_row((account_id, key), |row| Ok(row.try_into()?))
112            .optional()?)
113    }
114
115    /// Load preferences from the database.
116    ///
117    /// When no `account_id` is specified the preferences
118    /// are global.
119    pub fn load_preferences(
120        &self,
121        account_id: Option<i64>,
122    ) -> Result<Vec<PreferenceRow>> {
123        let query = self.find_preference_select(false);
124        let mut stmt = self.conn.prepare_cached(&query.as_string())?;
125
126        fn convert_row(row: &Row<'_>) -> Result<PreferenceRow> {
127            Ok(row.try_into()?)
128        }
129
130        let rows = stmt.query_and_then([account_id], |row| {
131            Ok::<_, crate::Error>(convert_row(row)?)
132        })?;
133        let mut preferences = Vec::new();
134        for row in rows {
135            preferences.push(row?);
136        }
137        Ok(preferences)
138    }
139
140    /// Create preferences in the database.
141    ///
142    /// When no `account_id` is specified the preferences
143    /// are global.
144    pub fn insert_preference(
145        &self,
146        account_id: Option<i64>,
147        row: &PreferenceRow,
148    ) -> std::result::Result<(), SqlError> {
149        let query = sql::Insert::new()
150            .insert_into(
151                r#"
152                preferences
153                (
154                    account_id,
155                    created_at,
156                    modified_at,
157                    key,
158                    json_data
159                )
160                "#,
161            )
162            .values("(?1, ?2, ?3, ?4, ?5)");
163        let mut stmt = self.conn.prepare_cached(&query.as_string())?;
164        stmt.execute((
165            account_id,
166            &row.created_at,
167            &row.modified_at,
168            &row.key,
169            &row.json_data,
170        ))?;
171        Ok(())
172    }
173
174    /// Create preferences in the database.
175    ///
176    /// When no `account_id` is specified the preferences
177    /// are global.
178    pub fn insert_preferences(
179        &self,
180        account_id: Option<i64>,
181        rows: &[PreferenceRow],
182    ) -> std::result::Result<(), SqlError> {
183        for row in rows {
184            self.insert_preference(account_id, row)?;
185        }
186        Ok(())
187    }
188
189    /// Update preference in the database.
190    ///
191    /// When no `account_id` is specified the preferences
192    /// are global.
193    pub fn update_preference(
194        &self,
195        account_id: Option<i64>,
196        row: &PreferenceRow,
197    ) -> std::result::Result<(), SqlError> {
198        let query = sql::Update::new()
199            .update("preferences")
200            .set("json_data = ?1, modified_at = ?2")
201            .where_clause("account_id = ?3")
202            .where_and("key = ?4");
203        let mut stmt = self.conn.prepare_cached(&query.as_string())?;
204        stmt.execute((
205            &row.json_data,
206            &row.modified_at,
207            account_id,
208            &row.key,
209        ))?;
210        Ok(())
211    }
212
213    /// Create or update preferences in the database.
214    ///
215    /// When no `account_id` is specified the preferences
216    /// are global.
217    pub fn upsert_preference(
218        &self,
219        account_id: Option<i64>,
220        row: &PreferenceRow,
221    ) -> std::result::Result<(), SqlError> {
222        let pref_row = self.find_optional(account_id, &row.key)?;
223        match pref_row {
224            Some(_) => {
225                self.update_preference(account_id, row)?;
226            }
227            None => self.insert_preference(account_id, row)?,
228        }
229        Ok(())
230    }
231
232    /// Delete a preference from the database.
233    pub fn delete_preference(
234        &self,
235        account_id: Option<i64>,
236        key: &str,
237    ) -> std::result::Result<(), SqlError> {
238        let query = sql::Delete::new()
239            .delete_from("preferences")
240            .where_clause("account_id = ?1")
241            .where_and("key = ?2");
242        let mut stmt = self.conn.prepare_cached(&query.as_string())?;
243        stmt.execute((account_id, key))?;
244        Ok(())
245    }
246
247    /// Delete all preferences from the database.
248    pub fn delete_all_preferences(
249        &self,
250        account_id: Option<i64>,
251    ) -> std::result::Result<(), SqlError> {
252        let query = sql::Delete::new()
253            .delete_from("preferences")
254            .where_clause("account_id = ?1");
255        let mut stmt = self.conn.prepare_cached(&query.as_string())?;
256        stmt.execute([account_id])?;
257        Ok(())
258    }
259}