use std::borrow::Cow;
use strum_macros::IntoStaticStr;
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
pub struct ElementCondition<'t> {
#[serde(rename = "condition")]
pub ctype: ElementConditionType,
pub value: Cow<'t, str>,
}
impl<'t> ElementCondition<'t> {
pub fn parse(raw_spec: &'t str) -> Vec<ElementCondition<'t>> {
fn get_spec(value: &str) -> (ElementConditionType, &str) {
if let Some(value) = value.strip_prefix('+') {
return (ElementConditionType::Required, value);
}
if let Some(value) = value.strip_prefix('-') {
return (ElementConditionType::Prohibited, value);
}
(ElementConditionType::Present, value)
}
raw_spec
.split(' ')
.filter(|s| !s.is_empty())
.map(|s| {
let (ctype, value) = get_spec(s);
ElementCondition {
ctype,
value: cow!(value),
}
})
.collect()
}
pub fn check(conditions: &[ElementCondition], values: &[Cow<str>]) -> bool {
let mut required = true;
let mut prohibited = true;
let mut present = false;
let mut had_present = false;
for condition in conditions {
let has_value = values.contains(&condition.value);
match condition.ctype {
ElementConditionType::Required => required &= has_value,
ElementConditionType::Prohibited => prohibited &= !has_value,
ElementConditionType::Present => {
present |= has_value;
had_present = true;
}
}
}
if !had_present {
present = true;
}
required && prohibited && present
}
}
#[derive(
Serialize, Deserialize, IntoStaticStr, Debug, Copy, Clone, Hash, PartialEq, Eq,
)]
#[serde(rename_all = "kebab-case")]
pub enum ElementConditionType {
Required,
Prohibited,
Present,
}
impl ElementConditionType {
#[inline]
pub fn name(self) -> &'static str {
self.into()
}
}