use std::str::Split;
pub type Parsed = Vec<String>;
pub type ParsedResponse = (Parsed, Parsed, Parsed);
#[derive(Debug, Copy, Clone)]
pub struct Prefixes<'a> {
pub positive: &'a str,
pub negative: &'a str,
}
#[derive(Debug, Clone)]
pub struct Keywords {
pub positive: Parsed,
pub negative: Parsed,
pub other: Parsed,
}
impl<'a> Default for Prefixes<'a> {
fn default() -> Self {
Self {
positive: "+",
negative: "-",
}
}
}
pub struct Parser<'a> {
input: String,
pub prefixes: Prefixes<'a>,
retain_prefix: bool,
}
impl<'a> Parser<'a> {
pub fn new(input: &str, prefixes: Prefixes<'a>) -> Self {
Self {
input: input.to_string(),
prefixes,
retain_prefix: false,
}
}
pub fn should_retain_prefix(&mut self, bool: bool) -> bool {
self.retain_prefix = bool;
bool
}
fn parse_with_prefix(&self, split: Split<&str>, prefix: &str) -> Vec<String> {
return split
.filter(|e| e.starts_with(&prefix))
.map(|e| {
if !self.retain_prefix {
e.replace(&prefix, "")
} else {
e.to_string()
}
})
.collect();
}
pub fn parse(&self) -> Keywords {
let split = self.input.clone();
let split = split.split(",");
let positive = self.parse_with_prefix(split.clone(), self.prefixes.positive);
let negative = self.parse_with_prefix(split.clone(), self.prefixes.negative);
let other = split
.filter(|x| {
!positive.iter().any(|y| x.contains(y)) && !negative.iter().any(|y| x.contains(y))
})
.map(|x| x.to_string())
.collect();
return Keywords {
positive,
negative,
other,
};
}
pub fn match_products(&self, products: Vec<&str>, keywords: Keywords) -> Vec<String> {
let mut found: Vec<String> = vec![];
for product in products {
let p_lower = &product.to_lowercase();
println!("Debug: {}", product);
if keywords.positive.iter().any(|e| p_lower.to_lowercase().contains(&e.to_lowercase()))
&& !keywords.negative.iter().any(|e| p_lower.contains(&e.to_lowercase()))
{
found.push(product.to_string());
}
}
return found;
}
}
#[cfg(test)]
mod test {
use crate::{Parser, Prefixes};
#[test]
fn basic_text() {
let parser = Parser::new(
"+foo,-bar,+baz",
Prefixes {
positive: "+",
negative: "-",
},
);
let keywords = parser.parse();
assert_eq!(keywords.positive, vec!["foo", "baz"]);
assert_eq!(keywords.negative, vec!["bar"]);
}
#[test]
fn do_not_retain_prefix() {
let mut parser = Parser::new(
"+foo,-bar,+baz",
Prefixes {
positive: "+",
negative: "-",
},
);
parser.should_retain_prefix(false);
let keywords = parser.parse();
assert_eq!(keywords.positive, vec!["foo", "baz"]);
assert_eq!(keywords.negative, vec!["bar"]);
}
#[test]
fn weird_prefixes() {
let parser = Parser::new(
"yes!!foo,no!!bar,yes!!baz",
Prefixes {
positive: "yes!!",
negative: "no!!",
},
);
let keywords = parser.parse();
assert_eq!(keywords.positive, vec!["foo", "baz"]);
assert_eq!(keywords.negative, vec!["bar"]);
}
#[test]
fn unparsed() {
let parser = Parser::new(
"+foo,-bar,+baz,bak",
Prefixes {
positive: "+",
negative: "-",
},
);
let keywords = parser.parse();
assert_eq!(keywords.other, vec!["bak"]);
}
#[test]
fn basic_products() {
let products = vec!["MyProduct Adult", "MyProduct Youth"];
let parser = Parser::new(
"+myproduct,-youth",
Prefixes {
positive: "+",
negative: "-",
},
);
let keywords = parser.parse();
let products = parser.match_products(products.clone(), keywords.clone());
assert_eq!(products, vec!["MyProduct Adult"]);
}
}