| 
						
						
						
					 | 
				
				 | 
				
					@ -1,18 +1,50 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::collections::{HashMap, HashSet}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use reqwest::Client; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::env::set_var; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::time::Duration; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::fs::File; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::io::Read; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use std::path::PathBuf; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					use log::{debug, info, warn}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					const TIMEOUT: u64 = 60; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					async fn asset_fetch(url: &str) -> Result<String, String> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					const ASSETS_DIR: &str = "/cleardns/assets/"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					/// Cut text line by line and remove invisible characters on both sides.
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					fn asset_tidy(data: &str) -> Vec<String> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    data.lines() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .map(|x| String::from(x.trim())) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .filter(|x| !x.is_empty()) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .collect() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					/// Remove duplicate elements from an array.
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					fn remove_dup(data: &Vec<String>) -> Vec<String> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let mut result: Vec<String> = vec![]; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let mut tmp: HashSet<String> = HashSet::new(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    for val in data { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if tmp.insert(val.clone()) { // value already exist
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            result.push(val.clone()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    result | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					/// Download the specified text file and organize it into a String array.
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					async fn http_fetch(url: &str, timeout: u64) -> Result<Vec<String>, String> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let client = Client::builder() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .timeout(Duration::from_secs(TIMEOUT)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .timeout(Duration::from_secs(timeout)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .build().unwrap(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    info!("Start downloading `{}`", url); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    match client.get(url).send().await { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        Ok(response) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            match response.text().await { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                Ok(text) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    Ok(text) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    info!("Asset `{}` download success", url); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    Ok(asset_tidy(&text)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                Err(err) => Err(format!("http content error: {}", err)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            } | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -21,17 +53,73 @@ async fn asset_fetch(url: &str) -> Result<String, String> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					/// Read the specified text file and organize it into a String array.
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					async fn local_fetch(path: &str) -> Result<Vec<String>, String> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let mut path = String::from(path); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if !path.starts_with("/") { // relative path
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        let file_path = PathBuf::from(ASSETS_DIR).join(path); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        path = String::from(file_path.to_str().unwrap()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    match File::open(&path) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        Ok(mut file) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            let mut text = String::new(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            if let Err(err) = file.read_to_string(&mut text) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                return Err(format!("File `{}` read failed: {}", path, err)); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            info!("Asset `{}` read success", path); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Ok(asset_tidy(&text)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        Err(err) => Err(format!("File `{}` open failed: {}", path, err)), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					/// Get multiple resource data and merge them.
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					async fn asset_fetch(name: &str, sources: Vec<&str>) -> Vec<String> { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let is_remote = |src: &str| { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        src.starts_with("http://") || src.starts_with("https://") | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let mut contents: Vec<Vec<String>> = vec![]; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    for source in sources { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        contents.push(match if is_remote(source) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            http_fetch(source.trim(), TIMEOUT).await | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } else { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            local_fetch(source.trim()).await | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Ok(data) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                debug!("Asset source `{}` fetch success with {} items", source.trim(), data.len()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                data | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Err(err) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                warn!("Asset source `{}` fetch failed: {}", source.trim(), err); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                break; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let contents = remove_dup(&contents | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .into_iter() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .flatten() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .collect::<Vec<String>>()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    info!("Asset `{}` fetch complete with {} items", name, contents.len()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    contents | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					async fn demo() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    println!("demo function start"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    match asset_fetch("https://res.343.re/Share/cleardns/gfwlist.txt").await { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // match asset_fetch("https://res.343.re/Share/cleardns/gfwlist.txt", TIMEOUT).await {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     Ok(data) => {
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //         println!("{:?}", data);
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     },
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     Err(err) => println!("error -> {}", err)
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // }
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    match local_fetch("../../tmp/gfwlist.txt").await { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        Ok(data) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            // println!("{}", data);
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            // println!("{:?}", data);
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        Err(err) => println!("error -> {}", err) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        Err(err) => println!("error -> `{}`", err) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    println!("demo function exit"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -40,11 +128,32 @@ async fn demo() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#[tokio::main] | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					async fn main() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // set_var("RUST_LOG", "debug");
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    set_var("RUST_LOG", "trace"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    env_logger::init(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    demo().await; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    let d = vec![ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "https://res.343.re/Share/cleardns/gfwlist.txt", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "/tmp/gfwlist.txt", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ]; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    asset_fetch("test", d).await; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // let demo = vec![
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     "baidu.com",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     "ip.343.re",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     "qq.com",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     "google.com",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     "res.343.re",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     "ip.343.re",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     "343.re",
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // ];
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // let demo = demo.into_iter()
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     .map(|x| String::from(x))
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    //     .collect();
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // remove_dup(&demo);
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // let data: &str = "dnomd343\n linjucong\n\nfuck\t\n7700\n";
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // println!("{}", data);
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // tidy(data);
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    println!("end demo"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
				 | 
				
					
  |