summaryrefslogtreecommitdiff
path: root/src/variable_list.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/variable_list.rs')
-rw-r--r--src/variable_list.rs264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/variable_list.rs b/src/variable_list.rs
new file mode 100644
index 0000000..2c839f0
--- /dev/null
+++ b/src/variable_list.rs
@@ -0,0 +1,264 @@
+//! Variable list.
+
+use crate::description::{Paragraph, ParagraphError, ParagraphPart, ParagraphPartError};
+use crate::xml::element::{Element, FromElements};
+
+/// Variable list.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct VariableList
+{
+ entries: Vec<Entry>,
+}
+
+impl VariableList
+{
+ /// Returns a new `VariableList`.
+ pub fn new(entries: impl IntoIterator<Item = Entry>) -> Self
+ {
+ Self {
+ entries: entries.into_iter().collect(),
+ }
+ }
+
+ /// Returns the variable list entries.
+ #[must_use]
+ pub fn entries(&self) -> &[Entry]
+ {
+ &self.entries
+ }
+}
+
+impl FromElements for VariableList
+{
+ type Error = Error;
+
+ fn from_elements(
+ elements: &crate::xml::element::Elements,
+ ) -> Result<Self, Self::Error>
+ {
+ let entries = elements
+ .get_all_tagged_elements_with_name("varlistentry")
+ .into_iter()
+ .map(|entry_elem| Entry::from_elements(entry_elem.child_elements()))
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Self { entries })
+ }
+}
+
+/// [`VariableList`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Invalid entry.
+ #[error("Invalid entry")]
+ InvalidEntry(#[from] EntryError),
+}
+
+/// Variable list entry.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Entry
+{
+ term: ParagraphPart,
+ item: Vec<Paragraph>,
+}
+
+impl Entry
+{
+ /// Returns a new `Entry`.
+ pub fn new(term: ParagraphPart, item: impl IntoIterator<Item = Paragraph>) -> Self
+ {
+ Self {
+ term,
+ item: item.into_iter().collect(),
+ }
+ }
+
+ /// Returns the variable list entry term.
+ #[must_use]
+ pub fn term(&self) -> &ParagraphPart
+ {
+ &self.term
+ }
+
+ /// Returns the variable list entry item.
+ #[must_use]
+ pub fn item(&self) -> &[Paragraph]
+ {
+ &self.item
+ }
+}
+
+impl FromElements for Entry
+{
+ type Error = EntryError;
+
+ fn from_elements(
+ elements: &crate::xml::element::Elements,
+ ) -> Result<Self, Self::Error>
+ {
+ let term_element = elements
+ .get_first_tagged_with_name("term")
+ .ok_or(Self::Error::MissingTerm)?;
+
+ let term = ParagraphPart::from_elements(term_element.child_elements())
+ .map_err(|err| Self::Error::InvalidTerm(Box::new(err)))?;
+
+ let item_element = elements
+ .get_first_tagged_with_name("listitem")
+ .ok_or(Self::Error::MissingListItem)?;
+
+ let item = item_element
+ .child_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 { term, item })
+ }
+}
+
+/// [`Entry`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum EntryError
+{
+ /// 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::xml::element::{Element, Elements, Tagged};
+
+ #[test]
+ fn variable_list_from_elements_works()
+ {
+ let variable_list = VariableList::from_elements(&Elements::from([
+ Element::Tagged(Tagged::new(
+ &"varlistentry",
+ [
+ Element::Tagged(Tagged::new(
+ &"term",
+ [Element::Text("foobar".to_string())],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"listitem",
+ [Element::Tagged(Tagged::new(
+ &"para",
+ [Element::Text("Hello hello.".to_string())],
+ [],
+ ))],
+ [],
+ )),
+ ],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"varlistentry",
+ [
+ Element::Tagged(Tagged::new(
+ &"term",
+ [Element::Text("Hello there".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(
+ &"varlistentry",
+ [
+ Element::Tagged(Tagged::new(
+ &"term",
+ [Element::Text("There is another".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!(variable_list.entries.len(), 3);
+ }
+
+ #[test]
+ fn entry_from_elements_works()
+ {
+ let entry = Entry::from_elements(&Elements::from([
+ Element::Tagged(Tagged::new(&"term", [Element::Text("Foo".to_string())], [])),
+ Element::Tagged(Tagged::new(
+ &"listitem",
+ [
+ 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!(
+ matches!(entry.term, ParagraphPart::Text(para_part) if para_part == "Foo")
+ );
+
+ assert_eq!(
+ entry.item,
+ vec![
+ Paragraph::new([ParagraphPart::Text("bar".to_string())]),
+ Paragraph::new([ParagraphPart::Text("Hello there.".to_string())]),
+ ]
+ );
+ }
+}