firebase_rs/lib.rs
1use constants::{Method, Response, AUTH};
2use errors::{RequestResult, UrlParseResult};
3use params::Params;
4use reqwest::StatusCode;
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7use serde_json::Value;
8use std::fmt::Debug;
9use url::Url;
10use utils::check_uri;
11
12use crate::sse::ServerEvents;
13
14pub use errors::{RequestError, ServerEventError, UrlParseError};
15
16mod constants;
17mod errors;
18mod params;
19mod sse;
20mod utils;
21
22#[derive(Debug)]
23pub struct Firebase {
24 uri: Url,
25}
26
27impl Firebase {
28 /// ```rust
29 /// use firebase_rs::Firebase;
30 ///
31 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap();
32 /// ```
33 pub fn new(uri: &str) -> UrlParseResult<Self>
34 where
35 Self: Sized,
36 {
37 match check_uri(&uri) {
38 Ok(uri) => Ok(Self { uri }),
39 Err(err) => Err(err),
40 }
41 }
42
43 /// ```rust
44 /// const URI: &str = "...";
45 /// const AUTH_KEY: &str = "...";
46 ///
47 /// use firebase_rs::Firebase;
48 ///
49 /// let firebase = Firebase::auth("https://myfirebase.firebaseio.com", AUTH_KEY).unwrap();
50 /// ```
51 pub fn auth(uri: &str, auth_key: &str) -> UrlParseResult<Self>
52 where
53 Self: Sized,
54 {
55 match check_uri(&uri) {
56 Ok(mut uri) => {
57 uri.set_query(Some(&format!("{}={}", AUTH, auth_key)));
58 Ok(Self { uri })
59 }
60 Err(err) => Err(err),
61 }
62 }
63
64 /// ```rust
65 /// use firebase_rs::Firebase;
66 ///
67 /// # async fn run() {
68 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().with_params().start_at(1).order_by("name").equal_to(5).finish();
69 /// let result = firebase.get::<String>().await;
70 /// # }
71 /// ```
72 pub fn with_params(&self) -> Params {
73 let uri = self.uri.clone();
74 Params::new(uri)
75 }
76
77 /// To use simple interface with synchronous callbacks, pair with `.listen()`:
78 /// ```rust
79 /// use firebase_rs::Firebase;
80 /// # async fn run() {
81 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users");
82 /// let stream = firebase.with_realtime_events().unwrap();
83 /// stream.listen(|event_type, data| {
84 /// println!("{:?} {:?}" ,event_type, data);
85 /// }, |err| println!("{:?}" ,err), false).await;
86 /// # }
87 /// ```
88 ///
89 /// To use streaming interface for async code, pair with `.stream()`:
90 /// ```rust
91 /// use firebase_rs::Firebase;
92 /// use futures_util::StreamExt;
93 ///
94 /// # async fn run() {
95 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users");
96 /// let stream = firebase.with_realtime_events()
97 /// .unwrap()
98 /// .stream(true);
99 /// stream.for_each(|event| {
100 /// match event {
101 /// Ok((event_type, maybe_data)) => println!("{:?} {:?}" ,event_type, maybe_data),
102 /// Err(err) => println!("{:?}" ,err),
103 /// }
104 /// futures_util::future::ready(())
105 /// }).await;
106 /// # }
107 /// ```
108 pub fn with_realtime_events(&self) -> Option<ServerEvents> {
109 ServerEvents::new(self.uri.as_str())
110 }
111
112 /// ```rust
113 /// use firebase_rs::Firebase;
114 ///
115 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users").at("USER_ID").at("f69111a8a5258c15286d3d0bd4688c55");
116 /// ```
117 pub fn at(&self, path: &str) -> Self {
118 let uri = self.build_uri(path);
119
120 Firebase { uri }
121 }
122
123 fn build_uri(&self, path: &str) -> Url {
124 let mut new_path = String::new();
125
126 if let Some(segments) = self.uri.path_segments() {
127 for segment in segments {
128 let clean_segment = segment.trim_end_matches(".json");
129 new_path.push_str(clean_segment);
130 new_path.push('/');
131 }
132 }
133
134 new_path.push_str(path);
135
136 let final_path = if new_path.ends_with(".json") {
137 new_path.trim_end_matches(".json").to_string()
138 } else {
139 new_path
140 };
141
142 let mut uri = self.uri.clone();
143 uri.set_path(&format!("{}.json", final_path));
144
145 uri
146 }
147
148 /// ```rust
149 /// use firebase_rs::Firebase;
150 ///
151 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users");
152 /// let uri = firebase.get_uri();
153 /// ```
154 pub fn get_uri(&self) -> String {
155 self.uri.to_string()
156 }
157
158 async fn request(&self, method: Method, data: Option<Value>) -> RequestResult<Response> {
159 let client = reqwest::Client::new();
160 let request = match method {
161 Method::GET => client.get(self.uri.to_string()).send().await,
162 Method::PUT | Method::PATCH | Method::POST => {
163 if data.is_none() {
164 return Err(RequestError::NotFoundOrNullBody);
165 }
166 let builder = if method == Method::PUT {
167 client.put(self.uri.to_string())
168 } else if method == Method::POST {
169 client.post(self.uri.to_string())
170 } else {
171 client.patch(self.uri.to_string())
172 };
173 builder.json(&data).send().await
174 }
175 Method::DELETE => client.delete(self.uri.to_string()).send().await,
176 };
177
178 match request {
179 Ok(response) => match response.status() {
180 StatusCode::OK | StatusCode::NO_CONTENT => {
181 let response_text = response.text().await.unwrap_or_default();
182 Ok(Response {
183 data: response_text,
184 })
185 }
186 StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => Err(RequestError::Unauthorized),
187 StatusCode::NOT_FOUND => Err(RequestError::NotFoundOrNullBody),
188 _ => Err(RequestError::NetworkError),
189 },
190 Err(_) => Err(RequestError::NetworkError),
191 }
192 }
193
194 async fn request_generic<T>(&self, method: Method) -> RequestResult<T>
195 where
196 T: DeserializeOwned + Debug,
197 {
198 let request = self.request(method, None).await;
199
200 match request {
201 Ok(response) => {
202 let value: serde_json::Value = serde_json::from_str(response.data.as_str())
203 .map_err(|_| RequestError::NotJSON)?;
204
205 let data: T = serde_json::from_value(value)
206 .map_err(RequestError::DeserializeError)?;
207
208 Ok(data)
209 }
210 Err(err) => Err(err),
211 }
212 }
213
214 /// ```rust
215 /// use firebase_rs::Firebase;
216 /// use serde::{Serialize, Deserialize};
217 ///
218 /// #[derive(Serialize, Deserialize, Debug)]
219 /// struct User {
220 /// name: String
221 /// }
222 ///
223 /// # async fn run() {
224 /// let user = User { name: String::default() };
225 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users");
226 /// let users = firebase.set(&user).await;
227 /// # }
228 /// ```
229 pub async fn set<T>(&self, data: &T) -> RequestResult<Response>
230 where
231 T: Serialize + Debug,
232 {
233 let data = serde_json::to_value(&data).map_err(RequestError::SerializeError)?;
234 self.request(Method::POST, Some(data)).await
235 }
236
237 /// ```rust
238 /// use firebase_rs::Firebase;
239 /// use serde::{Serialize, Deserialize};
240 ///
241 /// #[derive(Serialize, Deserialize, Debug)]
242 /// struct User {
243 /// name: String
244 /// }
245 ///
246 /// # async fn run() {
247 /// let user = User { name: String::default() };
248 /// let mut firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users");
249 /// let users = firebase.set_with_key("myKey", &user).await;
250 /// # }
251 /// ```
252 pub async fn set_with_key<T>(&mut self, key: &str, data: &T) -> RequestResult<Response>
253 where
254 T: Serialize + Debug,
255 {
256 self.uri = self.build_uri(key);
257 let data = serde_json::to_value(&data).map_err(RequestError::SerializeError)?;
258
259 self.request(Method::PUT, Some(data)).await
260 }
261
262 /// ```rust
263 /// use std::collections::HashMap;
264 /// use firebase_rs::Firebase;
265 /// use serde::{Serialize, Deserialize};
266 ///
267 /// #[derive(Serialize, Deserialize, Debug)]
268 /// struct User {
269 /// name: String
270 /// }
271 ///
272 /// # async fn run() {
273 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users");
274 /// let users = firebase.get::<HashMap<String, User>>().await;
275 /// # }
276 /// ```
277 pub async fn get_as_string(&self) -> RequestResult<Response> {
278 self.request(Method::GET, None).await
279 }
280
281 /// ```rust
282 /// use std::collections::HashMap;
283 /// use firebase_rs::Firebase;
284 /// use serde::{Serialize, Deserialize};
285 ///
286 /// #[derive(Serialize, Deserialize, Debug)]
287 /// struct User {
288 /// name: String
289 /// }
290 ///
291 /// # async fn run() {
292 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users").at("USER_ID");
293 /// let user = firebase.get::<User>().await;
294 ///
295 /// // OR
296 ///
297 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users");
298 /// let user = firebase.get::<HashMap<String, User>>().await;
299 /// # }
300 /// ```
301 pub async fn get<T>(&self) -> RequestResult<T>
302 where
303 T: DeserializeOwned + Debug,
304 {
305 self.request_generic::<T>(Method::GET).await
306 }
307
308 /// ```rust
309 /// use firebase_rs::Firebase;
310 ///
311 /// # async fn run() {
312 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users").at("USER_ID");
313 /// firebase.delete().await;
314 /// # }
315 /// ```
316 pub async fn delete(&self) -> RequestResult<Response> {
317 self.request(Method::DELETE, None).await
318 }
319
320 /// ```rust
321 /// use firebase_rs::Firebase;
322 /// use serde::{Serialize, Deserialize};
323 ///
324 /// #[derive(Serialize, Deserialize, Debug)]
325 /// struct User {
326 /// name: String
327 /// }
328 ///
329 /// # async fn run() {
330 /// let user = User { name: String::default() };
331 /// let firebase = Firebase::new("https://myfirebase.firebaseio.com").unwrap().at("users").at("USER_ID");
332 /// let users = firebase.update(&user).await;
333 /// # }
334 /// ```
335 pub async fn update<T>(&self, data: &T) -> RequestResult<Response>
336 where
337 T: Serialize + Debug,
338 {
339 let value = serde_json::to_value(&data).map_err(RequestError::SerializeError)?;
340 self.request(Method::PATCH, Some(value)).await
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use crate::{Firebase, UrlParseError};
347
348 const URI: &str = "https://firebase_id.firebaseio.com";
349 const URI_WITH_SLASH: &str = "https://firebase_id.firebaseio.com/";
350 const URI_NON_HTTPS: &str = "http://firebase_id.firebaseio.com/";
351
352 #[tokio::test]
353 async fn simple() {
354 let firebase = Firebase::new(URI).unwrap();
355 assert_eq!(URI_WITH_SLASH.to_string(), firebase.get_uri());
356 }
357
358 #[tokio::test]
359 async fn non_https() {
360 let firebase = Firebase::new(URI_NON_HTTPS).map_err(|e| e.to_string());
361 assert_eq!(
362 firebase.err(),
363 Some(String::from(UrlParseError::NotHttps.to_string()))
364 );
365 }
366
367 #[tokio::test]
368 async fn with_auth() {
369 let firebase = Firebase::auth(URI, "auth_key").unwrap();
370 assert_eq!(
371 format!("{}/?auth=auth_key", URI.to_string()),
372 firebase.get_uri()
373 );
374 }
375
376 #[tokio::test]
377 async fn with_sse_events() {
378 // TODO: SSE Events Test
379 }
380}