@@ -61,7 +61,13 @@ mod private
6161   /// A type alias for `std::result::Result` with the error type `Error`. 
6262   pub  type  Result <  R  >  = std:: result:: Result <  R ,  Error  > ; 
6363
64-   /// # Secret 
64+   pub  trait  Secret  
65+   { 
66+     #[  allow(  async_fn_in_trait )  ]  
67+     async  fn  get_token (  & self  )  -> gcore:: error:: Result <  String  > ; 
68+   } 
69+ 
70+   /// # ApplicationSecret 
6571   /// 
6672   /// A struct that represents configuration secrets loaded from a `.env` file. 
6773   /// 
@@ -95,15 +101,15 @@ mod private
95101   /// ``` 
96102   #[  derive(  Debug  )  ]  
97103  #[  allow(  non_snake_case )  ]  
98-   pub  struct  Secret 
104+   pub  struct  ApplicationSecret 
99105  { 
100106    pub  CLIENT_SECRET  :  String , 
101107    pub  CLIENT_ID :  String , 
102108    pub  AUTH_URI  :  String , 
103109    pub  TOKEN_URI  :  String , 
104110  } 
105111
106-   impl  Secret 
112+   impl  ApplicationSecret 
107113  { 
108114    #[  allow(  non_snake_case )  ]  
109115    pub  fn  load ( )  -> Result <  Self  > 
@@ -129,7 +135,127 @@ mod private
129135      Ok (  config ) 
130136    } 
131137
132-     pub  fn  read ( )  -> Secret 
138+     pub  fn  read ( )  -> ApplicationSecret 
139+     { 
140+       Self :: load ( ) . unwrap_or_else (  | err |
141+       { 
142+         let  example = include_str ! ( "../../.secret/readme.md" ) ; 
143+         let  explanation = format ! 
144+         ( 
145+                   r#" = Lack of secrets 
146+ 
147+ Failed to load secret or some its parameters. 
148+ {err} 
149+ 
150+  = Fix 
151+ 
152+ Add missing secret to .env file in .secret directory. Example: MISSING_SECRET=YOUR_MISSING_SECRET 
153+ 
154+  = More information 
155+ 
156+ {example} 
157+ "# 
158+         ) ; 
159+         panic ! (  "{}" ,  explanation ) ; 
160+        } ) 
161+     } 
162+ 
163+     pub  fn  get ( )  -> & ' static  ApplicationSecret 
164+     { 
165+       static  INSTANCE  :  OnceLock <  ApplicationSecret  >  = OnceLock :: new ( ) ; 
166+       INSTANCE . get_or_init (  || Self :: read ( )  ) 
167+     } 
168+ 
169+   } 
170+ 
171+   impl  Secret  for  ApplicationSecret  
172+   { 
173+     async  fn  get_token (  & self  )  -> gcore:: error:: Result <  String  >  
174+     { 
175+       let  secret:  yup_oauth2:: ApplicationSecret  = yup_oauth2:: ApplicationSecret 
176+       { 
177+         client_id  :  self . CLIENT_ID . clone ( ) , 
178+         auth_uri  :  self . AUTH_URI . clone ( ) , 
179+         token_uri  :  self . TOKEN_URI . clone ( ) , 
180+         client_secret  :  self . CLIENT_SECRET . clone ( ) , 
181+         .. Default :: default ( ) 
182+       } ; 
183+ 
184+       let  authenticator  = yup_oauth2:: InstalledFlowAuthenticator :: builder ( 
185+         secret, 
186+         yup_oauth2:: InstalledFlowReturnMethod :: HTTPRedirect , 
187+       ) 
188+       . build ( ) 
189+       . await 
190+       . map_err (  | err | gcore:: error:: Error :: AuthError (  err. to_string ( )  )  ) ?; 
191+ 
192+       let  scopes = vec ! 
193+       [  
194+         "https://www.googleapis.com/auth/spreadsheets" , 
195+         "https://www.googleapis.com/auth/spreadsheets.readonly"  
196+       ] ; 
197+ 
198+       let  access_token = authenticator
199+       . token (  & scopes ) 
200+       . await 
201+       . map_err (  | err | gcore:: error:: Error :: AuthError (  err. to_string ( )  )  ) ?; 
202+ 
203+       let  token = access_token. token ( ) . unwrap ( ) ; 
204+       Ok (  token. to_string ( )  ) 
205+     } 
206+   } 
207+ 
208+ 
209+   /// # ServiceAccountSecret 
210+    #[  derive(  Debug  )  ]  
211+   #[  allow(  non_snake_case )  ]  
212+   pub  struct  ServiceAccountSecret 
213+   { 
214+     pub  KEY_TYPE  :  String , 
215+     pub  PROJECT_ID :  String , 
216+     pub  PRIVATE_KEY_ID  :  String , 
217+     pub  PRIVATE_KEY  :  String , 
218+     pub  CLIENT_EMAIL  :  String , 
219+     pub  CLIENT_ID  :  String , 
220+     pub  AUTH_URI  :  String , 
221+     pub  TOKEN_URI  :  String , 
222+     pub  AUTH_PROVIDER_X509_CERT_URL  :  String , 
223+     pub  CLIENT_X509_CERT_URL  :  String , 
224+   } 
225+ 
226+   impl  ServiceAccountSecret 
227+   { 
228+     #[  allow(  non_snake_case )  ]  
229+     pub  fn  load ( )  -> Result <  Self  > 
230+     { 
231+       let  path = "./.secret/.env" ; 
232+ 
233+       let  r = dotenv:: from_path (  path ) ; 
234+       if  let  Err (  ref  err )  = r
235+       { 
236+         if  !matches ! (  err,  dotenv:: Error :: Io ( _)  ) 
237+         { 
238+           return  Err (  r. expect_err (  & format ! (  "Failed to load {path}"  )  ) . into ( )  ) ; 
239+         } 
240+       } 
241+ 
242+       let  config = Self 
243+       { 
244+         KEY_TYPE  :  var (  "GOOGLE_KEY_TYPE" ,  None  ) ?, 
245+         PROJECT_ID  :  var (  "GOOGLE_PROJECT_ID" ,  None  ) ?, 
246+         PRIVATE_KEY_ID  :  var  (  "GOOGLE_PRIVATE_KEY_ID" ,  None  ) ?, 
247+         PRIVATE_KEY  :  var  (  "GOOGLE_PRIVATE_KEY" ,  None  ) ?, 
248+         CLIENT_EMAIL  :  var (  "GOOGLE_CLIENT_EMAIL" ,  None  ) ?, 
249+         CLIENT_ID  :  var (  "GOOGLE_CLIENT_ID" ,  None  ) ?, 
250+         AUTH_URI  :  var (  "GOOGLE_AUTH_URI" ,  None  ) ?, 
251+         TOKEN_URI  :  var (  "GOOGLE_TOKEN_URI" ,  None  ) ?, 
252+         AUTH_PROVIDER_X509_CERT_URL  :  var (  "GOOGLE_AUTH_PROVIDER_X509_CERT_URL" ,  None  ) ?, 
253+         CLIENT_X509_CERT_URL  :  var (  "GOOGLE_CLIENT_X509_CERT_URL" ,  None  ) ?, 
254+       } ; 
255+       Ok (  config ) 
256+     } 
257+ 
258+     pub  fn  read ( )  -> ServiceAccountSecret 
133259    { 
134260      Self :: load ( ) . unwrap_or_else (  | err |
135261      { 
@@ -154,14 +280,47 @@ Add missing secret to .env file in .secret directory. Example: MISSING_SECRET=YO
154280       } ) 
155281    } 
156282
157-     pub  fn  get ( )  -> & ' static  Secret 
283+     pub  fn  get ( )  -> & ' static  ServiceAccountSecret 
158284    { 
159-       static  INSTANCE  :  OnceLock <  Secret  >  = OnceLock :: new ( ) ; 
285+       static  INSTANCE  :  OnceLock <  ServiceAccountSecret  >  = OnceLock :: new ( ) ; 
160286      INSTANCE . get_or_init (  || Self :: read ( )  ) 
161287    } 
162288
163289  } 
164290
291+   impl  Secret  for  ServiceAccountSecret 
292+   { 
293+     async  fn  get_token (  & self  )  -> gcore:: error:: Result <  String  >  
294+     { 
295+       let  key = yup_oauth2:: ServiceAccountKey 
296+       {  
297+         key_type  :  Some (  self . KEY_TYPE . clone ( )  ) ,  
298+         project_id  :  Some (  self . PROJECT_ID . clone ( )  ) ,  
299+         private_key_id  :  Some (  self . PRIVATE_KEY_ID . clone ( )  ) ,  
300+         private_key  :  self . PRIVATE_KEY . clone ( ) ,  
301+         client_email  :  self . CLIENT_EMAIL . clone ( ) ,  
302+         client_id  :  Some (  self . CLIENT_ID . clone ( )  ) ,  
303+         auth_uri  :  Some (  self . AUTH_URI . clone ( )  ) ,  
304+         token_uri  :  self . TOKEN_URI . clone ( ) ,  
305+         auth_provider_x509_cert_url  :  Some (  self . AUTH_PROVIDER_X509_CERT_URL . clone ( )  ) ,  
306+         client_x509_cert_url  :  Some (  self . CLIENT_X509_CERT_URL . clone ( )  ) ,  
307+       } ; 
308+ 
309+       let  auth = yup_oauth2:: ServiceAccountAuthenticator :: builder (  key ) 
310+       . build ( ) 
311+       . await 
312+       . map_err (  | err | gcore:: error:: Error :: AuthError (  err. to_string ( )  )  ) ?; 
313+ 
314+       let  scopes = & [  "https://www.googleapis.com/auth/spreadsheets"  ] ; 
315+ 
316+       let  token = auth. token (  scopes ) . await . map_err (  | err | gcore:: error:: Error :: AuthError (  err. to_string ( )  )  ) ?; 
317+ 
318+       let  token = token. token ( ) . unwrap ( ) ; 
319+ 
320+       Ok (  token. to_string ( )  ) 
321+     } 
322+   } 
323+ 
165324  /// # `var` 
166325   /// 
167326   /// Retrieves the value of an environment variable, or returns a default value if the variable is not set. 
@@ -235,5 +394,7 @@ crate::mod_interface!
235394  orphan use 
236395  { 
237396    Secret , 
397+     ApplicationSecret , 
398+     ServiceAccountSecret , 
238399  } ; 
239400} 
0 commit comments