1use chrono::{DateTime, Utc};
2use serde::{
3 de::{Error, Unexpected},
4 Deserialize, Deserializer, Serialize, Serializer,
5};
6
7#[derive(Debug, Serialize)]
8pub(crate) struct RedditAuth {
9 pub(crate) access_token: String,
10 pub(crate) token_type: Bearer,
11 pub(crate) expires_at: DateTime<Utc>,
12 pub(crate) scope: String,
13}
14
15impl<'de> Deserialize<'de> for RedditAuth {
16 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
17 where
18 D: Deserializer<'de>,
19 {
20 #[derive(Debug, Serialize, Deserialize)]
21 #[serde(untagged)]
22 enum Proxy {
23 FromReddit {
26 access_token: String,
27 token_type: Bearer,
28 expires_in: u32,
29 scope: String,
30 },
31 FromFile {
33 access_token: String,
34 token_type: Bearer,
35 expires_at: DateTime<Utc>,
36 scope: String,
37 },
38 }
39
40 let proxy = Proxy::deserialize(deserializer)?;
41
42 match proxy {
43 Proxy::FromReddit {
44 access_token,
45 token_type,
46 expires_in,
47 scope,
48 } => Ok(Self {
49 access_token,
50 scope,
51 expires_at: Utc::now()
52 .checked_add_signed(chrono::Duration::seconds(i64::from(expires_in)))
53 .expect("datetime overflowed for auth; this should be unreachable."),
54 token_type,
55 }),
56 Proxy::FromFile {
57 access_token,
58 token_type,
59 expires_at,
60 scope,
61 } => Ok(Self {
62 access_token,
63 token_type,
64 expires_at,
65 scope,
66 }),
67 }
68 }
69}
70
71#[derive(Debug)]
72pub(crate) struct Bearer;
73
74impl Serialize for Bearer {
75 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
76 where
77 S: Serializer,
78 {
79 serializer.serialize_str("bearer")
80 }
81}
82
83impl<'de> Deserialize<'de> for Bearer {
84 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
85 where
86 D: Deserializer<'de>,
87 {
88 let string = String::deserialize(deserializer)?;
89 match &*string {
90 "bearer" => Ok(Self),
91 _ => Err(D::Error::invalid_value(
92 Unexpected::Other(&string),
93 &"the string \"bearer\"",
94 )),
95 }
96 }
97}