firebase/lib.rs
1/*!
2 # Firebase REST API for Rust
3 Please have a look at the ```Firebase``` struct to get started.
4 */
5
6extern crate curl;
7extern crate url;
8extern crate rustc_serialize;
9
10use std::str;
11use std::borrow::Cow;
12use std::collections::HashMap;
13use std::sync::Arc;
14use std::thread;
15use std::thread::JoinHandle;
16
17use curl::http;
18use url::Url;
19
20use rustc_serialize::Decodable;
21use rustc_serialize::json;
22pub use rustc_serialize::json::{Json, BuilderError, DecoderError};
23
24/// A Firebase instance to manage data.
25#[derive(Clone)]
26pub struct Firebase {
27 url: Arc<Url>,
28}
29
30// TODO: Change all instances of &str to Into<String>
31// TODO: Make FB instance from Url and String.
32// TODO: Make closure lives 'static or 'fb ?
33impl Firebase {
34 /// Creates a new Firebase instance from the url of the fb server
35 /// The url should be a valid **HTTPS** url, anything else will
36 /// result in an error:
37 ///
38 /// https://docs-examples.firebaseio.com/
39 ///
40 /// # Failures
41 /// - If a url is not specified with the HTTPS scheme, a ```Err(ParseError::UrlIsNotHTTPS)```
42 /// will be returned.
43 /// - If a url cannot be parsed into a valid url then a ```Err(ParseError::Parser(url::ParseError)```
44 /// will be returned.
45 pub fn new(url: &str) -> Result<Self, ParseError> {
46 let url = try!( parse(&url) );
47 if url.scheme != "https" {
48 return Err(ParseError::UrlIsNotHTTPS);
49 }
50 try!( unwrap_path(&url) );
51
52 Ok(Firebase {
53 url: Arc::new(url),
54 })
55 }
56
57 /// Creates a firebase reference from a borrow of a Url instance.
58 /// # Failures
59 /// - If a url is not specified with the HTTPS scheme, a ```Err(ParseError::UrlIsNotHTTPS)```
60 /// will be returned.
61 /// - If a url cannot be parsed into a valid url then a ```Err(ParseError::Parser(url::ParseError)```
62 /// will be returned.
63 pub fn from_url(url: &Url) -> Result<Self, ParseError> {
64 let url = url.clone();
65 try!( unwrap_path(&url) );
66
67 Ok(Firebase {
68 url: Arc::new(url),
69 })
70 }
71
72 /// Creates a new authenticated Firebase instance from the firebaseio url and an auth token.
73 ///
74 /// # Examples
75 /// ```
76 /// # use firebase::Firebase;
77 /// let fb = Firebase::authed("https://myfb.firebaseio.com", "deadbeefcafe");
78 /// // The url shoud now be: https://myfb.firebaseio.com?auth=deadbeefcafe
79 /// ```
80 ///
81 /// # Failures
82 /// - If a url is not specified with the HTTPS scheme, a ```Err(ParseError::UrlIsNotHTTPS)```
83 /// will be returned.
84 /// - If a url cannot be parsed into a valid url then a ```Err(ParseError::Parser(url::ParseError)```
85 /// will be returned.
86 pub fn authed(url: &str, auth_token: &str) -> Result<Self, ParseError> {
87 let mut url = try!( parse(&url) );
88 if url.scheme != "https" {
89 return Err(ParseError::UrlIsNotHTTPS);
90 }
91 try!( unwrap_path(&url) );
92
93 let opts = vec![ (AUTH, auth_token) ];
94 url.set_query_from_pairs(opts.into_iter());
95
96 Ok(Firebase {
97 url: Arc::new(url),
98 })
99 }
100
101 /// Creates a new firebase instance that extends the path of an old firebase instance.
102 /// Each time a reference is created a clone of the Firebase instance if done, all
103 /// Firebase instances follow this immutable style.
104 ///
105 /// #Examples
106 /// ```
107 /// # use firebase::Firebase;
108 /// // Points to the root of the db ( / )
109 /// let fb = Firebase::new("https://myfb.firebaseio.com").unwrap();
110 /// // A new reference to /friends/yasha
111 /// let yasha = fb.at("/friends/yasha").unwrap();
112 /// // A new reference to /friends/yasha/messages
113 /// let messages = yasha.at("messages").unwrap();
114 pub fn at(&self, add_path: &str) -> Result<Self, ParseError> {
115 let mut url = (*self.url).clone();
116
117 { // Add path to original path, already checked for path.
118 let mut path = url.path_mut().unwrap();
119 // Remove .json from the old path's end.
120 if let Some(end) = path.pop() {
121 path.push(end.trim_right_matches(".json").to_string());
122 }
123 let add_path = add_path.trim_matches('/');
124 let add_path = if !add_path.ends_with(".json") {
125 Cow::Owned(add_path.to_string() + ".json")
126 } else {
127 Cow::Borrowed(add_path)
128 };
129
130 for component in add_path.split("/").into_iter() {
131 path.push(component.to_string());
132 }
133 }
134
135 Ok(Firebase {
136 url: Arc::new(url),
137 })
138 }
139
140 /// Creates a FirebaseParams instance, this instance has query parameters
141 /// that are associated with it and that are used in every request made.
142 /// Since query parameters only affect incomming data from Firebase, you can only
143 /// GET data with a FirebaseParams instance.
144 ///
145 /// This constructor takes in a FbOps struct to define all of its parameters,
146 /// all undefined parameters can be omitted by extending the new FbOps struct
147 /// by its default.
148 ///
149 /// # Examples
150 /// ```
151 /// # use firebase::*;
152 /// let fb = Firebase::new("https://db.fb.com").unwrap();
153 /// let query = fb.ops(&FbOps {
154 /// order_by: Some("Hello World"),
155 /// limit_to_first: Some(5),
156 /// end_at: Some(7),
157 /// equal_to: Some(3),
158 /// shallow: Some(true),
159 /// format: Some(true),
160 /// .. FbOps::default()
161 /// });
162 /// ```
163 pub fn ops(&self, opts: &FbOps) -> FirebaseParams {
164 FirebaseParams::from_ops(&self.url, opts)
165 }
166
167 /// Returns the current URL as a string that will be used
168 /// to make the REST call when talking to Firebase.
169 pub fn get_url(&self) -> String {
170 self.url.serialize()
171 }
172
173 /// Gets data from Firebase.
174 /// # Examples
175 /// ```
176 /// # use firebase::Firebase;
177 /// let firebase = Firebase::new("https://shows.firebaseio.com").unwrap();
178 /// let episode = firebase.at("/futurama/episodes/140").unwrap();
179 /// let info = episode.get();
180 /// ```
181 pub fn get(&self) -> Result<Response, ReqErr> {
182 self.request(Method::GET, None)
183 }
184
185 /// Sets data to Firebase.
186 /// # Examples
187 /// ```
188 /// # use firebase::Firebase;
189 /// let firebase = Firebase::new("https://shows.firebaseio.com").unwrap();
190 /// let episode = firebase.at("/futurama/episodes/140/description").unwrap();
191 /// let info = episode.set("The Last Episode!");
192 /// ```
193 pub fn set(&self, data: &str) -> Result<Response, ReqErr> {
194 self.request(Method::PUT, Some(data))
195 }
196
197 /// Pushes data to Firebase.
198 /// # Examples
199 /// ```
200 /// # use firebase::Firebase;
201 /// let firebase = Firebase::new("https://shows.firebaseio.com").unwrap();
202 /// let episodes = firebase.at("/futurama/episodes").unwrap();
203 /// let info = episodes.push("The Lost Episode");
204 /// ```
205 pub fn push(&self, data: &str) -> Result<Response, ReqErr> {
206 self.request(Method::POST, Some(data))
207 }
208
209 /// Updates Firebase data.
210 /// # Examples
211 /// ```
212 /// # use firebase::Firebase;
213 /// let firebase = Firebase::new("https://shows.firebaseio.com").unwrap();
214 /// let desc = firebase.at("/futurama/episodes/140/description").unwrap();
215 /// let info = desc.update("The Penultimate Episode!");
216 /// ```
217 pub fn update(&self, data: &str) -> Result<Response, ReqErr> {
218 self.request(Method::PATCH, Some(data))
219 }
220
221 /// Removes Firebase data.
222 /// # Examples
223 /// ```
224 /// # use firebase::Firebase;
225 /// let firebase = Firebase::new("https://shows.firebaseio.com").unwrap();
226 /// let episode = firebase.at("/futurama/episodes/141").unwrap();
227 /// episode.remove();
228 /// ```
229 pub fn remove(&self) -> Result<Response, ReqErr> {
230 self.request(Method::DELETE, None)
231 }
232
233 /// Asynchronous version of the get method, takes a callback
234 /// and returns a handle to the thread making the request to Firebase.
235 /// # Examples
236 /// ```
237 /// # use firebase::Firebase;
238 /// let firebase = Firebase::new("https://shows.firebaseio.com").unwrap();
239 /// let desc = firebase.at("/futurama/episodes/141/description").unwrap();
240 /// let original = "The Lost Episode";
241 /// desc.get_async(move |result| {
242 /// if result.unwrap().body != original {
243 /// println!("The description changed!");
244 /// }
245 /// });
246 pub fn get_async<F>(&self, callback: F) -> JoinHandle<()>
247 where F: Fn(Result<Response, ReqErr>) + Send + 'static {
248 Firebase::request_url_async(&self.url, Method::GET, None, callback)
249 }
250
251 /// Asynchronous version of the set method, takes a callback
252 /// and returns a handle to the thread making the request to Firebase.
253 pub fn set_async<S, F>(&self, data: S, callback: F) -> JoinHandle<()>
254 where F: Fn(Result<Response, ReqErr>) + Send + 'static, S: Into<String> {
255 Firebase::request_url_async(&self.url, Method::PUT, Some(data.into()), callback)
256 }
257
258 /// Asynchronous version of the push method, takes a callback
259 /// and returns a handle to the thread making the request to Firebase.
260 pub fn push_async<S, F>(&self, data: S, callback: F) -> JoinHandle<()>
261 where F: Fn(Result<Response, ReqErr>) + Send + 'static, S: Into<String> {
262 Firebase::request_url_async(&self.url, Method::POST, Some(data.into()), callback)
263 }
264
265 /// Asynchronous version of the update method, takes a callback
266 /// and returns a handle to the thread making the request to Firebase.
267 pub fn update_async<S, F>(&self, data: S, callback: F) -> JoinHandle<()>
268 where F: Fn(Result<Response, ReqErr>) + Send + 'static, S: Into<String> {
269 Firebase::request_url_async(&self.url, Method::PATCH, Some(data.into()), callback)
270 }
271
272 /// Asynchronous version of the remove method, takes a callback
273 /// and returns a handle to the thread making the request to Firebase.
274 pub fn remove_async<F>(&self, callback: F) -> JoinHandle<()>
275 where F: Fn(Result<Response, ReqErr>) + Send + 'static {
276 Firebase::request_url_async(&self.url, Method::DELETE, None, callback)
277 }
278
279 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
280 /// knows how to GET data, and sorts this data by the key provided.
281 pub fn order_by(&self, key: &str) -> FirebaseParams {
282 self.with_params(ORDER_BY, key)
283 }
284
285 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
286 /// knows how to GET data, and limits the number of entries returned
287 /// on each request to the first ```count```. Often used with ```order_by```.
288 pub fn limit_to_first(&self, count: u32) -> FirebaseParams {
289 self.with_params(LIMIT_TO_FIRST, count)
290 }
291
292 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
293 /// knows how to GET data, and limits the number of entries returned
294 /// on each request to the last ```count```. Often used with ```order_by```.
295 pub fn limit_to_last(&self, count: u32) -> FirebaseParams {
296 self.with_params(LIMIT_TO_LAST, count)
297 }
298
299 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
300 /// knows how to GET data, and only returns entries starting after
301 /// the specified index. Often used with ```order_by```.
302 pub fn start_at(&self, index: u32) -> FirebaseParams {
303 self.with_params(START_AT, index)
304 }
305
306 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
307 /// knows how to GET data, and only returns entries appearing before
308 /// the specified index. Often used with ```order_by```.
309 pub fn end_at(&self, index: u32) -> FirebaseParams {
310 self.with_params(END_AT, index)
311 }
312
313 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
314 /// knows how to GET data, and returns only the entry at the specified
315 /// index. Often used with ```order_by```.
316 pub fn equal_to(&self, index: u32) -> FirebaseParams {
317 self.with_params(EQUAL_TO, index)
318 }
319
320 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
321 /// knows how to GET data, and only returns a shallow copy of the db
322 /// in every request.
323 pub fn shallow(&self, flag: bool) -> FirebaseParams {
324 self.with_params(SHALLOW, flag)
325 }
326
327 /// Creates a ```FirebaseParams``` instance, a Firebase struct that only
328 /// knows how to GET data, and formats the data to be exported in every
329 /// request. (e.g. includes a priority field).
330 pub fn format(&self) -> FirebaseParams {
331 self.with_params(FORMAT, EXPORT)
332 }
333
334 #[inline]
335 fn request(&self, method: Method, data: Option<&str>) -> Result<Response, ReqErr> {
336 Firebase::request_url(&self.url, method, data)
337 }
338
339 fn request_url(url: &Url, method: Method, data: Option<&str>) -> Result<Response, ReqErr> {
340 let mut handler = http::handle();
341
342 let req = match method {
343 Method::GET => handler.get( url),
344 Method::POST => handler.post( url, data.unwrap()),
345 Method::PUT => handler.put( url, data.unwrap()),
346 Method::PATCH => handler.patch( url, data.unwrap()),
347 Method::DELETE => handler.delete(url),
348 };
349
350 let res = match req.exec() {
351 Ok(r) => r,
352 Err(e) => return Err(ReqErr::NetworkErr(e)),
353 };
354
355 let body = match str::from_utf8(res.get_body()) {
356 Ok(b) => b,
357 Err(e) => return Err(ReqErr::RespNotUTF8(e)),
358 };
359
360 Ok(Response {
361 body: body.to_string(),
362 code: res.get_code(),
363 })
364 }
365
366 fn request_url_async<F>(url: &Arc<Url>, method: Method, data: Option<String>, callback: F) -> JoinHandle<()>
367 where F: Fn(Result<Response, ReqErr>) + Send + 'static {
368 // Fast, because its in an arc.
369 let url = url.clone();
370
371 thread::spawn(move || {
372 callback(Firebase::request_url(&url, method, data.as_ref().map(|s| s as &str)));
373 })
374 }
375
376 fn with_params<T: ToString>(&self, key: &'static str, value: T) -> FirebaseParams {
377 FirebaseParams::new(&self.url, key, value)
378 }
379}
380
381/// The FirebaseParams struct is a Firebase reference with attatched
382/// query parameters that allow you to sort, limit, and format the data
383/// received from Firebase.
384///
385/// It has been made into its own struct because the Firebase API specifies
386/// that you can only GET data with query parameters. And so taking advantage of
387/// type systems, this struct can only GET data.
388///
389/// You can add any number of parameters to this struct by chaining calls together:
390///
391/// ```
392/// # use firebase::*;
393/// let episodes = Firebase::new("https://futurama.firebaseio.com/episodes/").unwrap();
394/// let alphabetic = episodes.order_by("\"title\"").limit_to_first(5);
395/// let first5 = alphabetic.get();
396/// ```
397///
398/// Setting the same parameter overwrites the previous parameter:
399///
400/// ```
401/// # use firebase::*;
402/// let episodes = Firebase::new("https://arrdev.firebaseio.com/episodes/").unwrap();
403/// // This will create a request that gets entries starting at the 0th index.
404/// let skip10 = episodes.start_at(10).start_at(0);
405/// ```
406#[derive(Clone)]
407pub struct FirebaseParams {
408 url: Arc<Url>,
409 params: HashMap<&'static str, String>,
410}
411
412impl FirebaseParams {
413 /// Gets data from Firebase.
414 /// # Examples
415 /// ```
416 /// # use firebase::Firebase;
417 /// let episodes = Firebase::new("https://futurama.firebaseio.com/episodes/").unwrap();
418 /// let alphabetic = episodes.order_by("\"title\"").limit_to_first(5);
419 /// let first5 = alphabetic.get();
420 /// ```
421 pub fn get(&self) -> Result<Response, ReqErr> {
422 Firebase::request_url(&self.url, Method::GET, None)
423 }
424
425 /// Asynchronous version of the get method, takes a callback
426 /// and returns a handle to the thread making the request to Firebase.
427 pub fn get_async<F>(&self, callback: F) -> JoinHandle<()>
428 where F: Fn(Result<Response, ReqErr>) + Send + 'static {
429 Firebase::request_url_async(&self.url, Method::GET, None, callback)
430 }
431
432 /// Returns the current URL as a string that will be used
433 /// to make the REST call when talking to Firebase.
434 pub fn get_url(&self) -> String {
435 self.url.serialize()
436 }
437
438 // TODO: Wrap in quotes if not already. Or always wrap in quotes.
439 /// Modifies the current ```FirebaseParams``` instance
440 /// and sorts this data by the key provided.
441 pub fn order_by(self, key: &str) -> Self {
442 self.add_param(ORDER_BY, key)
443 }
444
445 /// Modifies the current ```FirebaseParams``` instance
446 /// and limits the number of entries returned
447 /// on each request to the first ```count```. Often used with ```order_by```.
448 pub fn limit_to_first(self, count: u32) -> Self {
449 self.add_param(LIMIT_TO_FIRST, count)
450 }
451
452 /// Modifies the current ```FirebaseParams``` instance
453 /// and limits the number of entries returned
454 /// on each request to the last ```count```. Often used with ```order_by```.
455 pub fn limit_to_last(self, count: u32) -> Self {
456 self.add_param(LIMIT_TO_LAST, count)
457 }
458
459 /// Modifies the current ```FirebaseParams``` instance
460 /// and only returns entries starting after
461 /// the specified index. Often used with ```order_by```.
462 pub fn start_at(self, index: u32) -> Self {
463 self.add_param(START_AT, index)
464 }
465
466 /// Modifies the current ```FirebaseParams``` instance
467 /// and only returns entries appearing before
468 /// the specified index. Often used with ```order_by```.
469 pub fn end_at(self, index: u32) -> Self {
470 self.add_param(END_AT, index)
471 }
472
473 /// Modifies the current ```FirebaseParams``` instance
474 /// and returns only the entry at the specified
475 /// index. Often used with ```order_by```.
476 pub fn equal_to(self, value: u32) -> Self {
477 self.add_param(EQUAL_TO, value)
478 }
479
480 /// Modifies the current ```FirebaseParams``` instance
481 /// and only returns a shallow copy of the db
482 /// in every request.
483 pub fn shallow(self, flag: bool) -> Self {
484 self.add_param(SHALLOW, flag)
485 }
486
487 /// Modifies the current ```FirebaseParams``` instance
488 /// and formats the data to be exported in every
489 /// request. (e.g. includes a priority field)
490 pub fn format(self) -> Self {
491 self.add_param(FORMAT, EXPORT)
492 }
493
494 fn add_param<T: ToString>(mut self, key: &'static str, value: T) -> Self {
495 let value = value.to_string();
496 self.params.insert(key, value);
497 self.set_params();
498 self
499 }
500
501 fn set_params(&mut self) {
502 // Only clones the url when edited. This is CoW
503 // Many threads can run requests without ever cloning the url.
504 let mut url = (*self.url).clone();
505 url.set_query_from_pairs(self.params.iter().map(|(&k, v)| (k, v as &str)));
506 self.url = Arc::new(url);
507 }
508
509 fn get_auth(url: &Url) -> HashMap<&'static str, String> {
510 let mut pair: HashMap<&'static str, String> = HashMap::new();
511
512 if let Some(queries) = url.query_pairs() {
513 for &(ref k, ref v) in queries.iter() {
514 if k == AUTH {
515 pair.insert(AUTH, v.to_string());
516 }
517 }
518 }
519 pair
520 }
521
522 fn new<T: ToString>(url: &Url, key: &'static str, value: T) -> Self {
523 let me = FirebaseParams {
524 url: Arc::new(url.clone()),
525 params: FirebaseParams::get_auth(&url),
526 };
527 me.add_param(key, value)
528 }
529
530 fn from_ops(url: &Url, opts: &FbOps) -> Self {
531 let mut me = FirebaseParams {
532 url: Arc::new(url.clone()),
533 params: FirebaseParams::get_auth(&url),
534 };
535 if let Some(order) = opts.order_by {
536 me.params.insert(ORDER_BY, order.to_string());
537 }
538 if let Some(first) = opts.limit_to_first {
539 me.params.insert(LIMIT_TO_FIRST, first.to_string());
540 }
541 if let Some(last) = opts.limit_to_last {
542 me.params.insert(LIMIT_TO_LAST, last.to_string());
543 }
544 if let Some(start) = opts.start_at {
545 me.params.insert(START_AT, start.to_string());
546 }
547 if let Some(end) = opts.end_at {
548 me.params.insert(END_AT, end.to_string());
549 }
550 if let Some(equal) = opts.equal_to {
551 me.params.insert(EQUAL_TO, equal.to_string());
552 }
553 if let Some(shallow) = opts.shallow {
554 me.params.insert(SHALLOW, shallow.to_string());
555 }
556 if let Some(format) = opts.format {
557 if format {
558 me.params.insert(FORMAT, EXPORT.to_string());
559 }
560 }
561 // Copy all of the params into the url.
562 me.set_params();
563 me
564 }
565}
566
567enum Method {
568 GET,
569 POST,
570 PUT,
571 PATCH,
572 DELETE,
573}
574
575const ORDER_BY: &'static str = "orderBy";
576const LIMIT_TO_FIRST: &'static str = "limitToFirst";
577const LIMIT_TO_LAST: &'static str = "limitToLast";
578const START_AT: &'static str = "startAt";
579const END_AT: &'static str = "endAt";
580const EQUAL_TO: &'static str = "equalTo";
581const SHALLOW: &'static str = "shallow";
582const FORMAT: &'static str = "format";
583const EXPORT: &'static str = "export";
584const AUTH: &'static str = "auth";
585
586#[derive(Debug)]
587pub struct FbOps<'l> {
588 pub order_by: Option<&'l str>,
589 pub limit_to_first: Option<u32>,
590 pub limit_to_last: Option<u32>,
591 pub start_at: Option<u32>,
592 pub end_at: Option<u32>,
593 pub equal_to: Option<u32>,
594 pub shallow: Option<bool>,
595 pub format: Option<bool>,
596}
597
598impl<'l> Default for FbOps<'l> {
599 fn default() -> Self {
600 FbOps {
601 order_by: None,
602 limit_to_first: None,
603 limit_to_last: None,
604 start_at: None,
605 end_at: None,
606 equal_to: None,
607 shallow: None,
608 format: None,
609 }
610 }
611}
612
613#[derive(Debug)]
614pub enum ReqErr {
615 ReqNotJSON,
616 RespNotUTF8(str::Utf8Error),
617 NetworkErr(curl::ErrCode),
618}
619
620#[derive(Debug)]
621pub enum ParseError {
622 UrlHasNoPath,
623 UrlIsNotHTTPS,
624 Parser(url::ParseError),
625}
626
627#[derive(Debug)]
628pub struct Response {
629 pub body: String,
630 pub code: u32,
631}
632
633impl Response {
634 /// Returns true if the status code is 200
635 pub fn is_success(&self) -> bool {
636 self.code == 200
637 }
638
639 /// Turns the response body into a Json enum.
640 pub fn json(&self) -> Result<Json, BuilderError> {
641 Json::from_str(&self.body)
642 }
643
644 /// Encodes the data received into a struct matching the data.
645 /// # Examples
646 ///
647 /// ```
648 /// # use firebase::Response;
649 /// let response = Response {
650 /// body: "324567898".to_string(),
651 /// code: 200,
652 /// };
653 ///
654 /// let parsed: u32 = response.parse().unwrap();
655 /// println!("Data is: {}", parsed);
656 /// ```
657 pub fn parse<D>(&self) -> Result<D, DecoderError> where D: Decodable {
658 json::decode(&self.body)
659 }
660}
661
662fn parse(url: &str) -> Result<Url, ParseError> {
663 match Url::parse(&url) {
664 Ok(u) => Ok(u),
665 Err(e) => Err(ParseError::Parser(e)),
666 }
667}
668
669fn unwrap_path(url: &Url) -> Result<&[String], ParseError> {
670 match url.path() {
671 None => return Err(ParseError::UrlHasNoPath),
672 Some(p) => return Ok(p),
673 }
674}
675
676// This code will happen when Trait Specialization becomes available
677// in rust.
678// pub trait ToJsonStr {
679// fn to_json_str(&self) -> Result<Cow<str>, /* TODO */ Box<Error>>;
680// }
681//
682// impl<'l> ToJsonStr for &'l str {
683// fn to_json_str(&self) -> Result<Cow<str>, /* TODO */ Box<Error>> {
684// Ok(Cow::Borrowed(self))
685// }
686// }
687//
688// impl<S> ToJsonStr for S where S: Encodable {
689// fn to_json_str(&self) -> Result<Cow<str>, /* TODO */ Box<Error>> {
690// match json::encode(self) {
691// Ok(encoded) => Ok(Cow::Owned(encoded)),
692// Err(e) => Err(Box::new(e)),
693// }
694// }
695// }