use std::hash::Hash;
use std::ops::Deref;
use std::sync::Arc;
use regex::Regex;
use crate::affix::{ParsedCfg, RuleType};
use crate::error::BuildError;
use crate::helpers::{compile_re_pattern, ReWrapper};
use crate::morph::MorphInfo;
use crate::parser_affix::ParsedRuleGroup;
use crate::Error;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AfxRule {
kind: RuleType,
can_combine: bool,
patterns: Vec<AfxRulePattern>,
}
impl AfxRule {
#[cfg(test)]
pub fn new(
kind: RuleType,
affixes: &[&str],
patterns: &[&str],
can_combine: bool,
strip: Option<&str>,
condition: Option<&str>,
) -> Self {
let mut ret = Self {
kind,
can_combine,
patterns: affixes
.iter()
.map(|afx| AfxRulePattern::new(afx, None))
.collect(),
};
for (idx, pat) in patterns.iter().enumerate() {
ret.patterns[idx].set_pattern(pat, kind);
}
ret
}
pub fn from_parsed_group(cfg: &ParsedCfg, group: &ParsedRuleGroup) -> Self {
let mut ret = Self {
kind: group.kind,
can_combine: group.can_combine,
patterns: Vec::with_capacity(group.rules.len()),
};
for rule in &group.rules {
let mut morph_info: Vec<Arc<MorphInfo>> = rule
.morph_info
.iter()
.map(|m| Arc::new(m.clone()))
.collect();
ret.patterns.push(AfxRulePattern {
affix: rule.affix.clone(),
condition: rule.condition.clone(),
strip: rule.strip.clone(),
morph_info,
});
}
ret
}
pub fn is_pfx(&self) -> bool {
self.kind == RuleType::Prefix
}
pub fn is_sfx(&self) -> bool {
self.kind == RuleType::Prefix
}
pub fn can_combine(&self) -> bool {
self.can_combine
}
pub fn apply_pattern(&self, stem: &str) -> Option<String> {
self.patterns
.iter()
.find_map(|pat| pat.apply_pattern(stem, self.kind))
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
pub struct AfxRulePattern {
affix: String,
condition: Option<ReWrapper>,
strip: Option<String>,
morph_info: Vec<Arc<MorphInfo>>,
}
impl AfxRulePattern {
#[cfg(test)]
pub fn new(afx: &str, strip: Option<&str>) -> Self {
Self {
affix: afx.to_owned(),
condition: None,
strip: strip.map(ToOwned::to_owned),
morph_info: Vec::new(),
}
}
#[cfg(test)]
pub fn set_pattern(&mut self, condition: &str, kind: RuleType) -> Result<(), regex::Error> {
self.condition = compile_re_pattern(condition, kind)?;
Ok(())
}
#[allow(clippy::option_if_let_else)]
pub fn check_condition(&self, s: &str) -> bool {
match &self.condition {
Some(re) => re.is_match(s),
None => true,
}
}
#[allow(clippy::option_if_let_else)]
fn apply_pattern(&self, s: &str, kind: RuleType) -> Option<String> {
if !self.check_condition(s) {
return None;
}
match kind {
RuleType::Prefix => {
let mut working = self.affix.clone();
if let Some(sc) = &self.strip {
working.push_str(s.strip_prefix(sc.as_str()).unwrap_or(s));
} else {
working.push_str(s);
}
working.shrink_to_fit();
Some(working)
}
RuleType::Suffix => {
let mut working = if let Some(sc) = &self.strip {
s.strip_suffix(sc.as_str()).unwrap_or(s).to_owned()
} else {
s.to_owned()
};
working.push_str(&self.affix);
working.shrink_to_fit();
Some(working)
}
}
}
}
#[cfg(test)]
mod tests;