summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-03-04 15:01:38 +0100
committerHampusM <hampus@hampusmat.com>2023-03-04 15:01:38 +0100
commitc49f62a8c369e58ee5cbffe853a436e724789d2f (patch)
treea219e8377a3cac00990cebaa9b19507a48b64b2e /src
parentdcf34eff5651294d7c579431143b8638fbe338df (diff)
feat: add gloss list support
Diffstat (limited to 'src')
-rw-r--r--src/description.rs13
-rw-r--r--src/gloss_list.rs267
-rw-r--r--src/lib.rs1
3 files changed, 281 insertions, 0 deletions
diff --git a/src/description.rs b/src/description.rs
index 92af9c1..4f3b08a 100644
--- a/src/description.rs
+++ b/src/description.rs
@@ -1,4 +1,5 @@
//! Reference entry description.
+use crate::gloss_list::{Error as GlossListError, GlossList};
use crate::itemized_list::{Error as ItemizedListError, ItemizedList};
use crate::table::{Error as TableError, Informal, Table};
use crate::variable_list::{Error as VariableListError, VariableList};
@@ -111,6 +112,11 @@ impl FromElements for Description
.map(Part::Table)
.map_err(Self::Error::InvalidTable),
),
+ "glosslist" => Some(
+ GlossList::from_elements(part_elem.child_elements())
+ .map(Part::GlossList)
+ .map_err(Self::Error::InvalidGlossList),
+ ),
"title" => None,
name => Some(Err(Self::Error::UnknownPartFound(name.to_string()))),
})
@@ -150,6 +156,10 @@ pub enum Error
/// Invalid table.
#[error("Invalid table")]
InvalidTable(#[source] TableError),
+
+ /// Invalid gloss list.
+ #[error("Invalid gloss list")]
+ InvalidGlossList(#[source] GlossListError),
}
/// Description part.
@@ -173,6 +183,9 @@ pub enum Part
/// Table.
Table(Table),
+
+ /// Gloss list.
+ GlossList(GlossList),
}
/// Reference entry description paragraph.
diff --git a/src/gloss_list.rs b/src/gloss_list.rs
new file mode 100644
index 0000000..74cf6f1
--- /dev/null
+++ b/src/gloss_list.rs
@@ -0,0 +1,267 @@
+//! Gloss list.
+use crate::description::{Paragraph, ParagraphError};
+use crate::xml::element::{Element, FromElements};
+
+/// Gloss list.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GlossList
+{
+ entries: Vec<Entry>,
+}
+
+impl GlossList
+{
+ /// Returns a new `GlossList`.
+ pub fn new(entries: impl IntoIterator<Item = Entry>) -> Self
+ {
+ Self {
+ entries: entries.into_iter().collect(),
+ }
+ }
+
+ /// Returns the gloss list entries.
+ #[must_use]
+ pub fn entries(&self) -> &[Entry]
+ {
+ &self.entries
+ }
+}
+
+impl FromElements for GlossList
+{
+ type Error = Error;
+
+ fn from_elements(
+ elements: &crate::xml::element::Elements,
+ ) -> Result<Self, Self::Error>
+ {
+ let entries = elements
+ .get_all_tagged_elements_with_name("glossentry")
+ .into_iter()
+ .map(|entry_elem| Entry::from_elements(entry_elem.child_elements()))
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Self { entries })
+ }
+}
+
+/// [`GlossList`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Invalid entry.
+ #[error("Invalid entry")]
+ InvalidEntry(#[from] EntryError),
+}
+
+/// Gloss list entry.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Entry
+{
+ term: String,
+ definition: Vec<Paragraph>,
+}
+
+impl Entry
+{
+ /// Returns a new `Entry`.
+ pub fn new(term: String, definition: impl IntoIterator<Item = Paragraph>) -> Self
+ {
+ Self {
+ term,
+ definition: definition.into_iter().collect(),
+ }
+ }
+
+ /// Returns the gloss list entry term.
+ #[must_use]
+ pub fn term(&self) -> &str
+ {
+ &self.term
+ }
+
+ /// Returns the gloss list entry definition.
+ #[must_use]
+ pub fn definition(&self) -> &[Paragraph]
+ {
+ &self.definition
+ }
+}
+
+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("glossterm")
+ .ok_or(Self::Error::MissingTerm)?;
+
+ let term = term_element
+ .child_elements()
+ .get_first_text_element()
+ .cloned()
+ .unwrap_or_default();
+
+ let def_element = elements
+ .get_first_tagged_with_name("glossdef")
+ .ok_or(Self::Error::MissingDefinition)?;
+
+ let definition = def_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::InvalidDefinitionParagraph(Box::new(err)))?;
+
+ Ok(Self { term, definition })
+ }
+}
+
+/// [`Entry`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum EntryError
+{
+ /// Missing term.
+ #[error("Missing term")]
+ MissingTerm,
+
+ /// Missing definition.
+ #[error("Missing definition")]
+ MissingDefinition,
+
+ /// Invalid definition paragraph.
+ #[error("Invalid definition paragraph")]
+ InvalidDefinitionParagraph(#[source] Box<ParagraphError>),
+}
+
+#[cfg(test)]
+mod tests
+{
+ use pretty_assertions::assert_str_eq;
+
+ use super::*;
+ use crate::description::ParagraphPart;
+ use crate::xml::element::{Element, Elements, Tagged};
+
+ #[test]
+ fn gloss_list_from_elements_works()
+ {
+ let gloss_list = GlossList::from_elements(&Elements::from([
+ Element::Tagged(Tagged::new(
+ &"glossentry",
+ [
+ Element::Tagged(Tagged::new(
+ &"glossterm",
+ [Element::Text("foobar".to_string())],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"glossdef",
+ [Element::Tagged(Tagged::new(
+ &"para",
+ [Element::Text("Hello hello.".to_string())],
+ [],
+ ))],
+ [],
+ )),
+ ],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"glossentry",
+ [
+ Element::Tagged(Tagged::new(
+ &"glossterm",
+ [Element::Text("Hello there".to_string())],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"glossdef",
+ [Element::Tagged(Tagged::new(
+ &"para",
+ [
+ Element::Text("Tosche station".to_string()),
+ Element::Text("Power converters".to_string()),
+ ],
+ [],
+ ))],
+ [],
+ )),
+ ],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"glossentry",
+ [
+ Element::Tagged(Tagged::new(
+ &"glossterm",
+ [Element::Text("There is another".to_string())],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"glossdef",
+ [Element::Tagged(Tagged::new(
+ &"para",
+ [Element::Text("It's a trap".to_string())],
+ [],
+ ))],
+ [],
+ )),
+ ],
+ [],
+ )),
+ ]))
+ .expect("Expected Ok");
+
+ assert_eq!(gloss_list.entries.len(), 3);
+ }
+
+ #[test]
+ fn entry_from_elements_works()
+ {
+ let entry = Entry::from_elements(&Elements::from([
+ Element::Tagged(Tagged::new(
+ &"glossterm",
+ [Element::Text("Foo".to_string())],
+ [],
+ )),
+ Element::Tagged(Tagged::new(
+ &"glossdef",
+ [
+ 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_str_eq!(entry.term, "Foo");
+
+ assert_eq!(
+ entry.definition,
+ vec![
+ Paragraph::new([ParagraphPart::Text("bar".to_string())]),
+ Paragraph::new([ParagraphPart::Text("Hello there.".to_string())]),
+ ]
+ );
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index c02716f..9dfc888 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,6 +14,7 @@ use crate::xml::element::{Elements, FromElements};
use crate::xml::parser::{Error as ParserError, Parser};
pub mod description;
+pub mod gloss_list;
pub mod itemized_list;
pub mod table;
pub mod variable_list;