@@ -23,16 +23,10 @@ use reqwest::Client;
23
23
use rustls:: { Certificate , PrivateKey } ;
24
24
use serde:: Deserialize ;
25
25
use std:: {
26
- env:: var,
27
- fs:: { self , File } ,
28
- io:: BufReader ,
29
- iter,
30
- path:: { Path , PathBuf } ,
31
- sync:: {
26
+ env:: var, fs:: { self , File } , io:: BufReader , iter, path:: { Path , PathBuf } , sync:: {
32
27
atomic:: { AtomicU64 , Ordering } ,
33
28
Arc
34
- } ,
35
- time:: Duration
29
+ } , time:: Duration
36
30
} ;
37
31
use time:: OffsetDateTime ;
38
32
use tokio:: {
@@ -244,10 +238,12 @@ fn load_cert_and_key(
244
238
Ok ( ( certificates, key) )
245
239
}
246
240
247
- /// load a text file from url and cache it.
248
- /// If restore_from_cache is true only the cache is used.
249
- /// Return None if an Err has occure.
250
- async fn get_file ( url : & Url , restore_from_cache : bool ) -> ( Option < String > , String ) {
241
+ /// Load a text file from url and cache it.
242
+ /// If restore_from_cache is true, only the cache is used.
243
+ /// The first return value is the file content.
244
+ /// It will be None if an error has occured.
245
+ /// The second value is a combined error message.
246
+ async fn get_file ( url : & Url , restore_from_cache : bool , cache_file : bool ) -> ( Option < String > , String ) {
251
247
if url. scheme ( ) == "file" {
252
248
let path = url. path ( ) ;
253
249
info ! ( "load file {path:?}" ) ;
@@ -281,12 +277,13 @@ async fn get_file(url: &Url, restore_from_cache: bool) -> (Option<String>, Strin
281
277
. error_for_status ( ) ?
282
278
. text ( )
283
279
. await ?;
280
+ if cache_file {
284
281
if let Err ( err) = write ( & path, & resp)
285
282
. await
286
283
. with_context ( || format ! ( "failed to save to {path:?}" ) )
287
284
{
288
285
error ! ( "{err:?}" ) ;
289
- }
286
+ } }
290
287
Ok ( resp)
291
288
}
292
289
. await ;
@@ -493,6 +490,19 @@ fn main() {
493
490
Lazy :: force ( & CONFIG_PATH ) ;
494
491
Lazy :: force ( & LIST_DIR ) ;
495
492
493
+ let config = load_config ( ) ;
494
+
495
+ if std:: env:: args ( ) . any ( |x| x == "--validate" ) {
496
+ if !async_validate_config ( config) {
497
+ error ! ( "Config validation failed!" ) ;
498
+ std:: process:: exit ( 1 ) ;
499
+ }
500
+ } else {
501
+ async_main ( config) ;
502
+ }
503
+ }
504
+
505
+ fn load_config ( ) -> Config {
496
506
info ! ( "load config from {:?}" , & * CONFIG_PATH ) ;
497
507
let config = fs:: read ( & * CONFIG_PATH )
498
508
. with_context ( || format ! ( "Failed to read {:?}" , CONFIG_PATH . as_path( ) ) )
@@ -501,7 +511,42 @@ fn main() {
501
511
. with_context ( || "Failed to deserialize config" )
502
512
. unwrap_or_else ( |err| panic ! ( "{err:?}" ) ) ;
503
513
debug ! ( "{:#?}" , config) ;
504
- async_main ( config) ;
514
+
515
+ config
516
+ }
517
+
518
+ #[ tokio:: main]
519
+ async fn async_validate_config ( config : Config ) -> bool {
520
+ let mut validated = true ;
521
+ //Allow List
522
+ for list in config. blocklist . allow_list {
523
+ let ( file_content, error_message) = get_file ( & list, false , false ) . await ;
524
+ if let Some ( content) = file_content {
525
+ if let Err ( err) = parser:: Blocklist :: parse ( list. path ( ) , & content) {
526
+ error ! ( "{}" , err. msg( ) ) ;
527
+ validated = false ;
528
+ }
529
+ } else {
530
+ error ! ( "{error_message}" ) ;
531
+ validated = false ;
532
+ }
533
+ }
534
+
535
+ //Block List
536
+ for list in config. blocklist . lists {
537
+ let ( file_content, error_message) = get_file ( & list, false , false ) . await ;
538
+ if let Some ( content) = file_content {
539
+ if let Err ( err) = parser:: Blocklist :: parse ( list. path ( ) , & content) {
540
+ error ! ( "{}" , err. msg( ) ) ;
541
+ validated = false ;
542
+ }
543
+ } else {
544
+ error ! ( "{error_message}" ) ;
545
+ validated = false ;
546
+ }
547
+ }
548
+
549
+ validated
505
550
}
506
551
507
552
#[ cfg( test) ]
0 commit comments