wp-lang 0.3.0

WPL language crate with AST, parser, evaluator, builtins, and generators.
Documentation
use super::super::prelude::*;
use smol_str::SmolStr;
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr};
use wp_model_core::model::FNameStr;

use crate::derive_base_prs;
use crate::eval::runtime::field::FieldEvalUnit;
use crate::eval::value::parse_def::*;
use crate::generator::ParserValue;
use crate::generator::{FieldGenConf, GenScopeEnum};
use rand::RngExt;
use winnow::ascii::digit1;
use winnow::combinator::separated_pair;
use wp_model_core::model::IpNetValue;
use wp_primitives::net::ip;

derive_base_prs!(IpPSR);

impl ParserValue<IpAddr> for IpPSR {
    fn parse_value<'a>(data: &mut &str) -> ModalResult<IpAddr> {
        ip.context(ctx_desc("<ip>")).parse_next(data)
    }
}

impl PatternParser for IpPSR {
    fn pattern_parse(
        &self,
        _e_id: u64,
        _fpu: &FieldEvalUnit,
        _ups_sep: &WplSep,
        data: &mut &str,
        name: FNameStr,
        out: &mut Vec<DataField>,
    ) -> ModalResult<()> {
        let ip = IpPSR::parse_value(data)?;
        out.push(DataField::from_ip(name, ip));
        Ok(())
    }

    fn patten_gen(
        &self,
        gnc: &mut GenChannel,
        f_conf: &WplField,
        g_conf: Option<&FieldGenConf>,
    ) -> WplCodeResult<DataField> {
        let range = if let Some(Some(GenScopeEnum::Ip(conf))) = g_conf.map(|c| &c.scope) {
            let beg: u32 = conf.beg.into();
            let end: u32 = conf.end.into();
            beg..end
        } else {
            1000000000..2000000000
        };
        let digit = gnc.rng.random_range(range);
        let ip = Ipv4Addr::from(digit);

        if let Some(Some(fmt)) = g_conf.map(|c| &c.gen_fmt) {
            let mut vals = HashMap::new();
            vals.insert("val".to_string(), ip.to_string());
            match strfmt::strfmt(fmt, &vals) {
                Ok(dat) => {
                    return Ok(DataField::from_chars(
                        f_conf.safe_name(),
                        SmolStr::from(dat),
                    ));
                }
                Err(e) => {
                    error!("gen fmt error: {}", e);
                }
            }
        }

        Ok(DataField::from_ip(f_conf.safe_name(), IpAddr::V4(ip)))
    }
}

derive_base_prs!(IpNetP);
impl ParserValue<IpNetValue> for IpNetP {
    fn parse_value<'a>(data: &mut &str) -> ModalResult<IpNetValue> {
        separated_pair(ip, literal("/"), digit1.try_map(str::parse::<u8>))
            .map(|(ip, mask)| IpNetValue::new(ip, mask).unwrap())
            .parse_next(data)
    }
}

impl PatternParser for IpNetP {
    fn pattern_parse(
        &self,
        _e_id: u64,
        _fpu: &FieldEvalUnit,
        _ups_sep: &WplSep,
        data: &mut &str,
        name: FNameStr,
        out: &mut Vec<DataField>,
    ) -> ModalResult<()> {
        let ipnet = IpNetP::parse_value(data)?;
        out.push(DataField::new_opt(
            DataType::IpNet,
            Some(name),
            ipnet.into(),
        ));
        Ok(())
    }

    fn patten_gen(
        &self,
        _gen: &mut GenChannel,
        _f_conf: &WplField,
        _g_conf: Option<&FieldGenConf>,
    ) -> WplCodeResult<DataField> {
        unimplemented!("ip_net gen");
    }
}

#[cfg(test)]
mod tests {
    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

    use wp_primitives::Parser;

    use crate::ast::WplField;
    use crate::eval::value::parser::base::digit::DigitP;
    use crate::eval::value::parser::network::net::{IpNetP, IpPSR, ip};
    use crate::eval::value::test_utils::{ParserTUnit, verify_parse_v_suc_end};
    use crate::parser::error::WplCodeResult;
    use orion_error::dev::testing::TestAssert;
    use wp_model_core::model::{DataField, IpNetValue};

    #[test]
    fn test_ip_net() {
        let mut data = "172.0.0.1/24";
        let field = verify_parse_v_suc_end::<IpNetP, IpNetValue>(&mut data);
        assert_eq!(
            field,
            IpNetValue::new("172.0.0.1".parse().assert(), 24).assert()
        )
    }

