use chrono::{DateTime, Utc};
use rusqlite::{params, OptionalExtension};
use crate::{
manager::{PostArchiverConnection, PostArchiverManager},
AuthorId, CollectionId, PlatformId, Post, PostId, TagId,
};
use super::{
filter::{DateFilter, IdFilter, RelationshipsFilter, TextFilter},
sortable::impl_sortable,
BaseFilter, FromQuery, Query, Queryer, RawSql,
};
impl_sortable!(PostQuery(PostSort) {
Id: "id",
Updated: "updated",
Published: "published",
Title: "title"
});
#[derive(Debug)]
pub struct PostQuery<'a, C> {
queryer: Queryer<'a, C>,
pub ids: IdFilter<PostId>,
pub title: TextFilter,
pub source: TextFilter,
pub updated: DateFilter,
pub published: DateFilter,
pub platforms: IdFilter<PlatformId>,
pub tags: RelationshipsFilter<TagId>,
pub authors: RelationshipsFilter<AuthorId>,
pub collections: RelationshipsFilter<CollectionId>,
}
impl<'a, C: PostArchiverConnection> PostQuery<'a, C> {
pub fn new(manager: &'a PostArchiverManager<C>) -> Self {
PostQuery {
queryer: Queryer::new(manager),
ids: IdFilter::new("id"),
title: TextFilter::new("title"),
source: TextFilter::new("source"),
updated: DateFilter::new("updated"),
published: DateFilter::new("published"),
platforms: IdFilter::new("platform"),
tags: RelationshipsFilter::new("post_tags", "tag"),
authors: RelationshipsFilter::new("author_posts", "author"),
collections: RelationshipsFilter::new("collection_posts", "collection"),
}
}
}
impl<C: PostArchiverConnection> BaseFilter for PostQuery<'_, C> {
type Based = Post;
fn update_sql<T: FromQuery<Based = Self::Based>>(&self, mut sql: RawSql<T>) -> RawSql<T> {
sql = self.ids.build_sql(sql);
sql = self.title.build_sql(sql);
sql = self.source.build_sql(sql);
sql = self.updated.build_sql(sql);
sql = self.published.build_sql(sql);
sql = self.platforms.build_sql(sql);
sql = self.authors.build_sql(sql);
sql = self.tags.build_sql(sql);
sql = self.collections.build_sql(sql);
sql
}
fn queryer(&self) -> &Queryer<'_, impl PostArchiverConnection> {
&self.queryer
}
}
impl<C: PostArchiverConnection> Query for PostQuery<'_, C> {
type Wrapper<U> = Vec<U>;
type Based = Post;
fn query_with_context<T: FromQuery<Based = Self::Based>>(
self,
sql: RawSql<T>,
) -> crate::error::Result<Self::Wrapper<T>> {
let sql = self.update_sql(sql);
let (sql, params) = sql.build_sql();
self.queryer.fetch(&sql, params)
}
}
impl<C: PostArchiverConnection> PostArchiverManager<C> {
pub fn posts(&self) -> PostQuery<'_, C> {
PostQuery::new(self)
}
pub fn get_post(&self, id: PostId) -> crate::error::Result<Option<Post>> {
let mut stmt = self
.conn()
.prepare_cached("SELECT * FROM posts WHERE id = ?")?;
Ok(stmt.query_row([id], Post::from_row).optional()?)
}
pub fn find_post(&self, source: &str) -> crate::error::Result<Option<PostId>> {
let mut stmt = self
.conn()
.prepare_cached("SELECT id FROM posts WHERE source = ?")?;
Ok(stmt.query_row([source], |row| row.get(0)).optional()?)
}
pub fn find_post_with_updated(
&self,
source: &str,
updated: &DateTime<Utc>,
) -> crate::error::Result<Option<PostId>> {
let mut stmt = self
.conn()
.prepare_cached("SELECT id FROM posts WHERE source = ? AND updated >= ?")?;
Ok(stmt
.query_row(params![source, updated], |row| row.get(0))
.optional()?)
}
}