1use crate::{
4 custom_serde::{duration_second, space_separated_scopes},
5 ModelResult,
6};
7
8use std::{
9 collections::{HashMap, HashSet},
10 fs,
11 io::{Read, Write},
12 path::Path,
13};
14
15use chrono::{DateTime, Duration, TimeDelta, Utc};
16use serde::{Deserialize, Serialize};
17
18#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
22pub struct Token {
23 pub access_token: String,
25 #[serde(with = "duration_second")]
27 pub expires_in: Duration,
28 pub expires_at: Option<DateTime<Utc>>,
31 pub refresh_token: Option<String>,
34 #[serde(default, with = "space_separated_scopes", rename = "scope")]
42 pub scopes: HashSet<String>,
43}
44
45impl Default for Token {
46 fn default() -> Self {
47 Self {
48 access_token: String::new(),
49 expires_in: Duration::try_seconds(0).unwrap(),
50 expires_at: Some(Utc::now()),
51 refresh_token: None,
52 scopes: HashSet::new(),
53 }
54 }
55}
56
57impl Token {
58 pub fn from_cache<T: AsRef<Path>>(path: T) -> ModelResult<Self> {
60 let mut file = fs::File::open(path)?;
61 let mut tok_str = String::new();
62 file.read_to_string(&mut tok_str)?;
63 let tok = serde_json::from_str(&tok_str)?;
64
65 Ok(tok)
66 }
67
68 pub fn write_cache<T: AsRef<Path>>(&self, path: T) -> ModelResult<()> {
70 let token_info = serde_json::to_string(&self)?;
71
72 let mut file = fs::OpenOptions::new()
73 .write(true)
74 .create(true)
75 .truncate(true)
76 .open(path)?;
77 file.set_len(0)?;
78 file.write_all(token_info.as_bytes())?;
79
80 Ok(())
81 }
82
83 #[must_use]
86 pub fn is_expired(&self) -> bool {
87 self.expires_at
88 .is_none_or(|expiration| Utc::now() + TimeDelta::try_seconds(10).unwrap() >= expiration)
89 }
90
91 #[must_use]
93 pub fn auth_headers(&self) -> HashMap<String, String> {
94 let auth = "authorization".to_owned();
95 let value = format!("Bearer {}", self.access_token);
96
97 let mut headers = HashMap::new();
98 headers.insert(auth, value);
99 headers
100 }
101}
102
103#[cfg(test)]
104mod test {
105 use std::collections::HashSet;
106
107 use crate::Token;
108 use serde_json::json;
109
110 #[test]
111 fn test_bearer_auth() {
112 let tok = Token {
113 access_token: "access_token".to_string(),
114 ..Default::default()
115 };
116
117 let headers = tok.auth_headers();
118 assert_eq!(headers.len(), 1);
119 assert_eq!(
120 headers.get("authorization"),
121 Some(&"Bearer access_token".to_owned())
122 );
123 }
124
125 #[test]
126 fn test_token_deserialize() {
127 let mut scopes = HashSet::<String>::new();
128 scopes.insert("user-read-email".to_owned());
129 let tok = Token {
130 access_token: "access_token".to_string(),
131 scopes,
132 ..Default::default()
133 };
134 let value = json!(tok);
135 let token = serde_json::from_value::<Token>(value);
136 assert!(token.is_ok());
137 assert_eq!(token.unwrap().scopes, tok.scopes);
138 }
139}