use chrono::{DateTime, Utc};
use serde::{
de::{Error, Unexpected},
Deserialize, Deserializer, Serialize, Serializer,
};
#[derive(Debug, Serialize)]
pub(crate) struct RedditAuth {
pub(crate) access_token: String,
pub(crate) token_type: Bearer,
pub(crate) expires_at: DateTime<Utc>,
pub(crate) scope: String,
}
impl<'de> Deserialize<'de> for RedditAuth {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum Proxy {
FromReddit {
access_token: String,
token_type: Bearer,
expires_in: u32,
scope: String,
},
FromFile {
access_token: String,
token_type: Bearer,
expires_at: DateTime<Utc>,
scope: String,
},
}
let proxy = Proxy::deserialize(deserializer)?;
match proxy {
Proxy::FromReddit {
access_token,
token_type,
expires_in,
scope,
} => Ok(Self {
access_token,
scope,
expires_at: Utc::now()
.checked_add_signed(chrono::Duration::seconds(i64::from(expires_in)))
.expect("datetime overflowed for auth; this should be unreachable."),
token_type,
}),
Proxy::FromFile {
access_token,
token_type,
expires_at,
scope,
} => Ok(Self {
access_token,
token_type,
expires_at,
scope,
}),
}
}
}
#[derive(Debug)]
pub(crate) struct Bearer;
impl Serialize for Bearer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str("bearer")
}
}
impl<'de> Deserialize<'de> for Bearer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
match &*string {
"bearer" => Ok(Self),
_ => Err(D::Error::invalid_value(
Unexpected::Other(&string),
&"the string \"bearer\"",
)),
}
}
}