use super::super::prelude::*;
use crate::eval::runtime::field::FieldEvalUnit;
use crate::generator::ParserValue;
use wp_model_core::model::FNameStr;
use crate::parser::error::IntoWplCodeError;
use crate::parser::utils::quot_str;
use winnow::ascii::{digit1, multispace0};
use winnow::combinator::{fail, preceded, separated};
use wp_model_core::model::DigitValue;
use wp_primitives::symbol::{ctx_desc, symbol_brackets_beg, symbol_brackets_end};
#[derive(Default)]
pub struct ArrayP {}
impl ArrayP {
pub fn new() -> Self {
Self {}
}
fn parse_array_element(
next_fpu: &FieldEvalUnit,
sep: &WplSep,
name: &FNameStr,
idx_local: &mut usize,
out: &mut Vec<DataField>,
input: &mut &str,
) -> ModalResult<()> {
let idx_str = format!("{}/[{}]", name, idx_local);
let mut probe = *input;
match next_fpu.parse(0, sep, &mut probe, Some(idx_str.clone().into()), out) {
Ok(_) => {
*idx_local += 1;
*input = probe;
Ok(())
}
Err(_) => {
let mut quoted_probe = *input;
if let Ok(inner) = quot_str.parse_next(&mut quoted_probe) {
let mut inner_ref = inner;
next_fpu.parse(0, sep, &mut inner_ref, Some(idx_str.into()), out)?;
*idx_local += 1;
*input = quoted_probe;
Ok(())
} else {
let mut bad = "";
let dyn_label = format!("array/[{}]", idx_local);
let _: &str = alt((dyn_label.as_str(), "array element"))
.context(ctx_desc("array element failed"))
.parse_next(&mut bad)?;
unreachable!("array element error should not succeed");
}
}
}
}
}
impl ParserValue<DigitValue> for ArrayP {
fn parse_value<'a>(data: &mut &str) -> ModalResult<DigitValue> {
preceded(multispace0, digit1.try_map(str::parse::<DigitValue>)).parse_next(data)
}
}
impl PatternParser for ArrayP {
fn pattern_parse<'a>(
&self,
_e_id: u64,
fpu: &FieldEvalUnit,
_ups_sep: &WplSep,
data: &mut &str,
name: FNameStr,
out: &mut Vec<DataField>,
) -> ModalResult<()> {
let mut tdo_arr: Vec<DataField> = Vec::with_capacity(10);
if let Some(next_fpu) = fpu.next() {
let cur_sep = WplSep::field_sep_until(",", "]", false);
let mut idx_local = 0usize;
let mut sub_parser = |input: &mut &str| {
Self::parse_array_element(
next_fpu,
&cur_sep,
&name,
&mut idx_local,
&mut tdo_arr,
input,
)
};
symbol_brackets_beg.parse_next(data)?;
let _: Vec<()> = separated(0.., sub_parser.by_ref(), ",").parse_next(data)?;
let _ = preceded(multispace0, opt(literal(","))).parse_next(data)?;
symbol_brackets_end.parse_next(data)?;
out.push(DataField::from_arr(name, tdo_arr));
return Ok(());
}
fail.parse_next(data)
}
fn patten_gen(
&self,
_gen: &mut GenChannel,
_f_conf: &WplField,
_g_conf: Option<&FieldGenConf>,
) -> WplCodeResult<DataField> {
use crate::eval::value::parser::ParserFactory;
use wp_model_core::model::DataType;
let name = _f_conf.safe_name();
let elem_meta = match &_f_conf.meta_type {
DataType::Array(sub) => DataType::from(sub).unwrap_or(DataType::Chars),
_ => DataType::Chars,
};
let mut items: Vec<DataField> = Vec::with_capacity(2);
let elem_conf =
WplField::sub_for_arr(elem_meta.to_string().as_str()).map_err(|e| e.into_wpl_err())?;
let elem_parser = ParserFactory::create(&elem_meta)?;
let sep = WplSep::default();
let mut f0 = elem_parser
.generate(_gen, &sep, &elem_conf, _g_conf)
.map(|fmt| fmt.data_field)?;
f0.set_name(format!("{}/[0]", name));
items.push(f0);
let mut f1 = elem_parser
.generate(_gen, &sep, &elem_conf, _g_conf)
.map(|fmt| fmt.data_field)?;
f1.set_name(format!("{}/[1]", name));
items.push(f1);
Ok(DataField::from_arr(name, items))
}
}
#[cfg(test)]
mod tests {
use std::{net::IpAddr, str::FromStr};
use crate::eval::runtime::vm_unit::WplEvaluator;
use wp_model_core::model::DataType;
use super::*;
#[test]
fn test_array() -> WplCodeResult<()> {
let data = r#"[1, 2, 3]"#;
let rule = r#" rule x { (array/digit:array_val)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
println!("{}", tdc);
let expected = vec![
DataField::new_opt(DataType::Digit, Some("array_val/[0]".into()), 1.into()),
DataField::new_opt(DataType::Digit, Some("array_val/[1]".into()), 2.into()),
DataField::new_opt(DataType::Digit, Some("array_val/[2]".into()), 3.into()),
];
let expected = DataField::from_arr("array_val".to_string(), expected);
assert_eq!(tdc.get_field_owned("array_val"), Some(expected));
let data = r#"["hello", "_F]fe", "!@#$*&^\"123"]"#;
let rule = r#" rule x { (array/chars:array)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
println!("{}", tdc);
let expected = vec![
DataField::new_opt(DataType::Chars, Some("array/[0]".into()), "hello".into()),
DataField::new_opt(DataType::Chars, Some("array/[1]".into()), "_F]fe".into()),
DataField::new_opt(
DataType::Chars,
Some("array/[2]".into()),
"!@#$*&^\\\"123".into(),
),
];
let expected = DataField::from_arr("array".to_string(), expected);
assert_eq!(tdc.get_field_owned("array"), Some(expected));
let data = r#"[1,2,3,]"#;
let rule = r#" rule x { (array/digit:nums)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
let expected = vec![
DataField::new_opt(DataType::Digit, Some("nums/[0]".into()), 1.into()),
DataField::new_opt(DataType::Digit, Some("nums/[1]".into()), 2.into()),
DataField::new_opt(DataType::Digit, Some("nums/[2]".into()), 3.into()),
];
let expected = DataField::from_arr("nums".to_string(), expected);
assert_eq!(tdc.get_field_owned("nums"), Some(expected));
let data = r#"[]"#;
let rule = r#" rule x { (array/digit:empty)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
let expected: Vec<DataField> = vec![];
let expected = DataField::from_arr("empty".to_string(), expected);
assert_eq!(tdc.get_field_owned("empty"), Some(expected));
Ok(())
}
#[test]
fn test_array_ip_with_quotes() -> WplCodeResult<()> {
let data = r#"["1.1.1.1","2.2.2.2"]"#;
let rule = r#" rule x { (array/ip:ips)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
let expected = vec![
DataField::from_ip(
"ips/[0]",
IpAddr::from_str("1.1.1.1").map_err(|e| e.into_wpl_err())?,
),
DataField::from_ip(
"ips/[1]",
IpAddr::from_str("2.2.2.2").map_err(|e| e.into_wpl_err())?,
),
];
let expected = DataField::from_arr("ips".to_string(), expected);
assert_eq!(tdc.get_field_owned("ips"), Some(expected));
Ok(())
}
#[test]
fn test_arr_arr() -> WplCodeResult<()> {
let data = "[[1,2],[3,4]]";
let rule = r#" rule x { (array/array/digit:array)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
println!("{}", tdc);
let obj = DataField::from_arr(
"array/[0]",
vec![
DataField::new_opt(DataType::Digit, Some("array/[0]/[0]".into()), 1.into()),
DataField::new_opt(DataType::Digit, Some("array/[0]/[1]".into()), 2.into()),
],
);
let obj1 = DataField::from_arr(
"array/[1]",
vec![
DataField::new_opt(DataType::Digit, Some("array/[1]/[0]".into()), 3.into()),
DataField::new_opt(DataType::Digit, Some("array/[1]/[1]".into()), 4.into()),
],
);
assert_eq!(
tdc.get_field_owned("array"),
Some(DataField::from_arr("array".to_string(), vec![obj, obj1]))
);
Ok(())
}
#[test]
fn test_arr_json() -> WplCodeResult<()> {
let data = r#"[{"name":"xxx", "value":"xxx"}, {"name": "xxxx", "value": 85.2}]"#;
let rule = r#" rule x { (array/json:array)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
println!("{}", tdc);
let obj = vec![
DataField::new_opt(DataType::Chars, Some("array/[0]/name".into()), "xxx".into()),
DataField::new_opt(
DataType::Chars,
Some("array/[0]/value".into()),
"xxx".into(),
),
DataField::new_opt(
DataType::Chars,
Some("array/[1]/name".into()),
"xxxx".into(),
),
DataField::new_opt(DataType::Float, Some("array/[1]/value".into()), 85.2.into()),
];
let expected = DataField::from_arr("array", obj);
assert_eq!(tdc.get_field_owned("array"), Some(expected));
Ok(())
}
#[test]
fn test_arr_1() -> WplCodeResult<()> {
let data = r#"[1.1.1.1,2.2.2.2]"#;
let rule = r#" rule x { (array/ip:block_ips)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
println!("{}", tdc);
assert!(tdc.field("block_ips").is_some());
Ok(())
}
#[test]
fn test_arr_01() -> WplCodeResult<()> {
let data = r#"[]"#;
let rule = r#" rule x { (array/ip:block_ips)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
println!("{}", tdc);
assert!(tdc.field("block_ips").is_some());
Ok(())
}
#[test]
fn test_arr_00() -> WplCodeResult<()> {
let data = r#"[]"#;
let rule = r#" rule x { (array/chars:block_ips)}"#;
let pipe = WplEvaluator::from_code(rule)?;
let (tdc, _) = pipe.proc(0, data, 0).map_err(|e| e.into_wpl_err())?;
println!("{}", tdc);
assert!(tdc.field("block_ips").is_some());
Ok(())
}
}