supabase/
types.rs

1//! Common types and data structures for Supabase operations
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use uuid::Uuid;
7
8/// Configuration for Supabase client
9#[derive(Debug, Clone, Default)]
10pub struct SupabaseConfig {
11    /// Project URL (e.g., https://your-project.supabase.co)
12    pub url: String,
13    /// API key (anon key for client-side operations)
14    pub key: String,
15    /// Service role key for admin operations (optional)
16    pub service_role_key: Option<String>,
17    /// HTTP client configuration
18    pub http_config: HttpConfig,
19    /// Auth configuration
20    pub auth_config: AuthConfig,
21    /// Database configuration
22    pub database_config: DatabaseConfig,
23    /// Storage configuration
24    pub storage_config: StorageConfig,
25}
26
27/// HTTP client configuration
28#[derive(Debug, Clone)]
29pub struct HttpConfig {
30    /// Request timeout in seconds
31    pub timeout: u64,
32    /// Connection timeout in seconds
33    pub connect_timeout: u64,
34    /// Maximum number of redirects to follow
35    pub max_redirects: usize,
36    /// Custom headers to include in all requests
37    pub default_headers: HashMap<String, String>,
38}
39
40impl Default for HttpConfig {
41    fn default() -> Self {
42        Self {
43            timeout: 60,
44            connect_timeout: 10,
45            max_redirects: 10,
46            default_headers: HashMap::new(),
47        }
48    }
49}
50
51/// Authentication configuration
52#[derive(Debug, Clone)]
53pub struct AuthConfig {
54    /// Auto-refresh tokens before expiry
55    pub auto_refresh_token: bool,
56    /// Token refresh threshold in seconds before expiry
57    pub refresh_threshold: u64,
58    /// Persist session in storage
59    pub persist_session: bool,
60    /// Custom storage implementation
61    pub storage_key: String,
62}
63
64impl Default for AuthConfig {
65    fn default() -> Self {
66        Self {
67            auto_refresh_token: true,
68            refresh_threshold: 300, // 5 minutes
69            persist_session: true,
70            storage_key: "supabase.auth.token".to_string(),
71        }
72    }
73}
74
75/// Database configuration
76#[derive(Debug, Clone)]
77pub struct DatabaseConfig {
78    /// Default schema to use
79    pub schema: String,
80    /// Maximum number of retry attempts
81    pub max_retries: u32,
82    /// Retry delay in milliseconds
83    pub retry_delay: u64,
84}
85
86impl Default for DatabaseConfig {
87    fn default() -> Self {
88        Self {
89            schema: "public".to_string(),
90            max_retries: 3,
91            retry_delay: 1000,
92        }
93    }
94}
95
96/// Storage configuration
97#[derive(Debug, Clone)]
98pub struct StorageConfig {
99    /// Default bucket for operations
100    pub default_bucket: Option<String>,
101    /// File upload timeout in seconds
102    pub upload_timeout: u64,
103    /// Maximum file size in bytes
104    pub max_file_size: u64,
105}
106
107impl Default for StorageConfig {
108    fn default() -> Self {
109        Self {
110            default_bucket: None,
111            upload_timeout: 300,             // 5 minutes
112            max_file_size: 50 * 1024 * 1024, // 50MB
113        }
114    }
115}
116
117/// Generic response wrapper
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct SupabaseResponse<T> {
120    pub data: Option<T>,
121    pub error: Option<SupabaseError>,
122}
123
124/// Supabase API error response
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct SupabaseError {
127    pub message: String,
128    pub details: Option<String>,
129    pub hint: Option<String>,
130    pub code: Option<String>,
131}
132
133/// Pagination information
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct Pagination {
136    /// Current page number (0-based)
137    pub page: u32,
138    /// Number of items per page
139    pub per_page: u32,
140    /// Total number of items
141    pub total: Option<u64>,
142    /// Whether there are more pages
143    pub has_more: Option<bool>,
144}
145
146/// Query filter operations
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub enum FilterOperator {
149    #[serde(rename = "eq")]
150    Equal,
151    #[serde(rename = "neq")]
152    NotEqual,
153    #[serde(rename = "gt")]
154    GreaterThan,
155    #[serde(rename = "gte")]
156    GreaterThanOrEqual,
157    #[serde(rename = "lt")]
158    LessThan,
159    #[serde(rename = "lte")]
160    LessThanOrEqual,
161    #[serde(rename = "like")]
162    Like,
163    #[serde(rename = "ilike")]
164    ILike,
165    #[serde(rename = "is")]
166    Is,
167    #[serde(rename = "in")]
168    In,
169    #[serde(rename = "cs")]
170    Contains,
171    #[serde(rename = "cd")]
172    ContainedBy,
173    #[serde(rename = "sl")]
174    StrictlyLeft,
175    #[serde(rename = "sr")]
176    StrictlyRight,
177    #[serde(rename = "nxr")]
178    NotExtendToRight,
179    #[serde(rename = "nxl")]
180    NotExtendToLeft,
181    #[serde(rename = "adj")]
182    Adjacent,
183}
184
185/// Order direction for sorting
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub enum OrderDirection {
188    #[serde(rename = "asc")]
189    Ascending,
190    #[serde(rename = "desc")]
191    Descending,
192}
193
194/// HTTP method types
195#[derive(Debug, Clone, Copy)]
196pub enum HttpMethod {
197    Get,
198    Post,
199    Put,
200    Patch,
201    Delete,
202    Head,
203    Options,
204}
205
206impl HttpMethod {
207    pub fn as_str(&self) -> &'static str {
208        match self {
209            HttpMethod::Get => "GET",
210            HttpMethod::Post => "POST",
211            HttpMethod::Put => "PUT",
212            HttpMethod::Patch => "PATCH",
213            HttpMethod::Delete => "DELETE",
214            HttpMethod::Head => "HEAD",
215            HttpMethod::Options => "OPTIONS",
216        }
217    }
218}
219
220/// Generic timestamp type
221pub type Timestamp = DateTime<Utc>;
222
223/// Generic ID type
224pub type Id = Uuid;
225
226/// JSON value type for dynamic data
227pub type JsonValue = serde_json::Value;
228
229/// Headers type for HTTP requests
230pub type Headers = HashMap<String, String>;
231
232/// Query parameters type for HTTP requests
233pub type QueryParams = HashMap<String, String>;
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_http_config_default() {
241        let config = HttpConfig::default();
242        assert_eq!(config.timeout, 60);
243        assert_eq!(config.connect_timeout, 10);
244        assert_eq!(config.max_redirects, 10);
245    }
246
247    #[test]
248    fn test_auth_config_default() {
249        let config = AuthConfig::default();
250        assert!(config.auto_refresh_token);
251        assert_eq!(config.refresh_threshold, 300);
252        assert!(config.persist_session);
253    }
254
255    #[test]
256    fn test_filter_operator_serialization() {
257        use serde_json;
258        let op = FilterOperator::Equal;
259        let serialized = serde_json::to_string(&op).unwrap();
260        assert_eq!(serialized, "\"eq\"");
261    }
262
263    #[test]
264    fn test_order_direction() {
265        use serde_json;
266        let dir = OrderDirection::Ascending;
267        let serialized = serde_json::to_string(&dir).unwrap();
268        assert_eq!(serialized, "\"asc\"");
269    }
270
271    #[test]
272    fn test_http_method_as_str() {
273        assert_eq!(HttpMethod::Get.as_str(), "GET");
274        assert_eq!(HttpMethod::Post.as_str(), "POST");
275        assert_eq!(HttpMethod::Put.as_str(), "PUT");
276    }
277}