use super::*;
use crate::parse::{element, remove_surrounding_whitespace};
pub struct PropertyParser {
pub(crate) property_elem: ElementPtr,
base_url: Url,
}
impl PropertyParser {
pub fn new(property_elem: ElementPtr, base_url: &Url) -> Self {
Self {
property_elem,
base_url: base_url.to_owned(),
}
}
fn discern_textual_item_value(
&self,
property_class: &DeclKind,
item: &Item,
prop_name: &str,
) -> Result<(String, Option<ValueKind>), Error> {
let base_prop = item
.properties
.get(if matches!(property_class, DeclKind::Plain(_)) {
"name"
} else {
"url"
})
.and_then(|values| {
values
.iter()
.find(|value| matches!(value, PropertyValue::Plain(_) | PropertyValue::Url(_)))
});
let defined_prop = item.properties.get(prop_name).and_then(|values| {
values
.iter()
.find(|value| matches!(value, PropertyValue::Plain(_) | PropertyValue::Url(_)))
});
let mut resulting_value = base_prop
.or(defined_prop)
.cloned()
.filter(non_empty_property_value);
if resulting_value.is_none() {
let plain_text: String = self.property_elem.node.text_content(&self.base_url)?.into();
resulting_value = Some(PropertyValue::Plain(TextValue::new(plain_text)))
.filter(non_empty_property_value)
};
match resulting_value {
Some(PropertyValue::Plain(v)) if matches!(property_class, DeclKind::Plain(_)) => Ok((
prop_name.to_owned(),
Some(ValueKind::Plain(remove_surrounding_whitespace(
v.to_string(),
))),
)),
Some(PropertyValue::Url(u)) if matches!(property_class, DeclKind::Linked(_)) => {
Ok((prop_name.to_owned(), Some(ValueKind::Url((*u).clone()))))
}
_ => Ok((prop_name.to_owned(), None)),
}
}
fn discern_hypertext_item_value(
&self,
_property_class: &DeclKind,
_item: &Item,
prop_name: &str,
) -> Result<(String, Option<ValueKind>), Error> {
unimplemented!("use the associated item here for {prop_name} (first one?), ValueKind::Item")
}
fn discern_item_value(
&self,
property_class: &DeclKind,
item: &Item,
) -> Result<(String, Option<ValueKind>), Error> {
match property_class {
DeclKind::Plain(prop_name) | DeclKind::Linked(prop_name) => {
self.discern_textual_item_value(property_class, item, prop_name)
}
DeclKind::Hypertext(prop_name) => {
self.discern_hypertext_item_value(property_class, item, prop_name)
}
_ => unreachable!("define logic for {property_class:#?}"),
}
}
pub fn expand(self, matched_elements: &element::MatchedElements) -> Result<Properties, Error> {
let mut properties = Properties::default();
let prop_classes = self.property_elem.node.property_classes();
for prop_decl in prop_classes {
let mut property_item = matched_elements
.expand_item_from_element(Arc::clone(&self.property_elem), &self.base_url)?;
let (property_name, value) = self.discern_item_value(&prop_decl, &property_item)?;
property_item.value = value;
if let Some(values) = properties.get_mut(&property_name) {
values.push(PropertyValue::Item(property_item));
} else {
properties.insert(property_name, vec![PropertyValue::Item(property_item)]);
}
}
Ok(properties)
}
}
#[cfg(test)]
mod test;