use serde::{Deserialize, Deserializer, Serialize};
use crate::error::Result;
use crate::normalized_string::SplitDelimiterBehavior;
use crate::pattern::{Invert, SplitPattern, SysRegex};
use crate::pre_tokenized_string::PreTokenizedString;
use crate::traits::PreTokenizer;
#[derive(Debug, Clone, Serialize)]
#[non_exhaustive]
pub struct Split {
pub(crate) pattern: SplitPattern,
pub behavior: SplitDelimiterBehavior,
pub invert: bool,
#[serde(skip)]
regex: SysRegex,
}
impl PartialEq for Split {
fn eq(&self, other: &Self) -> bool {
self.pattern == other.pattern
&& self.behavior == other.behavior
&& self.invert == other.invert
}
}
impl<'de> Deserialize<'de> for Split {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
#[derive(Deserialize)]
struct Helper {
pattern: SplitPattern,
behavior: SplitDelimiterBehavior,
#[serde(default)]
invert: bool,
}
let h = Helper::deserialize(deserializer)?;
Split::new(h.pattern, h.behavior, h.invert).map_err(serde::de::Error::custom)
}
}
impl Split {
pub fn new(
pattern: impl Into<SplitPattern>,
behavior: SplitDelimiterBehavior,
invert: bool,
) -> Result<Self> {
let pattern = pattern.into();
let regex = pattern.to_regex()?;
Ok(Self {
pattern,
behavior,
invert,
regex,
})
}
pub fn pattern(&self) -> &SplitPattern {
&self.pattern
}
}
impl PreTokenizer for Split {
fn pre_tokenize(&self, pretokenized: &mut PreTokenizedString) -> Result<()> {
if self.invert {
pretokenized.split(|_, s| s.split(Invert(&self.regex), self.behavior))
} else {
pretokenized.split(|_, s| s.split(&self.regex, self.behavior))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pre_tokenizers::tests::splits;
use SplitDelimiterBehavior::*;
#[test]
fn behaviors() {
let s = |b| Split::new(SplitPattern::Regex(r"\s+".into()), b, false).unwrap();
assert_eq!(
splits(&s(Removed), "How are you?"),
vec![("How", (0, 3)), ("are", (4, 7)), ("you?", (9, 13))]
);
assert_eq!(
splits(&s(MergedWithPrevious), "How are you?"),
vec![("How ", (0, 4)), ("are ", (4, 9)), ("you?", (9, 13))]
);
assert_eq!(
splits(&s(MergedWithNext), "How are you?"),
vec![("How", (0, 3)), (" are", (3, 7)), (" you?", (7, 13))]
);
}
#[test]
fn invert_keeps_matches() {
let s = Split::new(SplitPattern::Regex(r"\w+".into()), Removed, true).unwrap();
assert_eq!(
splits(&s, "Hey, man"),
vec![("Hey", (0, 3)), ("man", (5, 8))]
);
}
#[test]
fn string_pattern_is_literal() {
let s = Split::new(SplitPattern::String("a.b".into()), Isolated, false).unwrap();
assert_eq!(
splits(&s, "xa.bxaxb"),
vec![("x", (0, 1)), ("a.b", (1, 4)), ("xaxb", (4, 8))]
);
}
#[test]
fn bad_regex_is_an_error() {
let json = r#"{"pattern":{"Regex":"("},"behavior":"Isolated","invert":false}"#;
assert!(serde_json::from_str::<Split>(json).is_err());
}
}