Skip to main content

microformats_types/
traits.rs

1//! Utility traits for item lookup.
2
3use crate::{Document, Item, PropertyValue};
4use std::collections::HashSet;
5
6/// Trait for filtering items by language.
7pub trait LanguageFilter: Sized {
8    /// Returns true if this item or any of its children match the given languages.
9    fn matches_languages(&self, languages: &HashSet<&str>) -> bool;
10
11    /// Returns a new instance with only items matching the given languages.
12    fn filter_by_languages_set(&self, languages: &HashSet<&str>) -> Option<Self>;
13}
14
15/// Trait for finding items by property value.
16pub trait FindItemByProperty {
17    /// Finds all items with properties matching the predicate.
18    fn find_items_with_matching_property_value_by<F>(&self, predicate: F) -> Vec<(String, Item)>
19    where
20        F: Fn(String, PropertyValue) -> bool + Copy;
21
22    /// Finds all items with a property matching the given value.
23    fn find_items_with_matching_property_value(
24        &self,
25        needle: PropertyValue,
26    ) -> Vec<(String, Item)> {
27        self.find_items_with_matching_property_value_by(|_name, property_value| {
28            property_value == needle
29        })
30    }
31}
32
33impl FindItemByProperty for Item {
34    fn find_items_with_matching_property_value_by<F>(&self, predicate: F) -> Vec<(String, Item)>
35    where
36        F: Fn(String, PropertyValue) -> bool + Copy,
37    {
38        let mut values = self
39            .properties
40            .iter()
41            .filter_map(|(name, values)| {
42                if values
43                    .iter()
44                    .any(|value| predicate(name.to_owned(), value.to_owned()))
45                {
46                    Some((name.to_owned(), self.to_owned()))
47                } else {
48                    None
49                }
50            })
51            .collect::<Vec<_>>();
52
53        self.children.iter().for_each(|child| {
54            values.extend(child.find_items_with_matching_property_value_by(predicate));
55        });
56
57        values
58    }
59}
60
61impl FindItemByProperty for Document {
62    fn find_items_with_matching_property_value_by<F>(&self, predicate: F) -> Vec<(String, Item)>
63    where
64        F: Fn(String, PropertyValue) -> bool + std::marker::Copy,
65    {
66        self.items
67            .iter()
68            .flat_map(|item| item.find_items_with_matching_property_value_by(predicate))
69            .collect()
70    }
71}
72
73/// Trait for finding items by URL property.
74pub trait FindItemByUrl: FindItemByProperty {
75    /// Finds an item with a url property matching the expected URL.
76    fn find_item_by_url(&self, expected_url: &url::Url) -> Option<Item> {
77        let url_property_value = PropertyValue::Url(crate::UrlValue::new(expected_url.to_owned()));
78        self.find_items_with_matching_property_value(url_property_value)
79            .first()
80            .map(|(_name, value)| value.to_owned())
81    }
82}
83
84impl FindItemByUrl for Document {}
85
86impl FindItemByUrl for Item {}
87
88/// Trait for finding items by ID.
89pub trait FindItemById {
90    /// Finds an item with the given ID.
91    fn find_item_by_id(&self, expected_id: &str) -> Option<Item>;
92}
93
94impl FindItemById for Item {
95    fn find_item_by_id(&self, expected_id: &str) -> Option<Item> {
96        if self.id == Some(expected_id.to_string()) {
97            Some(self.to_owned())
98        } else {
99            self.children
100                .iter()
101                .find_map(|item| item.find_item_by_id(expected_id))
102        }
103    }
104}
105
106impl FindItemById for Document {
107    fn find_item_by_id(&self, expected_id: &str) -> Option<Item> {
108        self.items
109            .iter()
110            .find_map(|item| item.find_item_by_id(expected_id))
111    }
112}