summaryrefslogtreecommitdiff
path: root/src/itemized_list.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-02-26 12:12:07 +0100
committerHampusM <hampus@hampusmat.com>2023-02-26 12:12:07 +0100
commit2d633644a53dfe278b681a911b2eaea1230a07cc (patch)
treef493c209707df57a3f12398191f0a87dbabd8523 /src/itemized_list.rs
parente225d7344cef05f03676f6579415999478328ead (diff)
feat: add itemized list support
Diffstat (limited to 'src/itemized_list.rs')
-rw-r--r--src/itemized_list.rs195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/itemized_list.rs b/src/itemized_list.rs
new file mode 100644
index 0000000..efe43b5
--- /dev/null
+++ b/src/itemized_list.rs
@@ -0,0 +1,195 @@
+//! Itemized list.
+use crate::description::{Paragraph, ParagraphError, ParagraphPartError};
+use crate::xml::element::{Element, FromElements};
+
+/// Itemized list.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ItemizedList
+{
+ items: Vec<Item>,
+}
+
+impl ItemizedList
+{
+ /// Returns a new `ItemizedList`.
+ pub fn new(entries: impl IntoIterator<Item = Item>) -> Self
+ {
+ Self {
+ items: entries.into_iter().collect(),
+ }
+ }
+
+ /// Returns the itemized list's items.
+ #[must_use]
+ pub fn items(&self) -> &[Item]
+ {
+ &self.items
+ }
+}
+
+impl FromElements for ItemizedList
+{
+ type Error = Error;
+
+ fn from_elements(
+ elements: &crate::xml::element::Elements,
+ ) -> Result<Self, Self::Error>
+ {
+ let items = elements
+ .get_all_tagged_elements_with_name("listitem")
+ .into_iter()
+ .map(|entry_elem| Item::from_elements(entry_elem.child_elements()))
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Self { items })
+ }
+}
+
+/// [`ItemizedList`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Invalid item.
+ #[error("Invalid item")]
+ InvalidItem(#[from] ItemError),
+}
+
+/// Itemized list item.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Item
+{
+ paragraphs: Vec<Paragraph>,
+}
+
+impl Item
+{
+ /// Returns a new `Item`.
+ pub fn new(paragraphs: impl IntoIterator<Item = Paragraph>) -> Self
+ {
+ Self {
+ paragraphs: paragraphs.into_iter().collect(),
+ }
+ }
+
+ /// Returns the itemized list item paragraphs.
+ #[must_use]
+ pub fn paragraphs(&self) -> &[Paragraph]
+ {
+ &self.paragraphs
+ }
+}
+
+impl FromElements for Item
+{
+ type Error = ItemError;
+
+ fn from_elements(
+ elements: &crate::xml::element::Elements,
+ ) -> Result<Self, Self::Error>
+ {
+ let item = elements
+ .into_iter()
+ .filter_map(|elem| match elem {
+ Element::Tagged(tagged_elem) if tagged_elem.name() == "para" => {
+ Some(tagged_elem)
+ }
+ _ => None,
+ })
+ .map(|para_elem| Paragraph::from_elements(para_elem.child_elements()))
+ .collect::<Result<Vec<_>, _>>()
+ .map_err(|err| Self::Error::InvalidItemParagraph(Box::new(err)))?;
+
+ Ok(Self { paragraphs: item })
+ }
+}
+
+/// [`Item`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum ItemError
+{
+ /// Missing tagged element with name 'term'.
+ #[error("Missing tagged element with name 'term'")]
+ MissingTerm,
+
+ /// Missing tagged element with name 'listitem'.
+ #[error("Missing tagged element with name 'listitem'")]
+ MissingListItem,
+
+ /// Invalid term.
+ #[error("Invalid term")]
+ InvalidTerm(#[source] Box<ParagraphPartError>),
+
+ /// Invalid item paragraph.
+ #[error("Invalid item paragraph")]
+ InvalidItemParagraph(#[source] Box<ParagraphError>),
+}
+
+#[cfg(test)]
+mod tests
+{
+ use super::*;
+ use crate::description::ParagraphPart;
+ use crate::xml::element::{Element, Elements, Tagged};
+
+ #[test]
+ fn itemized_list_from_elements_works()
+ {
+ let itemized_list = ItemizedList::from_elements(&Elements::from([
+ Element::Tagged(Tagged::new(
+ &"listitem",
+ [Element::Tagged(Tagged::new(
+ &"para",
+ [Element::Text("Hello hello.".to_string())],
+ [],
+ ))],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"listitem",
+ [Element::Tagged(Tagged::new(
+ &"para",
+ [
+ Element::Text("Tosche station".to_string()),
+ Element::Text("Power converters".to_string()),
+ ],
+ [],
+ ))],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"listitem",
+ [Element::Tagged(Tagged::new(
+ &"para",
+ [Element::Text("It's a trap".to_string())],
+ [],
+ ))],
+ [],
+ )),
+ ]))
+ .expect("Expected Ok");
+
+ assert_eq!(itemized_list.items.len(), 3);
+ }
+
+ #[test]
+ fn item_from_elements_works()
+ {
+ let item = Item::from_elements(&Elements::from([
+ Element::Tagged(Tagged::new(&"para", [Element::Text("bar".to_string())], [])),
+ Element::Tagged(Tagged::new(
+ &"para",
+ [Element::Text("Hello there.".to_string())],
+ [],
+ )),
+ ]))
+ .expect("Expected Ok");
+
+ assert_eq!(
+ item.paragraphs,
+ vec![
+ Paragraph::new([ParagraphPart::Text("bar".to_string())]),
+ Paragraph::new([ParagraphPart::Text("Hello there.".to_string())]),
+ ]
+ );
+ }
+}