use serde::{de::DeserializeOwned, Serialize};
use crate::error::{ApiErrorResponse, CommonErrorCode};
use crate::ApiKey;
use crate::Endpoint;
use crate::Error;
use crate::LanguageCode;
use crate::Result;
#[derive(Clone, Debug)]
pub struct Client {
inner: reqwest::Client,
}
impl Default for Client {
fn default() -> Self {
Self::new()
}
}
impl Client {
pub fn new() -> Self {
Self {
inner: reqwest::Client::new(),
}
}
#[cfg(feature = "custom_client")]
pub fn custom(client: crate::reqwest::Client) -> Self {
Self {
inner: client,
}
}
#[allow(dead_code)]
pub(crate) fn inner(&self) -> &reqwest::Client {
&self.inner
}
pub(crate) async fn send_post<T, U>(
&self,
endpoint: Endpoint,
api_key: &ApiKey,
request_payload: T,
locale: Option<LanguageCode>,
) -> Result<U>
where
T: Serialize,
U: DeserializeOwned,
{
let url = format!(
"https://identitytoolkit.googleapis.com/v1/{}?key={}",
endpoint.format(),
api_key.inner()
);
let mut builder = self
.inner
.post(url)
.json(&request_payload);
if let Some(locale) = locale {
builder = builder.headers(optional_locale_header(locale)?);
}
let response = builder
.send()
.await
.map_err(Error::HttpRequestError)?;
let status_code = response.status();
let response_text = response
.text()
.await
.map_err(|error| Error::ReadResponseTextFailed {
error,
})?;
if status_code.is_success() {
serde_json::from_str::<U>(&response_text).map_err(|error| {
Error::DeserializeResponseJsonFailed {
error,
json: response_text,
}
})
}
else {
let error_response =
serde_json::from_str::<ApiErrorResponse>(&response_text)
.map_err(|error| {
Error::DeserializeErrorResponseJsonFailed {
error,
json: response_text,
}
})?;
let error_code: CommonErrorCode = error_response
.error
.message
.clone()
.into();
match error_code {
| CommonErrorCode::InvalidIdToken => Err(Error::InvalidIdToken),
| _ => Err(Error::ApiError {
status_code,
error_code,
response: error_response,
}),
}
}
}
}
fn optional_locale_header(
locale: LanguageCode
) -> Result<reqwest::header::HeaderMap> {
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
"X-Firebase-Locale",
reqwest::header::HeaderValue::from_str(locale.format()).map_err(
|error| Error::InvalidHeaderValue {
key: "X-Firebase-Locale",
error,
},
)?,
);
Ok(headers)
}