haprox-rs 0.2.1

A HaProxy protocol parser.
Documentation
extern crate serde;

use std::{error::Error, fs::File};
use std::io::{self, Write};




macro_rules! dbg 
{
    ($($tokens: tt)*) => {
        println!("cargo::warning={}", format!($($tokens)*))
    }
}

#[derive(Debug, serde::Deserialize)]
struct AlpnProtocolIds
{
    Protocol: String,
    Idc: String,
    Reference: String,
}

struct AlpnStorTmp
{
    ident_hex: String, 
    ident_str: String,
    descr: String,
}

impl AlpnStorTmp
{
    fn rewrite_enum(raw: &str) -> String
    {
        let mut s = String::with_capacity(raw.len());

        let mut upper = true;

        let mut pc = None;

        for c in raw.chars()
        {
            pc = Some(c);

            if s.len() == 0
            {
                if c.is_ascii_alphabetic() == true
                {
                    s.push(c.to_ascii_uppercase());
                    upper = false;
                }
            }
            else
            {
                if c.is_ascii_punctuation() == true || c.is_whitespace() == true
                {
                    upper = true;
                }
                else if c.is_ascii_digit() == true
                {
                    upper = true;
                    s.push(c);
                }
                else if c.is_ascii_alphabetic() == true
                {
                    if upper == true
                    {
                        s.push(c.to_ascii_uppercase());
                        upper = false;
                    }
                    else
                    {
                        s.push(c.to_ascii_lowercase());
                    }
                }
            }
        }

        return s;
    }

    pub 
    fn write_enum(&self, file: &mut File) -> io::Result<()>
    {
        writeln!(file, "\t{},", Self::rewrite_enum(&self.descr))
    }

    pub 
    fn write_array(&self, file: &mut File) -> io::Result<()>
    {
        writeln!(file, "\tAlpnProtocolId\n\t{{ \n\t\tident: AlpnType::{}, \n\t\tident_seq: b\"{}\", \n\t\tident_str: \"{}\", \n\t\tdescr: \"{}\" \n\t}},",
            Self::rewrite_enum(&self.descr), self.ident_hex, self.ident_str, self.descr)
    }
}

impl AlpnProtocolIds
{
    fn example() -> Result<(), Box<dyn Error>> 
    {
        let mut autogen_path = std::env::current_dir().unwrap();
        autogen_path.push("src/protocol/autogen/autogen_alpn.rs");

        let mut path = std::env::current_dir().unwrap();
        path.push("alpn-protocol-ids.csv");

        let mut file = 
            File::options()
                .create(true)
                .truncate(true)
                .write(true)
                .open(autogen_path)
                .unwrap();

        writeln!(file, "// ---- AUTOGENERATED FILE ----\n ")?;
        writeln!(file, "use super::AlpnProtocolId;\n\n")?; 

        

        let mut alpns: Vec<AlpnStorTmp> = Vec::new();
            
        let mut rdr = csv::Reader::from_path(path)?;
        for result in rdr.deserialize() 
        {
            // Notice that we need to provide a type hint for automatic
            // deserialization.
            let mut record: AlpnProtocolIds = result?;
            dbg!("{:?}", record);

            if record.Protocol.as_str() == "Reserved"
            {
                continue;
            }
            record.Idc.retain(|c| !r#"""#.contains(c) && !c.is_whitespace());

            let Some((ident_hex, ident_str)) = record.Idc.split_once("(")
            else {continue;};

            let ident_hex = ident_hex.replace("0x", "\\x");

            let ident_str = ident_str.replace(")", "");

            dbg!("{} {} {}", ident_hex, ident_str, record.Protocol);

            alpns.push(AlpnStorTmp{ ident_hex, ident_str, descr: record.Protocol });
        }

        // sort in alphabetical order
        alpns.sort_by(|a, b| a.ident_str.cmp(&b.ident_str));

        // generate ENUM
        writeln!(file, "#[repr(usize)]\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]")?;
        writeln!(file, "pub enum AlpnType \n{{\n")?;

        for alpn in alpns.iter()
        {
            alpn.write_enum(&mut file)?;
        }

        writeln!(file, "}}\n\n")?;

        write!(file, "pub const ALPNS_LIST: &'static [AlpnProtocolId] = \n&[\n")?;

        for alpn in alpns.iter()
        {
            alpn.write_array(&mut file)?;
        }

        write!(file, "];")?;

        Ok(())
    }
}

fn main()
{
   if std::env::var("DOCS_RS").is_ok() 
    {
        println!("cargo:warning=DOCS_RS, AlpnProtocolIds will not be build.");

        return;
    }

    AlpnProtocolIds::example().unwrap();
}