use std::fmt::Debug;
use std::rc::Rc;
use smol_str::SmolStr;
use crate::parser::utils::take_to_end;
use winnow::ascii::multispace0;
use winnow::combinator::preceded;
use winnow::combinator::{opt, trace};
use winnow::stream::{FindSlice, Stream};
use winnow::token::{literal, take_while};
use wp_primitives::WResult as ModalResult;
use wp_primitives::Parser;
#[derive(Debug)]
pub struct CharSep {
sep_char: char,
sep_str: SmolStr,
}
impl CharSep {
#[allow(dead_code)]
pub fn new(sep_char: char) -> Self {
Self {
sep_char,
sep_str: SmolStr::from(sep_char.to_string()),
}
}
}
impl Separator for CharSep {
fn sep_str(&self) -> &str {
self.sep_str.as_str()
}
fn not_sep(&self, c: char) -> bool {
c != self.sep_char
}
fn flag(&self) -> &str {
self.sep_str.as_str()
}
fn get_field<'a>(&self, input: &mut &'a str) -> WResult<&'a str> {
if self.sep_char == '0' {
preceded(multispace0, take_to_end).parse_next(input)
} else {
preceded(multispace0, take_while(0.., |c| self.not_sep(c))).parse_next(input)
}
}
}
#[derive(Debug)]
pub struct StrSep {
sep_str: SmolStr,
}
impl StrSep {
#[allow(dead_code)]
pub fn new<S: Into<SmolStr>>(sep_str: S) -> Self {
let sep_str = sep_str.into();
assert!(!sep_str.is_empty());
Self { sep_str }
}
fn sep_tag(&self) -> &str {
self.sep_str.as_str()
}
}
impl Separator for StrSep {
fn sep_str(&self) -> &str {
self.sep_str.as_str()
}
fn not_sep(&self, c: char) -> bool {
c.to_string() != self.sep_str
}
fn flag(&self) -> &str {
self.sep_str.as_str()
}
fn get_field<'a>(&self, input: &mut &'a str) -> WResult<&'a str> {
match input.find_slice(self.sep_tag()) {
None => {
let field = winnow::token::take_while(0.., |_| true).parse_next(input)?;
Ok(field)
}
Some(index) => Ok(input.next_slice(index.start)),
}
}
}
pub trait Separator: Debug {
fn sep_str(&self) -> &str;
fn not_sep(&self, c: char) -> bool;
fn flag(&self) -> &str;
fn tag<'a>(&self, input: &mut &'a str) -> WResult<Option<&'a str>> {
let sep = self.sep_str();
trace(format!("尝试忽略分隔符:[{}]", sep), opt(literal(sep))).parse_next(input)
}
fn get_field<'a>(&self, input: &mut &'a str) -> WResult<&'a str>;
}
pub type Hold<T> = Rc<T>;
pub type SeparatorHold = Hold<dyn Separator>;
#[cfg(test)]
mod tests {
use crate::parser::error::WplCodeResult;
use super::{CharSep, Separator, StrSep};
#[test]
fn test_sep_get_field() -> WplCodeResult<()> {
let mut data = "kv=val;kv2=val2 ";
assert_eq!(
CharSep::new(',').get_field_owned(&mut data),
Ok("kv=val;kv2=val2 ")
);
let mut data = "kv=val;kv2=val2,";
assert_eq!(
CharSep::new(',').get_field_owned(&mut data),
Ok("kv=val;kv2=val2")
);
assert_eq!(data, ",");
let mut data = "kv=val;kv2=val2, ";
assert_eq!(
CharSep::new(',').get_field_owned(&mut data),
Ok("kv=val;kv2=val2")
);
assert_eq!(data, ", ");
let mut data = "kv=val;kv2=val2,xyz ";
assert_eq!(
CharSep::new('0').get_field_owned(&mut data),
Ok("kv=val;kv2=val2,xyz ")
);
let mut data = "kv=val;kv2=val2, xxx";
assert_eq!(
CharSep::new(',').get_field_owned(&mut data),
Ok("kv=val;kv2=val2")
);
assert_eq!(data, ", xxx");
let mut data = "";
assert_eq!(CharSep::new(',').get_field_owned(&mut data), Ok(""));
assert_eq!(data, "");
Ok(())
}
#[test]
fn test_sep_tag() -> WplCodeResult<()> {
let mut data = "kv=val;kv2=val2 ";
assert_eq!(CharSep::new(',').tag(&mut data), Ok(None));
let mut data = "kv=val;kv2=val2,";
assert_eq!(CharSep::new(',').tag(&mut data), Ok(None));
let mut data = ",kv=val;kv2=val2,";
assert_eq!(CharSep::new(',').tag(&mut data), Ok(Some(",")));
let mut data = "kv=val;kv2=val2, ";
assert_eq!(CharSep::new('k').tag(&mut data), Ok(Some("k")));
let mut data = "kv=val;kv2=val2, ";
assert_eq!(StrSep::new("kv").tag(&mut data), Ok(Some("kv")));
Ok(())
}
#[test]
fn test_diysep() -> WplCodeResult<()> {
let mut data = "kv=val;kv2=val2, xxx";
assert_eq!(
StrSep::new(";".to_string()).get_field_owned(&mut data),
Ok("kv=val")
);
assert_eq!(data, ";kv2=val2, xxx");
let mut data = "hello";
assert_eq!(
StrSep::new(";".to_string()).get_field_owned(&mut data),
Ok("hello")
);
assert_eq!(data, "");
let mut data = "hello|!";
assert_eq!(
StrSep::new("|!".to_string()).get_field_owned(&mut data),
Ok("hello")
);
assert_eq!(data, "|!");
let mut data = "hello|!hello|!|";
assert_eq!(
StrSep::new("|!|".to_string()).get_field_owned(&mut data),
Ok("hello|!hello")
);
assert_eq!(data, "|!|");
Ok(())
}
}