metabase_api_rs/repository/
traits.rs1use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use std::fmt::Debug;
8use thiserror::Error;
9
10#[derive(Debug, Error)]
12pub enum RepositoryError {
13 #[error("Entity not found: {0}")]
15 NotFound(String),
16
17 #[error("Invalid parameters: {0}")]
19 InvalidParams(String),
20
21 #[error("Network error: {0}")]
23 Network(String),
24
25 #[error("Authentication error: {0}")]
27 Authentication(String),
28
29 #[error("Serialization error: {0}")]
31 Serialization(String),
32
33 #[error("Repository error: {0}")]
35 Other(String),
36}
37
38pub type RepositoryResult<T> = Result<T, RepositoryError>;
40
41impl From<crate::core::error::Error> for RepositoryError {
43 fn from(err: crate::core::error::Error) -> Self {
44 use crate::core::error::Error;
45 match err {
46 Error::NotFound(msg) => RepositoryError::NotFound(msg),
47 Error::Network(msg) => RepositoryError::Network(msg),
48 Error::Authentication(msg) => RepositoryError::Authentication(msg),
49 Error::Validation(msg) => RepositoryError::InvalidParams(msg),
50 Error::Http { status: 404, .. } => {
51 RepositoryError::NotFound("Resource not found".to_string())
52 }
53 Error::Http { message, .. } => RepositoryError::Network(message),
54 other => RepositoryError::Other(other.to_string()),
55 }
56 }
57}
58
59#[derive(Debug, Clone, Default, Serialize, Deserialize)]
61pub struct PaginationParams {
62 pub page: Option<u32>,
64 pub limit: Option<u32>,
66 pub offset: Option<u32>,
68}
69
70impl PaginationParams {
71 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn with_page(mut self, page: u32) -> Self {
78 self.page = Some(page);
79 self
80 }
81
82 pub fn with_limit(mut self, limit: u32) -> Self {
84 self.limit = Some(limit);
85 self
86 }
87
88 pub fn with_offset(mut self, offset: u32) -> Self {
90 self.offset = Some(offset);
91 self
92 }
93
94 pub fn to_query_params(&self) -> Vec<(String, String)> {
96 let mut params = vec![];
97 if let Some(page) = self.page {
98 params.push(("page".to_string(), page.to_string()));
99 }
100 if let Some(limit) = self.limit {
101 params.push(("limit".to_string(), limit.to_string()));
102 }
103 if let Some(offset) = self.offset {
104 params.push(("offset".to_string(), offset.to_string()));
105 }
106 params
107 }
108}
109
110#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
112#[serde(rename_all = "lowercase")]
113pub enum SortOrder {
114 Asc,
116 Desc,
118}
119
120impl Default for SortOrder {
121 fn default() -> Self {
122 Self::Asc
123 }
124}
125
126#[derive(Debug, Clone, Default, Serialize, Deserialize)]
128pub struct FilterParams {
129 pub query: Option<String>,
131 pub archived: Option<bool>,
133 pub created_after: Option<String>,
135 pub created_before: Option<String>,
137 pub updated_after: Option<String>,
139 pub updated_before: Option<String>,
141 pub custom: Option<serde_json::Value>,
143}
144
145impl FilterParams {
146 pub fn new() -> Self {
148 Self::default()
149 }
150
151 pub fn with_query(mut self, query: impl Into<String>) -> Self {
153 self.query = Some(query.into());
154 self
155 }
156
157 pub fn with_archived(mut self, archived: bool) -> Self {
159 self.archived = Some(archived);
160 self
161 }
162}
163
164#[async_trait]
168pub trait Repository: Send + Sync {
169 type Entity: Send + Sync + Debug;
171
172 type Id: Send + Sync + Debug + Clone;
174
175 async fn get(&self, id: &Self::Id) -> RepositoryResult<Self::Entity>;
177
178 async fn list(
180 &self,
181 pagination: Option<PaginationParams>,
182 filters: Option<FilterParams>,
183 ) -> RepositoryResult<Vec<Self::Entity>>;
184
185 async fn create(&self, entity: &Self::Entity) -> RepositoryResult<Self::Entity>;
187
188 async fn update(&self, id: &Self::Id, entity: &Self::Entity) -> RepositoryResult<Self::Entity>;
190
191 async fn delete(&self, id: &Self::Id) -> RepositoryResult<()>;
193
194 async fn exists(&self, id: &Self::Id) -> RepositoryResult<bool> {
196 match self.get(id).await {
197 Ok(_) => Ok(true),
198 Err(RepositoryError::NotFound(_)) => Ok(false),
199 Err(e) => Err(e),
200 }
201 }
202
203 async fn count(&self, filters: Option<FilterParams>) -> RepositoryResult<u64> {
205 let entities = self.list(None, filters).await?;
206 Ok(entities.len() as u64)
207 }
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct PaginatedResponse<T> {
213 pub items: Vec<T>,
215 pub total: u64,
217 pub page: u32,
219 pub limit: u32,
221 pub total_pages: u32,
223}
224
225impl<T> PaginatedResponse<T> {
226 pub fn new(items: Vec<T>, total: u64, page: u32, limit: u32) -> Self {
228 let total_pages = ((total as f64) / (limit as f64)).ceil() as u32;
229 Self {
230 items,
231 total,
232 page,
233 limit,
234 total_pages,
235 }
236 }
237
238 pub fn has_next(&self) -> bool {
240 self.page < self.total_pages
241 }
242
243 pub fn has_prev(&self) -> bool {
245 self.page > 1
246 }
247}