    #[test]
    fn test_ip_partial() -> WplCodeResult<()> {
        assert_eq!(
            ip.parse_peek("172.0.0.9"),
            Ok(("", "172.0.0.9".parse().assert()))
        );
        assert_eq!(
            ip.parse_peek("172.0.0.9:80"),
            Ok((":80", "172.0.0.9".parse().assert()))
        );
        assert_eq!(
            ip.parse_peek("2001:db8::1:80"),
            Ok(("", "2001:db8::1:80".parse().assert()))
        );
        assert_eq!(
            ip.parse_peek("2001:db8::1.80"),
            Ok((".80", "2001:db8::1".parse().assert()))
        );

        let mut data = "172.0.0.1:80";
        let conf_ip = WplField::try_parse("ip\\:").assert();
        let field = ParserTUnit::new(IpPSR::default(), conf_ip.clone())
            .verify_parse_suc(&mut data)
            .assert();
        assert_eq!(
            field[0],
            DataField::from_ip("ip", "172.0.0.1".parse().assert())
        );
        assert_eq!(data, "80");

        let conf_port = WplField::try_parse("digit").assert();
        let field = ParserTUnit::new(DigitP::default(), conf_port.clone())
            .verify_parse_suc(&mut data)
            .assert();
        assert_eq!(field[0], DataField::from_digit("digit", 80));
        assert_eq!(data, "");

        let mut data = "[172.0.0.1] [80]";
        let conf_ip = WplField::try_parse("ip<[,]>").assert();
        let field = ParserTUnit::new(IpPSR::default(), conf_ip.clone())
            .verify_parse_suc(&mut data)
            .assert();
        assert_eq!(
            field[0],
            DataField::from_ip("ip", "172.0.0.1".parse().assert())
        );
        assert_eq!(data, "[80]");

        let conf_port = WplField::try_parse("digit<[,]>").assert();
        let field = ParserTUnit::new(DigitP::default(), conf_port.clone())
            .verify_parse_suc(&mut data)
            .assert();
        assert_eq!(field[0], DataField::from_digit("digit", 80));
        assert_eq!(data, "");

        let mut data = "192.168.1.2 - -";
        let conf_ip = WplField::try_parse("ip").assert();
        let field = ParserTUnit::new(IpPSR::default(), conf_ip.clone())
            .verify_parse_suc(&mut data)
            .assert();
        assert_eq!(
            field[0],
            DataField::from_ip("ip", "192.168.1.2".parse().assert())
        );
        assert_eq!(data, "- -");
        Ok(())
    }

    #[test]
    fn test_ip() -> WplCodeResult<()> {
        let conf_ip = WplField::try_parse("ip").assert();
        let mut data = "192.168.1.2";
        let x = ParserTUnit::new(IpPSR::default(), conf_ip.clone())
            .verify_parse_suc(&mut data)
            .assert();
        assert_eq!(
            x,
            vec![DataField::from_ip(
                "ip",
                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)),
            )]
        );

        let mut data = " localhost ";
        let x = ParserTUnit::new(IpPSR::default(), conf_ip.clone())
            .verify_parse_suc(&mut data)
            .assert();
        assert_eq!(
            x,
            vec![DataField::from_ip(
                "ip",
                IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
            )]
        );

        Ok(())
    }

    #[test]
    fn test_ip_gen() -> WplCodeResult<()> {
        let conf_ip = WplField::try_parse("ip").assert();
        ParserTUnit::new(IpPSR::default(), conf_ip).verify_gen_parse_suc();

        let conf_ip = WplField::try_parse("ip<[,]>").assert();
        ParserTUnit::new(IpPSR::default(), conf_ip).verify_gen_parse_suc();

        let conf_ip = WplField::try_parse("ip\"").assert();
        ParserTUnit::new(IpPSR::default(), conf_ip).verify_gen_parse_suc();

        Ok(())
    }

    #[test]
    fn test_ip_2() {
        assert_eq!(
            Ok(("", IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)))),
            ip.parse_peek("ff00::")
        );
        assert_eq!(
            ip.parse_peek("127.0.0.1"),
            Ok(("", IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))),
        );
        assert_eq!(
            ip.parse_peek("::1"),
            Ok(("", IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))),
        );
    }
}