use rusqlite::params;
use crate::{
error::Result,
manager::{binded::Binded, PostArchiverConnection},
query::FromQuery,
Collection, CollectionId, FileMetaId, PostId,
};
#[derive(Debug, Clone)]
pub enum CollectionThumb {
Set(Option<FileMetaId>),
ByLatest,
}
#[derive(Debug, Clone, Default)]
pub struct UpdateCollection {
pub name: Option<String>,
pub source: Option<Option<String>>,
pub thumb: Option<CollectionThumb>,
}
impl UpdateCollection {
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn source(mut self, source: Option<String>) -> Self {
self.source = Some(source);
self
}
pub fn thumb(mut self, thumb: Option<FileMetaId>) -> Self {
self.thumb = Some(CollectionThumb::Set(thumb));
self
}
pub fn thumb_by_latest(mut self) -> Self {
self.thumb = Some(CollectionThumb::ByLatest);
self
}
}
impl<'a, C: PostArchiverConnection> Binded<'a, CollectionId, C> {
pub fn value(&self) -> Result<Collection> {
let mut stmt = self
.conn()
.prepare_cached("SELECT * FROM collections WHERE id = ?")?;
Ok(stmt.query_row([self.id()], Collection::from_row)?)
}
pub fn delete(self) -> Result<()> {
self.conn()
.execute("DELETE FROM collections WHERE id = ?", [self.id()])?;
Ok(())
}
pub fn update(&self, update: UpdateCollection) -> Result<()> {
use rusqlite::types::ToSql;
let id = self.id();
let mut sets: Vec<&str> = Vec::new();
let mut params: Vec<&dyn ToSql> = Vec::new();
macro_rules! push {
($field:expr, $col:expr) => {
if let Some(ref v) = $field {
sets.push($col);
params.push(v);
}
};
}
push!(update.name, "name = ?");
push!(update.source, "source = ?");
match &update.thumb {
Some(CollectionThumb::Set(v)) => {
sets.push("thumb = ?");
params.push(v);
}
Some(CollectionThumb::ByLatest) => {
sets.push("thumb = (SELECT posts.thumb FROM posts INNER JOIN collection_posts ON collection_posts.post = posts.id WHERE collection_posts.collection = ? AND posts.thumb IS NOT NULL ORDER BY posts.updated DESC LIMIT 1)");
params.push(&id);
}
None => {}
}
if sets.is_empty() {
return Ok(());
}
params.push(&id);
let sql = format!("UPDATE collections SET {} WHERE id = ?", sets.join(", "));
self.conn().execute(&sql, params.as_slice())?;
Ok(())
}
}
impl<'a, C: PostArchiverConnection> Binded<'a, CollectionId, C> {
pub fn list_posts(&self) -> Result<Vec<PostId>> {
let mut stmt = self
.conn()
.prepare_cached("SELECT post FROM collection_posts WHERE collection = ?")?;
let rows = stmt.query_map([self.id()], |row| row.get(0))?;
rows.collect::<std::result::Result<_, _>>()
.map_err(Into::into)
}
pub fn add_posts(&self, posts: &[PostId]) -> Result<()> {
let mut stmt = self.conn().prepare_cached(
"INSERT OR IGNORE INTO collection_posts (collection, post) VALUES (?, ?)",
)?;
for post in posts {
stmt.execute(params![self.id(), post])?;
}
Ok(())
}
pub fn remove_posts(&self, posts: &[PostId]) -> Result<()> {
let mut stmt = self
.conn()
.prepare_cached("DELETE FROM collection_posts WHERE collection = ? AND post = ?")?;
for post in posts {
stmt.execute(params![self.id(), post])?;
}
Ok(())
}
}