11use std:: fmt;
2+ use std:: sync:: Arc ;
23
34use serde:: Deserializer ;
45use serde:: { Deserialize , Serialize } ;
56use time:: { Duration , OffsetDateTime } ;
67
78/// Represents an access token. All access tokens are Bearer tokens.
89///
9- /// Tokens cannot be cached.
10+ /// Tokens should not be cached, the [`AuthenticationManager`] handles the correct caching
11+ /// already. Tokens are cheap to clone.
1012///
1113/// The token does not implement [`Display`] to avoid accidentally printing the token in log
1214/// files, likewise [`Debug`] does not expose the token value itself which is only available
1315/// using the [Token::`as_str`] method.
1416///
17+ /// [`AuthenticationManager`]: crate::AuthenticationManager
1518/// [`Display`]: fmt::Display
1619/// [`Debug`]: fmt::Debug
1720#[ derive( Clone , PartialEq , Eq , PartialOrd , Ord , Hash , Deserialize , Serialize ) ]
1821pub struct Token {
19- access_token : String ,
20- #[ serde(
21- default ,
22- deserialize_with = "deserialize_time" ,
23- rename( deserialize = "expires_in" )
24- ) ]
25- expires_at : Option < OffsetDateTime > ,
22+ #[ serde( flatten) ]
23+ inner : Arc < InnerToken > ,
2624}
2725
2826impl fmt:: Debug for Token {
2927 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
3028 f. debug_struct ( "Token" )
3129 . field ( "access_token" , & "****" )
32- . field ( "expires_at" , & self . expires_at )
30+ . field ( "expires_at" , & self . inner . expires_at )
3331 . finish ( )
3432 }
3533}
3634
35+ #[ derive( Clone , PartialEq , Eq , PartialOrd , Ord , Hash , Deserialize , Serialize ) ]
36+ struct InnerToken {
37+ access_token : String ,
38+ #[ serde(
39+ default ,
40+ deserialize_with = "deserialize_time" ,
41+ rename( deserialize = "expires_in" )
42+ ) ]
43+ expires_at : Option < OffsetDateTime > ,
44+ }
45+
3746impl Token {
3847 /// Define if the token has has_expired
3948 ///
4049 /// This takes an additional 30s margin to ensure the token can still be reasonably used
4150 /// instead of expiring right after having checked.
4251 pub fn has_expired ( & self ) -> bool {
43- self . expires_at
52+ self . inner
53+ . expires_at
4454 . map ( |expiration_time| {
4555 expiration_time - Duration :: seconds ( 30 ) <= OffsetDateTime :: now_utc ( )
4656 } )
@@ -49,12 +59,12 @@ impl Token {
4959
5060 /// Get str representation of the token.
5161 pub fn as_str ( & self ) -> & str {
52- & self . access_token
62+ & self . inner . access_token
5363 }
5464
5565 /// Get expiry of token, if available
5666 pub fn expires_at ( & self ) -> Option < OffsetDateTime > {
57- self . expires_at
67+ self . inner . expires_at
5868 }
5969}
6070
7080
7181pub ( crate ) type HyperClient =
7282 hyper:: Client < hyper_rustls:: HttpsConnector < hyper:: client:: HttpConnector > > ;
83+
84+ #[ cfg( test) ]
85+ mod tests {
86+ use super :: * ;
87+
88+ #[ test]
89+ fn test_serialise ( ) {
90+ let token = Token {
91+ inner : Arc :: new ( InnerToken {
92+ access_token : "abc123" . to_string ( ) ,
93+ expires_at : Some ( OffsetDateTime :: from_unix_timestamp ( 123 ) . unwrap ( ) ) ,
94+ } ) ,
95+ } ;
96+ let s = serde_json:: to_string ( & token) . unwrap ( ) ;
97+
98+ assert_eq ! (
99+ s,
100+ r#"{"access_token":"abc123","expires_at":[1970,1,0,2,3,0,0,0,0]}"#
101+ ) ;
102+ }
103+
104+ #[ test]
105+ fn test_deserialise_with_time ( ) {
106+ let s = r#"{"access_token":"abc123","expires_in":100}"# ;
107+ let token: Token = serde_json:: from_str ( s) . unwrap ( ) ;
108+ let expires = OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 100 ) ;
109+
110+ assert_eq ! ( token. as_str( ) , "abc123" ) ;
111+
112+ // Testing time is always racy, give it 1s leeway.
113+ let expires_at = token. expires_at ( ) . unwrap ( ) ;
114+ assert ! ( expires_at < expires + Duration :: seconds( 1 ) ) ;
115+ assert ! ( expires_at > expires - Duration :: seconds( 1 ) ) ;
116+ }
117+
118+ #[ test]
119+ fn test_deserialise_no_time ( ) {
120+ let s = r#"{"access_token":"abc123"}"# ;
121+ let token: Token = serde_json:: from_str ( s) . unwrap ( ) ;
122+
123+ assert_eq ! ( token. as_str( ) , "abc123" ) ;
124+ assert ! ( token. expires_at( ) . is_none( ) ) ;
125+ }
126+ }
0 commit comments