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