use crate::{Error, Result};
use async_sqlite::rusqlite::{
Connection, Error as SqlError, OptionalExtension, Row,
};
use sos_core::UtcDateTime;
use sos_preferences::Preference;
use sql_query_builder as sql;
use std::ops::Deref;
#[doc(hidden)]
#[derive(Debug, Default)]
pub struct PreferenceRow {
pub row_id: i64,
created_at: String,
modified_at: String,
key: String,
json_data: String,
}
impl PreferenceRow {
pub fn new_insert(key: &str, value: &Preference) -> Result<Self> {
Ok(Self {
created_at: UtcDateTime::default().to_rfc3339()?,
modified_at: UtcDateTime::default().to_rfc3339()?,
key: key.to_owned(),
json_data: serde_json::to_string(value)?,
..Default::default()
})
}
pub fn new_update(key: &str, value: &Preference) -> Result<Self> {
Ok(Self {
modified_at: UtcDateTime::default().to_rfc3339()?,
key: key.to_owned(),
json_data: serde_json::to_string(value)?,
..Default::default()
})
}
}
impl<'a> TryFrom<&Row<'a>> for PreferenceRow {
type Error = SqlError;
fn try_from(row: &Row<'a>) -> std::result::Result<Self, Self::Error> {
Ok(PreferenceRow {
row_id: row.get(0)?,
created_at: row.get(1)?,
modified_at: row.get(2)?,
key: row.get(3)?,
json_data: row.get(4)?,
})
}
}
impl TryFrom<PreferenceRow> for (String, Preference) {
type Error = Error;
fn try_from(value: PreferenceRow) -> Result<Self> {
let pref: Preference = serde_json::from_str(&value.json_data)?;
Ok((value.key, pref))
}
}
pub struct PreferenceEntity<'conn, C>
where
C: Deref<Target = Connection>,
{
conn: &'conn C,
}
impl<'conn, C> PreferenceEntity<'conn, C>
where
C: Deref<Target = Connection>,
{
pub fn new(conn: &'conn C) -> Self {
Self { conn }
}
fn find_preference_select(
&self,
account_id: Option<i64>,
select_one: bool,
) -> sql::Select {
let mut query = sql::Select::new()
.select(
r#"
preference_id,
created_at,
modified_at,
key,
json_data
"#,
)
.from("preferences");
if account_id.is_some() {
query = query.where_clause("account_id=?1");
if select_one {
query = query.where_and("key=?2");
}
} else {
query = query.where_clause("account_id IS NULL");
if select_one {
query = query.where_and("key=?1");
}
}
query
}
pub fn find_optional(
&self,
account_id: Option<i64>,
key: &str,
) -> std::result::Result<Option<PreferenceRow>, SqlError> {
let query = self.find_preference_select(account_id, true);
let mut stmt = self.conn.prepare_cached(&query.as_string())?;
if let Some(account_id) = account_id {
stmt.query_row((account_id, key), |row| row.try_into())
.optional()
} else {
stmt.query_row((key,), |row| row.try_into()).optional()
}
}
pub fn load_preferences(
&self,
account_id: Option<i64>,
) -> Result<Vec<PreferenceRow>> {
let query = self.find_preference_select(account_id, false);
let mut stmt = self.conn.prepare_cached(&query.as_string())?;
fn convert_row(row: &Row<'_>) -> Result<PreferenceRow> {
Ok(row.try_into()?)
}
let rows = if let Some(account_id) = account_id {
stmt.query_and_then([account_id], convert_row)?
} else {
stmt.query_and_then([], convert_row)?
};
let mut preferences = Vec::new();
for row in rows {
preferences.push(row?);
}
Ok(preferences)
}
pub fn insert_preference(
&self,
account_id: Option<i64>,
row: &PreferenceRow,
) -> std::result::Result<(), SqlError> {
let query = sql::Insert::new()
.insert_into(
r#"
preferences
(
account_id,
created_at,
modified_at,
key,
json_data
)
"#,
)
.values("(?1, ?2, ?3, ?4, ?5)");
let mut stmt = self.conn.prepare_cached(&query.as_string())?;
stmt.execute((
account_id,
&row.created_at,
&row.modified_at,
&row.key,
&row.json_data,
))?;
Ok(())
}
pub fn insert_preferences(
&self,
account_id: Option<i64>,
rows: &[PreferenceRow],
) -> std::result::Result<(), SqlError> {
for row in rows {
self.insert_preference(account_id, row)?;
}
Ok(())
}
pub fn update_preference(
&self,
account_id: Option<i64>,
row: &PreferenceRow,
) -> std::result::Result<(), SqlError> {
let mut query = sql::Update::new()
.update("preferences")
.set("json_data = ?1, modified_at = ?2");
if account_id.is_some() {
query =
query.where_clause("account_id = ?3").where_and("key = ?4");
} else {
query = query
.where_clause("account_id IS NULL")
.where_and("key = ?3");
}
let mut stmt = self.conn.prepare_cached(&query.as_string())?;
if let Some(account_id) = account_id {
stmt.execute((
&row.json_data,
&row.modified_at,
account_id,
&row.key,
))?;
} else {
stmt.execute((&row.json_data, &row.modified_at, &row.key))?;
}
Ok(())
}
pub fn upsert_preference(
&self,
account_id: Option<i64>,
row: &PreferenceRow,
) -> std::result::Result<(), SqlError> {
let pref_row = self.find_optional(account_id, &row.key)?;
match pref_row {
Some(_) => {
self.update_preference(account_id, row)?;
}
None => self.insert_preference(account_id, row)?,
}
Ok(())
}
pub fn delete_preference(
&self,
account_id: Option<i64>,
key: &str,
) -> std::result::Result<(), SqlError> {
let mut query = sql::Delete::new().delete_from("preferences");
if account_id.is_some() {
query =
query.where_clause("account_id = ?1").where_and("key = ?2");
} else {
query = query
.where_clause("account_id IS NULL")
.where_and("key = ?1");
}
let mut stmt = self.conn.prepare_cached(&query.as_string())?;
if let Some(account_id) = account_id {
stmt.execute((account_id, key))?;
} else {
stmt.execute((key,))?;
}
Ok(())
}
pub fn delete_all_preferences(
&self,
account_id: Option<i64>,
) -> std::result::Result<(), SqlError> {
let mut query = sql::Delete::new().delete_from("preferences");
if account_id.is_some() {
query = query.where_clause("account_id = ?1");
} else {
query = query.where_clause("account_id IS NULL");
}
let mut stmt = self.conn.prepare_cached(&query.as_string())?;
if let Some(account_id) = account_id {
stmt.execute([account_id])?;
} else {
stmt.execute([])?;
}
Ok(())
}
}