@@ -2,32 +2,116 @@ use crate::constants::{
22 ENV_ACCESS_TOKEN , ENV_CLIENT_ID , ENV_CLIENT_SECRET , ENV_REDIRECT_URI , ENV_REFRESH_TOKEN ,
33 TOML_ACCESS_TOKEN , TOML_CLIENT_ID , TOML_CLIENT_SECRET , TOML_REDIRECT_URI , TOML_REFRESH_TOKEN ,
44} ;
5+ use puddle:: auth:: oauth;
56use std:: env;
67use std:: fs;
78use std:: io;
89use std:: path:: { Path , PathBuf } ;
910use toml:: Value ;
1011
11- #[ derive( Debug ) ]
12- pub struct Config {
12+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
13+ pub ( crate ) struct ConfigValues {
1314 pub client_id : String ,
1415 pub client_secret : String ,
1516 pub redirect_uri : String ,
1617 pub access_token : String ,
1718 pub refresh_token : String ,
1819}
1920
21+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
22+ pub ( crate ) enum Config {
23+ Env ( ConfigValues ) ,
24+ File { path : PathBuf , values : ConfigValues } ,
25+ }
26+
2027impl Config {
21- pub fn load ( ) -> io:: Result < Self > {
22- if let Some ( config) = Self :: from_env ( ) ? {
23- return Ok ( config) ;
28+ pub ( crate ) fn load ( ) -> io:: Result < Self > {
29+ if let Some ( values) = ConfigValues :: from_env ( ) ? {
30+ return Ok ( Self :: Env ( values) ) ;
31+ }
32+
33+ let path = global_config_path ( ) ?;
34+ let values = ConfigValues :: from_path ( & path) ?;
35+
36+ Ok ( Self :: File { path, values } )
37+ }
38+
39+ pub ( crate ) fn values ( & self ) -> & ConfigValues {
40+ match self {
41+ Self :: Env ( values) => values,
42+ Self :: File { values, .. } => values,
43+ }
44+ }
45+
46+ pub ( crate ) fn with_refreshed_tokens (
47+ & self ,
48+ access_token : String ,
49+ refresh_token : Option < String > ,
50+ ) -> Self {
51+ match self {
52+ Self :: Env ( values) => {
53+ Self :: Env ( values. with_refreshed_tokens ( access_token, refresh_token) )
54+ }
55+ Self :: File { path, values } => Self :: File {
56+ path : path. clone ( ) ,
57+ values : values. with_refreshed_tokens ( access_token, refresh_token) ,
58+ } ,
59+ }
60+ }
61+
62+ pub ( crate ) fn persist ( & self ) -> io:: Result < ( ) > {
63+ if let Self :: File { path, values } = self {
64+ let mut table = toml:: Table :: new ( ) ;
65+
66+ table. insert (
67+ TOML_CLIENT_ID . to_string ( ) ,
68+ Value :: String ( values. client_id . clone ( ) ) ,
69+ ) ;
70+ table. insert (
71+ TOML_CLIENT_SECRET . to_string ( ) ,
72+ Value :: String ( values. client_secret . clone ( ) ) ,
73+ ) ;
74+ table. insert (
75+ TOML_REDIRECT_URI . to_string ( ) ,
76+ Value :: String ( values. redirect_uri . clone ( ) ) ,
77+ ) ;
78+ table. insert (
79+ TOML_ACCESS_TOKEN . to_string ( ) ,
80+ Value :: String ( values. access_token . clone ( ) ) ,
81+ ) ;
82+ table. insert (
83+ TOML_REFRESH_TOKEN . to_string ( ) ,
84+ Value :: String ( values. refresh_token . clone ( ) ) ,
85+ ) ;
86+
87+ let content = toml:: to_string_pretty ( & table) . map_err ( |e| {
88+ io:: Error :: other ( format ! ( "failed to serialize global config toml: {e}" ) )
89+ } ) ?;
90+ fs:: write ( path, format ! ( "{content}\n " ) ) ?;
2491 }
2592
26- let config_path = global_config_path ( ) ?;
27- Self :: from_path ( & config_path)
93+ Ok ( ( ) )
94+ }
95+
96+ pub ( crate ) async fn refresh_access_token ( self ) -> Result < Self , Box < dyn std:: error:: Error > > {
97+ let values = self . values ( ) ;
98+ let token = oauth:: TokenRequestBuilder :: refresh (
99+ & values. client_id ,
100+ & values. client_secret ,
101+ & values. refresh_token ,
102+ )
103+ . send ( )
104+ . await ?;
105+
106+ let config = self . with_refreshed_tokens ( token. access_token , token. refresh_token ) ;
107+ config. persist ( ) ?;
108+
109+ Ok ( config)
28110 }
111+ }
29112
30- pub fn from_path ( path : impl AsRef < Path > ) -> io:: Result < Self > {
113+ impl ConfigValues {
114+ pub ( crate ) fn from_path ( path : impl AsRef < Path > ) -> io:: Result < Self > {
31115 let path = path. as_ref ( ) ;
32116 let content = fs:: read_to_string ( path) . map_err ( |err| {
33117 io:: Error :: new (
@@ -51,10 +135,10 @@ impl Config {
51135 } )
52136 }
53137
54- pub fn write_to_global_path ( & self ) -> io:: Result < ( ) > {
138+ pub ( crate ) fn write_to_global_path ( & self ) -> io:: Result < ( ) > {
55139 let path = global_config_path ( ) ?;
56-
57140 let mut table = toml:: Table :: new ( ) ;
141+
58142 table. insert (
59143 TOML_CLIENT_ID . to_string ( ) ,
60144 Value :: String ( self . client_id . clone ( ) ) ,
@@ -84,6 +168,20 @@ impl Config {
84168 Ok ( ( ) )
85169 }
86170
171+ pub ( crate ) fn with_refreshed_tokens (
172+ & self ,
173+ access_token : String ,
174+ refresh_token : Option < String > ,
175+ ) -> Self {
176+ Self {
177+ client_id : self . client_id . clone ( ) ,
178+ client_secret : self . client_secret . clone ( ) ,
179+ redirect_uri : self . redirect_uri . clone ( ) ,
180+ access_token,
181+ refresh_token : refresh_token. unwrap_or_else ( || self . refresh_token . clone ( ) ) ,
182+ }
183+ }
184+
87185 fn from_env ( ) -> io:: Result < Option < Self > > {
88186 let entries = [
89187 ENV_CLIENT_ID ,
0 commit comments