supabase_rust_gftd/lib.rs
1//! Supabase Rust Client Library
2//!
3//! A Rust client library for Supabase, providing access to Supabase services including
4//! database, auth, storage, and realtime subscriptions.
5//!
6//! This is a wrapper around individual Supabase component crates that provides
7//! a unified and convenient API that matches the JavaScript supabase-js client.
8
9// 各コンポーネントクレートの再エクスポート
10pub use supabase_rust_auth as auth;
11pub use supabase_rust_postgrest as postgrest;
12// pub use supabase_rust_storage as storage;
13// pub use supabase_rust_realtime as realtime;
14// pub use supabase_rust_functions as functions;
15
16// 内部モジュール
17mod config;
18mod error;
19
20// 公開エクスポート
21pub use config::ClientOptions;
22pub use error::{Error, Result};
23
24use reqwest::Client;
25// 使われていないインポートを削除
26// use reqwest::header::{HeaderMap, HeaderValue, HeaderName};
27// use serde::{Serialize, Deserialize};
28// use serde_json::Value;
29// use std::collections::HashMap;
30// use thiserror::Error;
31// use url::Url;
32// use serde_json::json;
33// use std::sync::Arc;
34// use std::sync::atomic::{AtomicBool, Ordering};
35use serde::Serialize;
36
37/// The main entry point for the Supabase Rust client
38pub struct Supabase {
39 /// The base URL for the Supabase project
40 pub url: String,
41 /// The anonymous API key for the Supabase project
42 pub key: String,
43 /// HTTP client used for requests
44 pub http_client: Client,
45 /// Auth client for user management and authentication
46 pub auth: auth::Auth,
47 /// Client options
48 pub options: config::ClientOptions,
49}
50
51impl Supabase {
52 /// Create a new Supabase client
53 ///
54 /// # Arguments
55 ///
56 /// * `supabase_url` - The base URL for your Supabase project
57 /// * `supabase_key` - The anonymous API key for your Supabase project
58 ///
59 /// # Example
60 ///
61 /// ```
62 /// use supabase_rust::Supabase;
63 ///
64 /// let supabase = Supabase::new("https://your-project-url.supabase.co", "your-anon-key");
65 /// ```
66 pub fn new(supabase_url: &str, supabase_key: &str) -> Self {
67 Self::new_with_options(supabase_url, supabase_key, ClientOptions::default())
68 }
69
70 /// Create a new Supabase client with custom options
71 ///
72 /// # Arguments
73 ///
74 /// * `supabase_url` - The base URL for your Supabase project
75 /// * `supabase_key` - The anonymous API key for your Supabase project
76 /// * `options` - Custom client options
77 ///
78 /// # Example
79 ///
80 /// ```
81 /// use supabase_rust::{Supabase, ClientOptions};
82 ///
83 /// let options = ClientOptions::default().with_auto_refresh_token(true);
84 /// let supabase = Supabase::new_with_options(
85 /// "https://your-project-url.supabase.co",
86 /// "your-anon-key",
87 /// options
88 /// );
89 /// ```
90 pub fn new_with_options(supabase_url: &str, supabase_key: &str, options: ClientOptions) -> Self {
91 let http_client = Client::new();
92
93 let auth_options = auth::AuthOptions {
94 auto_refresh_token: options.auto_refresh_token,
95 persist_session: options.persist_session,
96 detect_session_in_url: options.detect_session_in_url,
97 };
98
99 let auth = auth::Auth::new(supabase_url, supabase_key, http_client.clone(), auth_options);
100
101 Self {
102 url: supabase_url.to_string(),
103 key: supabase_key.to_string(),
104 http_client,
105 auth,
106 options,
107 }
108 }
109
110 /// Get a reference to the auth client for user management and authentication
111 pub fn auth(&self) -> &auth::Auth {
112 &self.auth
113 }
114
115 /// Create a new PostgrestClient for database operations on a specific table or view
116 ///
117 /// # Arguments
118 ///
119 /// * `table` - The name of the table or view
120 ///
121 /// # Example
122 ///
123 /// ```
124 /// use supabase_rust::Supabase;
125 ///
126 /// let supabase = Supabase::new("https://your-project-url.supabase.co", "your-anon-key");
127 /// let query = supabase.from("users");
128 /// ```
129 pub fn from(&self, table: &str) -> postgrest::PostgrestClient {
130 postgrest::PostgrestClient::new(
131 &self.url,
132 &self.key,
133 table,
134 self.http_client.clone(),
135 )
136 }
137
138 /// Create a client for the Storage API
139 pub fn storage(&self) -> /* storage::StorageClient */ () {
140 /* storage::StorageClient::new(&self.url, &self.key, self.http_client.clone()) */
141 // Storage client is temporarily disabled until the crate is published
142 panic!("Storage client is not available in this version")
143 }
144
145 /// Create a client for the Realtime API
146 pub fn realtime(&self) -> /* realtime::RealtimeClient */ () {
147 /* realtime::RealtimeClient::new(&self.url, &self.key) */
148 // Realtime client is temporarily disabled until the crate is published
149 panic!("Realtime client is not available in this version")
150 }
151
152 /// Create a client for the Edge Functions API
153 pub fn functions(&self) -> /* functions::FunctionsClient */ () {
154 /* functions::FunctionsClient::new(&self.url, &self.key, self.http_client.clone()) */
155 // Functions client is temporarily disabled until the crate is published
156 panic!("Functions client is not available in this version")
157 }
158
159 /// Execute a Postgres function via RPC
160 ///
161 /// # Arguments
162 ///
163 /// * `function_name` - The name of the function to call
164 /// * `params` - Parameters to pass to the function
165 ///
166 /// # Example
167 ///
168 /// ```
169 /// use supabase_rust::Supabase;
170 /// use serde_json::json;
171 ///
172 /// let supabase = Supabase::new("https://your-project-url.supabase.co", "your-anon-key");
173 /// let result = supabase.rpc("calculate_total", json!({"user_id": 123}));
174 /// ```
175 pub fn rpc(&self, function_name: &str, params: serde_json::Value) -> postgrest::PostgrestClient {
176 postgrest::PostgrestClient::rpc(
177 &self.url,
178 &self.key,
179 function_name,
180 params,
181 self.http_client.clone()
182 )
183 }
184}
185
186/// A convenience module for common imports
187pub mod prelude {
188 pub use crate::Supabase;
189 pub use crate::error::{Error, Result};
190 pub use crate::config::ClientOptions;
191 pub use crate::postgrest::{IsolationLevel, TransactionMode};
192}
193
194/// フィルター演算子
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
196#[serde(rename_all = "lowercase")]
197pub enum FilterOperator {
198 /// 等しい
199 Eq,
200 /// 等しくない
201 Neq,
202 /// より大きい
203 Gt,
204 /// より大きいか等しい
205 Gte,
206 /// より小さい
207 Lt,
208 /// より小さいか等しい
209 Lte,
210 /// 含む
211 In,
212 /// 含まない
213 NotIn,
214 /// 近い値(配列内の値に対して)
215 ContainedBy,
216 /// 含む(配列が対象の値を含む)
217 Contains,
218 /// 完全に含む(配列が対象の配列のすべての要素を含む)
219 ContainedByArray,
220 /// LIKE演算子(ワイルドカード検索)
221 Like,
222 /// ILIKE演算子(大文字小文字を区別しないワイルドカード検索)
223 ILike,
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use wiremock::{MockServer, Mock, ResponseTemplate};
230 use wiremock::matchers::{method, path, query_param};
231 use serde_json::json;
232
233 #[tokio::test]
234 async fn test_integration() {
235 let mock_server = MockServer::start().await;
236
237 // Auth モックエンドポイント
238 Mock::given(method("POST"))
239 .and(path("/auth/v1/token"))
240 .respond_with(ResponseTemplate::new(200)
241 .set_body_json(json!({
242 "access_token": "test_token",
243 "refresh_token": "test_refresh",
244 "expires_in": 3600,
245 "token_type": "bearer",
246 "user": {
247 "id": "1234",
248 "email": "test@example.com"
249 }
250 }))
251 )
252 .mount(&mock_server)
253 .await;
254
255 // Database モックエンドポイント
256 Mock::given(method("GET"))
257 .and(path("/rest/v1/users"))
258 .respond_with(ResponseTemplate::new(200)
259 .set_body_json(json!([
260 { "id": 1, "name": "Test User" }
261 ]))
262 )
263 .mount(&mock_server)
264 .await;
265
266 // Storage モックエンドポイント
267 Mock::given(method("GET"))
268 .and(path("/storage/v1/bucket"))
269 .respond_with(ResponseTemplate::new(200)
270 .set_body_json(json!([
271 { "id": "test-bucket", "name": "test-bucket", "public": true, "owner": "owner", "created_at": "2023-01-01", "updated_at": "2023-01-01" }
272 ]))
273 )
274 .mount(&mock_server)
275 .await;
276
277 let supabase = Supabase::new(&mock_server.uri(), "test_key");
278
279 // Database操作のテスト
280 let users: Vec<serde_json::Value> = supabase
281 .from("users")
282 .select("*")
283 .execute()
284 .await
285 .unwrap();
286
287 assert_eq!(users.len(), 1);
288 assert_eq!(users[0]["name"], "Test User");
289
290 // Storageバケット一覧取得のテスト
291 let buckets = supabase.storage().list_buckets().await.unwrap();
292
293 assert_eq!(buckets.len(), 1);
294 assert_eq!(buckets[0].name, "test-bucket");
295 }
296}