firestore_db_and_auth/
errors.rs

1//! # Error and Result Type
2
3use std::error;
4use std::fmt;
5
6use reqwest;
7use reqwest::StatusCode;
8use serde::{Deserialize, Serialize};
9
10/// A result type that uses [`FirebaseError`] as an error type
11pub type Result<T> = std::result::Result<T, FirebaseError>;
12
13/// The main error type used throughout this crate. It wraps / converts from a few other error
14/// types and implements [error::Error] so that you can use it in any situation where the
15/// standard error type is expected.
16#[derive(Debug)]
17pub enum FirebaseError {
18    /// Generic errors are very rarely used and only used if no other error type matches
19    Generic(&'static str),
20    /// If the http status code is != 200 and no Google error response is attached
21    /// (see https://firebase.google.com/docs/reference/rest/auth#section-error-format)
22    /// then this error type will be returned
23    UnexpectedResponse(&'static str, reqwest::StatusCode, String, String),
24    /// An error returned by the Firestore API - Contains the numeric code, a string code and
25    /// a context. If the APIError happened on a document query or mutation, the document
26    /// path will be set as context.
27    /// If the APIError happens on a user_* method, the user id will be set as context.
28    /// For example: 400, CREDENTIAL_TOO_OLD_LOGIN_AGAIN
29    APIError(usize, String, String),
30    /// An error caused by the http library. This only happens if the http request is badly
31    /// formatted (too big, invalid characters) or if the server did strange things
32    /// (connection abort, ssl verification error).
33    Request(reqwest::Error),
34    /// Should not happen. If jwt encoding / decoding fails or an value cannot be extracted or
35    /// a jwt is badly formatted or corrupted
36    JWT(biscuit::errors::Error),
37    JWTValidation(biscuit::errors::ValidationError),
38    /// Serialisation failed
39    Ser {
40        doc: Option<String>,
41        ser: serde_json::Error,
42    },
43    /// Verbose deserialization failure
44    SerdeVerbose {
45        doc: Option<String>,
46        input_doc: String,
47        ser: serde_json::Error,
48    },
49    /// When the credentials.json file contains an invalid private key this error is returned
50    RSA(ring::error::KeyRejected),
51    /// Disk access errors
52    IO(std::io::Error),
53}
54
55impl std::convert::From<std::io::Error> for FirebaseError {
56    fn from(error: std::io::Error) -> Self {
57        FirebaseError::IO(error)
58    }
59}
60
61impl std::convert::From<ring::error::KeyRejected> for FirebaseError {
62    fn from(error: ring::error::KeyRejected) -> Self {
63        FirebaseError::RSA(error)
64    }
65}
66
67impl std::convert::From<serde_json::Error> for FirebaseError {
68    fn from(error: serde_json::Error) -> Self {
69        FirebaseError::Ser { doc: None, ser: error }
70    }
71}
72
73impl std::convert::From<biscuit::errors::Error> for FirebaseError {
74    fn from(error: biscuit::errors::Error) -> Self {
75        FirebaseError::JWT(error)
76    }
77}
78
79impl std::convert::From<biscuit::errors::ValidationError> for FirebaseError {
80    fn from(error: biscuit::errors::ValidationError) -> Self {
81        FirebaseError::JWTValidation(error)
82    }
83}
84
85impl std::convert::From<reqwest::Error> for FirebaseError {
86    fn from(error: reqwest::Error) -> Self {
87        FirebaseError::Request(error)
88    }
89}
90
91impl fmt::Display for FirebaseError {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        match self {
94            FirebaseError::Generic(m) => write!(f, "{}", m),
95            FirebaseError::APIError(code, m, context) => {
96                write!(f, "API Error! Code {} - {}. Context: {}", code, m, context)
97            }
98            FirebaseError::UnexpectedResponse(m, status, text, source) => {
99                writeln!(f, "{} - {}", &m, status)?;
100                writeln!(f, "{}", text)?;
101                writeln!(f, "{}", source)?;
102                Ok(())
103            }
104            FirebaseError::Request(e) => e.fmt(f),
105            FirebaseError::JWT(e) => e.fmt(f),
106            FirebaseError::JWTValidation(e) => e.fmt(f),
107            FirebaseError::RSA(e) => e.fmt(f),
108            FirebaseError::IO(e) => e.fmt(f),
109            FirebaseError::Ser { doc, ser } => {
110                if let Some(doc) = doc {
111                    writeln!(f, "{} in document {}", ser, doc)
112                } else {
113                    ser.fmt(f)
114                }
115            }
116            FirebaseError::SerdeVerbose { doc, input_doc, ser } => {
117                let doc = doc.clone().unwrap_or("Unknown document".to_string());
118                writeln!(
119                    f,
120                    "Serde deserialization failed for document '{}' with error '{}' on input: '{}'",
121                    doc, ser, input_doc
122                )
123            }
124        }
125    }
126}
127
128impl error::Error for FirebaseError {
129    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
130        match *self {
131            FirebaseError::Generic(ref _m) => None,
132            FirebaseError::UnexpectedResponse(_, _, _, _) => None,
133            FirebaseError::APIError(_, _, _) => None,
134            FirebaseError::Request(ref e) => Some(e),
135            FirebaseError::JWT(ref e) => Some(e),
136            FirebaseError::JWTValidation(ref e) => Some(e),
137            FirebaseError::RSA(_) => None,
138            FirebaseError::IO(ref e) => Some(e),
139            FirebaseError::Ser { ref ser, .. } => Some(ser),
140            FirebaseError::SerdeVerbose { ref ser, .. } => Some(ser),
141        }
142    }
143}
144
145#[derive(Default, Serialize, Deserialize)]
146struct GoogleRESTApiError {
147    pub message: String,
148    pub domain: String,
149    pub reason: String,
150}
151
152#[derive(Default, Serialize, Deserialize)]
153struct GoogleRESTApiErrorInfo {
154    pub code: usize,
155    pub message: String,
156    pub errors: Option<Vec<GoogleRESTApiError>>,
157}
158
159#[derive(Default, Serialize, Deserialize)]
160struct GoogleRESTApiErrorWrapper {
161    pub error: Option<GoogleRESTApiErrorInfo>,
162}
163
164/// If the given reqwest response is status code 200, nothing happens
165/// Otherwise the response will be analysed if it contains a Google API Error response.
166/// See https://firebase.google.com/docs/reference/rest/auth#section-error-response
167///
168/// Arguments:
169/// - response: The http requests response. Must be mutable, because the contained value will be extracted in an error case
170/// - context: A function that will be called in an error case that returns a context string
171pub(crate) fn extract_google_api_error(
172    response: reqwest::blocking::Response,
173    context: impl Fn() -> String,
174) -> Result<reqwest::blocking::Response> {
175    if response.status() == 200 {
176        return Ok(response);
177    }
178
179    Err(extract_google_api_error_intern(
180        response.status().clone(),
181        response.text()?,
182        context,
183    ))
184}
185
186/// If the given reqwest response is status code 200, nothing happens
187/// Otherwise the response will be analysed if it contains a Google API Error response.
188/// See https://firebase.google.com/docs/reference/rest/auth#section-error-response
189///
190/// Arguments:
191/// - response: The http requests response. Must be mutable, because the contained value will be extracted in an error case
192/// - context: A function that will be called in an error case that returns a context string
193pub(crate) async fn extract_google_api_error_async(
194    response: reqwest::Response,
195    context: impl Fn() -> String,
196) -> Result<reqwest::Response> {
197    if response.status() == 200 {
198        return Ok(response);
199    }
200
201    Err(extract_google_api_error_intern(
202        response.status().clone(),
203        response.text().await?,
204        context,
205    ))
206}
207
208fn extract_google_api_error_intern(
209    status: StatusCode,
210    http_body: String,
211    context: impl Fn() -> String,
212) -> FirebaseError {
213    let google_api_error_wrapper: std::result::Result<GoogleRESTApiErrorWrapper, serde_json::Error> =
214        serde_json::from_str(&http_body);
215    if let Ok(google_api_error_wrapper) = google_api_error_wrapper {
216        if let Some(google_api_error) = google_api_error_wrapper.error {
217            return FirebaseError::APIError(google_api_error.code, google_api_error.message.to_owned(), context());
218        }
219    };
220
221    FirebaseError::UnexpectedResponse("", status, http_body, context())
222}