blocklist 0.2.0

The project is based on blocklistproject. It provides perfect hash map/set structures for fast lookup of blocklisted items.
use fst::SetBuilder;
use std::fs::{self, File};
use std::path::Path;
use std::{env, io};

pub type GenericError = Box<dyn std::error::Error + Send + Sync>;

async fn fetch_list(name: &str) -> Result<Vec<String>, GenericError> {
    let response: String = reqwest::Client::new()
        .get(format!(
            "https://blocklistproject.github.io/Lists/alt-version/{name}-nl.txt"
        ))
        .send()
        .await?
        .text()
        .await?;

    let mut list = response
        .lines()
        .filter(|line| !line.starts_with('#'))
        .map(|line| line.to_string())
        .collect::<Vec<String>>();

    list.sort();

    build_set_file(name, list.clone())?;

    Ok(list)
}

fn build_set_file(name: &str, list: Vec<String>) -> Result<(), GenericError> {
    let out_dir = env::var("OUT_DIR").unwrap();
    let path = Path::new(&out_dir).join(format!("blocklist-{name}.fst"));
    let _ = fs::remove_file(&path);
    let file = File::create(path)?;
    let wtr = io::BufWriter::new(file);

    let mut set = SetBuilder::new(wtr)?;

    for item in list.iter() {
        let _ = set.insert(item)?;
    }

    let _ = set.finish()?;

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), GenericError> {
    let blocked = vec![
        "abuse",
        "drugs",
        "fraud",
        "gambling",
        "malware",
        "phishing",
        "piracy",
        "porn",
        "ransomware",
        "redirect",
        "scam",
        "torrent",
        "tracking",
        "ads",
    ];

    let mut global_list = Vec::new();

    for item in blocked.iter() {
        global_list.append(&mut fetch_list(item).await?);
    }

    global_list.sort();
    global_list.dedup();

    build_set_file("all", global_list)?;

    Ok(())
}