icydb_core/db/query/
load.rs

1use crate::{
2    db::{
3        primitives::{FilterExpr, FilterSlot, LimitExpr, LimitSlot, SortExpr, SortSlot},
4        query::{QueryError, QueryValidate, prelude::*},
5    },
6    traits::{EntityKind, FieldValue},
7};
8use candid::CandidType;
9use serde::{Deserialize, Serialize};
10
11///
12/// LoadQuery
13///
14
15#[derive(CandidType, Clone, Debug, Default, Deserialize, Serialize)]
16pub struct LoadQuery {
17    pub filter: Option<FilterExpr>,
18    pub limit: Option<LimitExpr>,
19    pub sort: Option<SortExpr>,
20}
21
22impl LoadQuery {
23    // ─────────────────────────────────────────────
24    // CONSTRUCTORS
25    // ─────────────────────────────────────────────
26
27    /// Construct an empty load query.
28    #[must_use]
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    #[must_use]
34    pub const fn is_empty(&self) -> bool {
35        self.filter.is_none() && self.limit.is_none() && self.sort.is_none()
36    }
37
38    // ─────────────────────────────────────────────
39    // ENTITY CONVENIENCE HELPERS
40    // ─────────────────────────────────────────────
41
42    /// Filter by a single primary key value.
43    #[must_use]
44    pub fn one<E: EntityKind>(self, value: impl FieldValue) -> Self {
45        self.one_by_field(E::PRIMARY_KEY, value)
46    }
47
48    /// Read all rows (alias for default).
49    #[must_use]
50    pub fn all() -> Self {
51        Self::default()
52    }
53
54    /// Filter by a set of field values.
55    #[must_use]
56    pub fn many<E, I, V>(self, values: I) -> Self
57    where
58        E: EntityKind,
59        I: IntoIterator<Item = V>,
60        V: FieldValue,
61    {
62        self.filter(|f| f.in_iter(E::PRIMARY_KEY, values))
63    }
64
65    // ─────────────────────────────────────────────
66    // FIELD-BASED PRIMITIVES
67    // ─────────────────────────────────────────────
68
69    /// Filter by a single field value.
70    #[must_use]
71    pub fn one_by_field(self, field: impl AsRef<str>, value: impl FieldValue) -> Self {
72        self.filter(|f| f.eq(field, value))
73    }
74
75    /// Filter by a set of field values.
76    #[must_use]
77    pub fn many_by_field<I, V>(self, field: impl AsRef<str>, values: I) -> Self
78    where
79        I: IntoIterator<Item = V>,
80        V: FieldValue,
81    {
82        self.filter(|f| f.in_iter(field, values))
83    }
84
85    // ─────────────────────────────────────────────
86    // CONVENIENCE
87    // ─────────────────────────────────────────────
88
89    /// Set offset=0, limit=1 (useful for existence checks / fast-paths).
90    #[must_use]
91    pub fn limit_1(self) -> Self {
92        self.offset(0).limit(1)
93    }
94}
95
96// ─────────────────────────────────────────────
97// TRAIT IMPLEMENTATIONS
98// ─────────────────────────────────────────────
99
100impl FilterSlot for LoadQuery {
101    fn filter_slot(&mut self) -> &mut Option<FilterExpr> {
102        &mut self.filter
103    }
104}
105
106impl LimitSlot for LoadQuery {
107    fn limit_slot(&mut self) -> &mut Option<LimitExpr> {
108        &mut self.limit
109    }
110}
111
112impl SortSlot for LoadQuery {
113    fn sort_slot(&mut self) -> &mut Option<SortExpr> {
114        &mut self.sort
115    }
116}
117
118impl<E: EntityKind> QueryValidate<E> for LoadQuery {
119    fn validate(&self) -> Result<(), QueryError> {
120        if let Some(filter) = &self.filter {
121            QueryValidate::<E>::validate(filter)?;
122        }
123        if let Some(limit) = &self.limit {
124            QueryValidate::<E>::validate(limit)?;
125        }
126        if let Some(sort) = &self.sort {
127            QueryValidate::<E>::validate(sort)?;
128        }
129
130        Ok(())
131    }
132}