Skip to main content

rskit_database/
repository.rs

1//! Repository trait and helpers for the data-access layer.
2
3use async_trait::async_trait;
4
5use rskit_errors::AppResult;
6
7/// Generic repository interface for CRUD operations.
8///
9/// Implement this trait for each entity type. Backend-specific repository
10/// helpers belong in adapter crates so the core contract remains vendor-neutral.
11#[async_trait]
12pub trait Repository<T, ID>: Send + Sync
13where
14    T: Send + Sync,
15    ID: Send + Sync,
16{
17    /// Find a single entity by its primary key.
18    async fn find_by_id(&self, id: &ID) -> AppResult<Option<T>>;
19
20    /// Find all entities matching the given options.
21    async fn find_all(&self, opts: FindOpts) -> AppResult<Vec<T>>;
22
23    /// Find the first entity matching the given options.
24    async fn find_first(&self, opts: FindOpts) -> AppResult<Option<T>>;
25
26    /// Count entities matching the given options.
27    async fn count(&self, opts: FindOpts) -> AppResult<i64>;
28
29    /// Check whether an entity with the given ID exists.
30    async fn exists(&self, id: &ID) -> AppResult<bool>;
31
32    /// Insert a new entity and return the persisted version.
33    async fn create(&self, entity: &T) -> AppResult<T>;
34
35    /// Update an existing entity and return the updated version.
36    async fn update(&self, entity: &T) -> AppResult<T>;
37
38    /// Delete the entity with the given primary key.
39    async fn delete(&self, id: &ID) -> AppResult<()>;
40
41    /// Insert or update (upsert) an entity and return the result.
42    async fn upsert(&self, entity: &T) -> AppResult<T>;
43}
44
45/// Options for paginated / filtered queries.
46#[derive(Debug, Default)]
47pub struct FindOpts {
48    /// Maximum number of rows to return.
49    pub limit: Option<i64>,
50    /// Number of rows to skip.
51    pub offset: Option<i64>,
52    /// Columns to order by (e.g. `"created_at DESC"`).
53    pub order_by: Vec<String>,
54    /// Column-value filter pairs.
55    pub filters: Vec<(String, serde_json::Value)>,
56}
57
58impl FindOpts {
59    /// Set a maximum number of rows.
60    #[must_use]
61    pub fn with_limit(mut self, n: i64) -> Self {
62        self.limit = Some(n);
63        self
64    }
65
66    /// Set the row offset for pagination.
67    #[must_use]
68    pub fn with_offset(mut self, n: i64) -> Self {
69        self.offset = Some(n);
70        self
71    }
72
73    /// Append an ordering clause.
74    #[must_use]
75    pub fn order_by(mut self, col: &str) -> Self {
76        self.order_by.push(col.to_owned());
77        self
78    }
79
80    /// Append a column filter.
81    #[must_use]
82    pub fn filter(mut self, col: &str, val: impl Into<serde_json::Value>) -> Self {
83        self.filters.push((col.to_owned(), val.into()));
84        self
85    }
86}