ipcap-codegen 0.1.0

ipcap codegen crate.
Documentation
use crate::names::name_to_enum_name;
use std::fs;

mod names;

const NAMES_DATA: &str = include_str!("../countries-names.txt");
const CODES_2_DATA: &str = include_str!("../countries-two.txt");
const CODES_3_DATA: &str = include_str!("../countries-three.txt");
const COUNTRIES_TO_CONTINENTS: &str = include_str!("../countries-to-continents.txt");
const DMA: &str = include_str!("../dma.txt");

trait PushMut<T> {
    fn add(self, item: T) -> Self;
}

impl<T> PushMut<T> for Vec<T> {
    fn add(self, item: T) -> Self {
        let mut new = self;
        new.push(item);
        new
    }
}

macro_rules! codes {
    ($data: expr, $enum_names: expr) => {
        &$data
            .split('\n')
            .enumerate()
            .map(|(i, code)| format!("Country::{} => \"{}\"", $enum_names[i], code))
            .collect::<Vec<String>>()
            .join(",\n")
    };
}

macro_rules! codes_reverse {
    ($data: expr, $enum_names: expr) => {{
        $data
            .split('\n')
            .enumerate()
            .map(|(i, code)| format!("\"{}\" => Some(Country::{})", code, $enum_names[i]))
            .collect::<Vec<String>>()
            .add("_ => None".to_string())
            .join(",\n")
    }};
}

macro_rules! _save_content {
    ($data: expr, $file_name: expr) => {
        let out_dir = std::env::var("OUT_DIR").unwrap();
        fs::write(
            format!("{out_dir}/{}", $file_name),
            $data
        )
        .unwrap();
    };
}

macro_rules! save_content {
    (result $data: expr, $value: expr, $file_name: expr) => {
        _save_content!(
            format!(
                r#"Ok(match {} {{
                    {}
                }}?)"#,
                $value,
                $data
            ),
            $file_name
        )
    };

    ($data: expr, $value: expr, $file_name: expr) => {
        _save_content!(
            format!(
                r#"match {} {{
                    {}
                }}"#,
                $value,
                $data
            ),
            $file_name
        )
    };
}

macro_rules! codes_with_fs_write {
    (reverse $data: expr, $enum_names: expr, $file_name: expr) => {
        save_content!(codes_reverse!($data, $enum_names), "value", $file_name);
    };

    ($data: expr, $enum_names: expr, $file_name: expr) => {
        save_content!(codes!($data, $enum_names), "self", $file_name);
    };
}

pub fn run() {
    const OFFSET: u8 = 1;
    let names_by_line = NAMES_DATA.split("\n");
    let enum_names = names_by_line
        .clone()
        .map(name_to_enum_name)
        .collect::<Vec<String>>();

    let out_dir = std::env::var("OUT_DIR").unwrap();

    let enum_content = &*enum_names.join(", \n\t");
    fs::write(
        format!("{out_dir}/countries-enum-values"),
        format!(
            r#"
            #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
            pub enum Country {{
                {enum_content}
            }}"#
        ),
    )
    .unwrap();

    codes_with_fs_write!(CODES_2_DATA, enum_names, "countries-codes-2");
    codes_with_fs_write!(CODES_3_DATA, enum_names, "countries-codes-3");
    codes_with_fs_write!(reverse CODES_2_DATA, enum_names, "countries-codes-2-reverse");
    codes_with_fs_write!(reverse CODES_3_DATA, enum_names, "countries-codes-3-reverse");

    let match_pattern = names_by_line
        .clone()
        .enumerate()
        .map(|(i, name)| format!("Country::{} => f.write_str(\"{name}\")", enum_names[i]))
        .collect::<Vec<String>>()
        .join(",\n");
    save_content!(result match_pattern, "self", "countries-to-names");

    let match_pattern = enum_names
        .iter()
        .enumerate()
        .map(|(i, name)| format!("{} => Some(Country::{name})", i as u8 + OFFSET))
        .collect::<Vec<String>>()
        .add("_ => None".to_string())
        .join(",\n");
    save_content!(match_pattern, "value", "countries-from-buffer");

    let match_pattern = COUNTRIES_TO_CONTINENTS
        .split('\n')
        .enumerate()
        .map(|(i, continent)| {
            if continent.is_empty() {
                format!("Country::{} => None", enum_names[i])
            } else {
                format!("Country::{} => Some(Continent::{continent})", enum_names[i])
            }
        })
        .collect::<Vec<String>>()
        .join(",\n");
    save_content!(match_pattern, "value", "country-to-continent");

    let match_pattern = DMA.split('\n')
        .map(|dma| {
            let data: Vec<&str> = dma.split("; ").collect();
            format!("{} => f.write_str(\"{}\")", data[0], data[1])
        })
        .collect::<Vec<String>>()
        .add("_ => f.write_str(\"Unknown DMA\")".to_string())
        .join(",\n");
    save_content!(match_pattern, "value", "dma-code-to-name");
}