Skip to main content

post_archiver/query/
collection.rs

1//! Collection query builder and related point-query helpers.
2
3use rusqlite::OptionalExtension;
4
5use crate::{
6    manager::{PostArchiverConnection, PostArchiverManager},
7    Collection, CollectionId,
8};
9
10use super::{
11    filter::{IdFilter, TextFilter},
12    sortable::impl_sortable,
13    BaseFilter, FromQuery, Query, Queryer, RawSql,
14};
15
16/// Fluent query builder for collections.  Obtained via [`PostArchiverManager::collections()`].
17///
18/// # Available filter fields
19/// - `ids`: filter by a set of [`CollectionId`] values.
20/// - `name`: `LIKE` fuzzy match on the collection name.
21/// - `source`: `LIKE` fuzzy match on the source field.
22#[derive(Debug)]
23pub struct CollectionQuery<'a, C> {
24    queryer: Queryer<'a, C>,
25    pub ids: IdFilter<CollectionId>,
26    pub name: TextFilter,
27    pub source: TextFilter,
28}
29
30impl<'a, C: PostArchiverConnection> CollectionQuery<'a, C> {
31    pub fn new(manager: &'a PostArchiverManager<C>) -> Self {
32        CollectionQuery {
33            queryer: Queryer::new(manager),
34            ids: IdFilter::new("id"),
35            name: TextFilter::new("name"),
36            source: TextFilter::new("source"),
37        }
38    }
39}
40
41impl_sortable!(CollectionQuery(CollectionSort) {
42    Id: "id",
43    Name: "name",
44    Source: "source"
45});
46
47impl<C: PostArchiverConnection> BaseFilter for CollectionQuery<'_, C> {
48    type Based = Collection;
49
50    fn update_sql<T: FromQuery<Based = Self::Based>>(&self, mut sql: RawSql<T>) -> RawSql<T> {
51        sql = self.ids.build_sql(sql);
52        sql = self.name.build_sql(sql);
53        sql = self.source.build_sql(sql);
54
55        sql
56    }
57
58    fn queryer(&self) -> &Queryer<'_, impl PostArchiverConnection> {
59        &self.queryer
60    }
61}
62
63impl<C: PostArchiverConnection> Query for CollectionQuery<'_, C> {
64    type Wrapper<T> = Vec<T>;
65    type Based = Collection;
66
67    fn query_with_context<T: FromQuery<Based = Self::Based>>(
68        self,
69        sql: RawSql<T>,
70    ) -> crate::error::Result<Self::Wrapper<T>> {
71        let sql = self.update_sql(sql);
72        let (sql, params) = sql.build_sql();
73        self.queryer.fetch(&sql, params)
74    }
75}
76
77impl<C: PostArchiverConnection> PostArchiverManager<C> {
78    /// Entry point for the collection query builder.
79    pub fn collections(&self) -> CollectionQuery<'_, C> {
80        CollectionQuery::new(self)
81    }
82
83    /// Fetch a single collection by primary key. Returns `None` if not found.
84    pub fn get_collection(&self, id: CollectionId) -> crate::error::Result<Option<Collection>> {
85        let mut stmt = self
86            .conn()
87            .prepare_cached("SELECT * FROM collections WHERE id = ?")?;
88        Ok(stmt.query_row([id], Collection::from_row).optional()?)
89    }
90
91    /// Find a collection ID by `name` (exact match).
92    pub fn find_collection(&self, name: &str) -> crate::error::Result<Option<CollectionId>> {
93        let mut stmt = self
94            .conn()
95            .prepare_cached("SELECT id FROM collections WHERE platform IS ? AND name = ?")?;
96        Ok(stmt
97            .query_row(rusqlite::params![name], |row| row.get(0))
98            .optional()?)
99    }
100
101    /// Find a collection ID by its `source` field.
102    pub fn find_collection_by_source(
103        &self,
104        source: &str,
105    ) -> crate::error::Result<Option<CollectionId>> {
106        let mut stmt = self
107            .conn()
108            .prepare_cached("SELECT id FROM collections WHERE source = ?")?;
109        Ok(stmt.query_row([source], |row| row.get(0)).optional()?)
110    }
111}