use chrono::{DateTime, Utc};
use rusqlite::params;
use crate::{
error::Result,
manager::{binded::Binded, PostArchiverConnection},
query::FromQuery,
Alias, Author, AuthorId, FileMetaId, PlatformId, PostId,
};
#[derive(Debug, Clone)]
pub enum AuthorThumb {
Set(Option<FileMetaId>),
ByLatest,
}
#[derive(Debug, Clone)]
pub enum AuthorUpdated {
Set(DateTime<Utc>),
ByLatest,
}
#[derive(Debug, Clone, Default)]
pub struct UpdateAuthor {
pub name: Option<String>,
pub thumb: Option<AuthorThumb>,
pub updated: Option<AuthorUpdated>,
}
impl UpdateAuthor {
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn thumb(mut self, thumb: Option<FileMetaId>) -> Self {
self.thumb = Some(AuthorThumb::Set(thumb));
self
}
pub fn thumb_by_latest(mut self) -> Self {
self.thumb = Some(AuthorThumb::ByLatest);
self
}
pub fn updated(mut self, updated: DateTime<Utc>) -> Self {
self.updated = Some(AuthorUpdated::Set(updated));
self
}
pub fn updated_by_latest(mut self) -> Self {
self.updated = Some(AuthorUpdated::ByLatest);
self
}
}
impl<'a, C: PostArchiverConnection> Binded<'a, AuthorId, C> {
pub fn value(&self) -> Result<Author> {
let mut stmt = self
.conn()
.prepare_cached("SELECT * FROM authors WHERE id = ?")?;
Ok(stmt.query_row([self.id()], Author::from_row)?)
}
pub fn delete(self) -> Result<()> {
self.conn()
.execute("DELETE FROM authors WHERE id = ?", [self.id()])?;
Ok(())
}
pub fn update(&self, update: UpdateAuthor) -> 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 = ?");
match &update.thumb {
Some(AuthorThumb::Set(v)) => {
sets.push("thumb = ?");
params.push(v);
}
Some(AuthorThumb::ByLatest) => {
sets.push("thumb = (SELECT thumb FROM posts WHERE id IN (SELECT post FROM author_posts WHERE author = ?) AND thumb IS NOT NULL ORDER BY updated DESC LIMIT 1)");
params.push(&id);
}
None => {}
}
match &update.updated {
Some(AuthorUpdated::Set(v)) => {
sets.push("updated = ?");
params.push(v);
}
Some(AuthorUpdated::ByLatest) => {
sets.push("updated = (SELECT updated FROM posts WHERE id IN (SELECT post FROM author_posts WHERE author = ?) ORDER BY updated DESC LIMIT 1)");
params.push(&id);
}
None => {}
}
if sets.is_empty() {
return Ok(());
}
params.push(&id);
let sql = format!("UPDATE authors SET {} WHERE id = ?", sets.join(", "));
self.conn().execute(&sql, params.as_slice())?;
Ok(())
}
}
impl<'a, C: PostArchiverConnection> Binded<'a, AuthorId, C> {
pub fn list_aliases(&self) -> Result<Vec<Alias>> {
let mut stmt = self
.conn()
.prepare_cached("SELECT * FROM author_aliases WHERE target = ?")?;
let rows = stmt.query_map([self.id()], Alias::from_row)?;
rows.collect::<std::result::Result<_, _>>()
.map_err(Into::into)
}
pub fn add_aliases(&self, aliases: Vec<(String, PlatformId, Option<String>)>) -> Result<()> {
let mut stmt = self.conn().prepare_cached(
"INSERT OR REPLACE INTO author_aliases (target, source, platform, link) VALUES (?, ?, ?, ?)",
)?;
for (source, platform, link) in aliases {
stmt.execute(params![self.id(), source, platform, link])?;
}
Ok(())
}
pub fn remove_aliases(&self, aliases: &[(String, PlatformId)]) -> Result<()> {
let mut stmt = self.conn().prepare_cached(
"DELETE FROM author_aliases WHERE target = ? AND source = ? AND platform = ?",
)?;
for (source, platform) in aliases {
stmt.execute(params![self.id(), source, platform])?;
}
Ok(())
}
pub fn set_alias_name(&self, alias: &(String, PlatformId), name: String) -> Result<()> {
let mut stmt = self.conn().prepare_cached(
"UPDATE author_aliases SET source = ? WHERE target = ? AND source = ? AND platform = ?",
)?;
stmt.execute(params![name, self.id(), alias.0, alias.1])?;
Ok(())
}
pub fn set_alias_platform(
&self,
alias: &(String, PlatformId),
platform: PlatformId,
) -> Result<()> {
let mut stmt = self.conn().prepare_cached(
"UPDATE author_aliases SET platform = ? WHERE target = ? AND source = ? AND platform = ?",
)?;
stmt.execute(params![platform, self.id(), alias.0, alias.1])?;
Ok(())
}
pub fn set_alias_link(&self, alias: &(String, PlatformId), link: Option<String>) -> Result<()> {
let mut stmt = self.conn().prepare_cached(
"UPDATE author_aliases SET link = ? WHERE target = ? AND source = ? AND platform = ?",
)?;
stmt.execute(params![link, self.id(), alias.0, alias.1])?;
Ok(())
}
}
impl<'a, C: PostArchiverConnection> Binded<'a, AuthorId, C> {
pub fn list_posts(&self) -> Result<Vec<PostId>> {
let mut stmt = self
.conn()
.prepare_cached("SELECT post FROM author_posts WHERE author = ?")?;
let rows = stmt.query_map([self.id()], |row| row.get(0))?;
rows.collect::<std::result::Result<_, _>>()
.map_err(Into::into)
}
}
pub trait FindAlias {
fn source(&self) -> &str;
fn platform(&self) -> PlatformId;
}
impl FindAlias for (&str, PlatformId) {
fn source(&self) -> &str {
self.0
}
fn platform(&self) -> PlatformId {
self.1
}
}
#[cfg(feature = "importer")]
impl FindAlias for crate::importer::UnsyncAlias {
fn source(&self) -> &str {
&self.source
}
fn platform(&self) -> PlatformId {
self.platform
}
}