summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-05-16 21:59:46 +0200
committerHampusM <hampus@hampusmat.com>2023-05-16 21:59:46 +0200
commitcc30a537284871d668911353bd121e38d0353eb0 (patch)
tree121976a44346f5979447d9d9052372246d4ff66c
parentb336066d6067a0eb9ff9fc34c5aa062b86e56c62 (diff)
refactor: reorganize structsHEADmaster
-rw-r--r--src/description.rs550
-rw-r--r--src/description/mod.rs98
-rw-r--r--src/description/part.rs104
-rw-r--r--src/emphasis.rs66
-rw-r--r--src/gloss_list.rs4
-rw-r--r--src/itemized_list.rs8
-rw-r--r--src/lib.rs2
-rw-r--r--src/paragraph/mod.rs61
-rw-r--r--src/paragraph/part.rs247
-rw-r--r--src/table.rs2
-rw-r--r--src/variable_list.rs7
11 files changed, 593 insertions, 556 deletions
diff --git a/src/description.rs b/src/description.rs
deleted file mode 100644
index d55737d..0000000
--- a/src/description.rs
+++ /dev/null
@@ -1,550 +0,0 @@
-//! 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};
-use crate::xml::element::{Element, Elements, FromElements, Tagged};
-
-/// Reference entry description.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Description
-{
- for_function: Option<String>,
- parts: Vec<Part>,
-}
-
-impl Description
-{
- /// Returns a new empty `Description`.
- #[must_use]
- pub fn new() -> Self
- {
- Self {
- for_function: None,
- parts: Vec::new(),
- }
- }
-
- /// Returns what function this description is specific for.
- #[must_use]
- pub fn for_function(&self) -> &Option<String>
- {
- &self.for_function
- }
-
- /// Returns the description's parts.
- #[must_use]
- pub fn parts(&self) -> &[Part]
- {
- &self.parts
- }
-}
-
-impl Default for Description
-{
- fn default() -> Self
- {
- Self::new()
- }
-}
-
-impl FromElements for Description
-{
- type Error = Error;
-
- fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
- {
- let for_function =
- elements
- .get_first_tagged_with_name("title")
- .and_then(|title_element| {
- let title_text =
- title_element.child_elements().get_first_text_element()?;
-
- if title_text != "Description for " {
- return None;
- }
-
- let function_element = title_element
- .child_elements()
- .get_first_tagged_with_name("function")?;
-
- function_element
- .child_elements()
- .get_first_text_element()
- .cloned()
- });
-
- let parts = elements
- .get_all_tagged_elements()
- .into_iter()
- .filter_map(|part_elem| match part_elem.name() {
- "para" => Some(
- Paragraph::from_elements(part_elem.child_elements())
- .map(Part::Paragraph)
- .map_err(Self::Error::InvalidParagraph),
- ),
-
- "variablelist" => Some(
- VariableList::from_elements(part_elem.child_elements())
- .map(Part::VariableList)
- .map_err(Self::Error::InvalidVariableList),
- ),
- "programlisting" => Some(Ok(Part::ProgramListing(
- part_elem
- .child_elements()
- .get_first_text_element()
- .cloned()
- .unwrap_or_default(),
- ))),
- "informaltable" => Some(
- Informal::from_elements(part_elem.child_elements())
- .map(Part::InformalTable)
- .map_err(Self::Error::InvalidInformalTable),
- ),
- "itemizedlist" => Some(
- ItemizedList::from_elements(part_elem.child_elements())
- .map(Part::ItemizedList)
- .map_err(Self::Error::InvalidItemizedList),
- ),
- "table" => Some(
- Table::from_elements(part_elem.child_elements())
- .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()))),
- })
- .collect::<Result<Vec<_>, Self::Error>>()?;
-
- Ok(Description {
- for_function,
- parts,
- })
- }
-}
-
-/// [`Description`] error.
-#[derive(Debug, thiserror::Error)]
-pub enum Error
-{
- /// Unknown part element found.
- #[error("Unknown part element with name '{0}' found")]
- UnknownPartFound(String),
-
- /// Invalid paragraph.
- #[error("Invalid paragraph")]
- InvalidParagraph(#[source] ParagraphError),
-
- /// Invalid variable list.
- #[error("Invalid variable list")]
- InvalidVariableList(#[source] VariableListError),
-
- /// Invalid informal table.
- #[error("Invalid informal table")]
- InvalidInformalTable(#[source] TableError),
-
- /// Invalid itemized list.
- #[error("Invalid itemized list")]
- InvalidItemizedList(#[source] ItemizedListError),
-
- /// Invalid table.
- #[error("Invalid table")]
- InvalidTable(#[source] TableError),
-
- /// Invalid gloss list.
- #[error("Invalid gloss list")]
- InvalidGlossList(#[source] GlossListError),
-}
-
-/// Description part.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum Part
-{
- /// Paragraph.
- Paragraph(Paragraph),
-
- /// Variable list.
- VariableList(VariableList),
-
- /// Program listing.
- ProgramListing(String),
-
- /// Informal table.
- InformalTable(Informal),
-
- /// Itemized list.
- ItemizedList(ItemizedList),
-
- /// Table.
- Table(Table),
-
- /// Gloss list.
- GlossList(GlossList),
-}
-
-/// Reference entry description paragraph.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Paragraph
-{
- parts: Vec<ParagraphPart>,
-}
-
-impl Paragraph
-{
- /// Returns a new `Paragraph`.
- pub fn new(parts: impl IntoIterator<Item = ParagraphPart>) -> Self
- {
- Self {
- parts: parts.into_iter().collect(),
- }
- }
-
- /// Returns the parts of the paragraph.
- #[must_use]
- pub fn parts(&self) -> &[ParagraphPart]
- {
- &self.parts
- }
-}
-
-impl FromElements for Paragraph
-{
- type Error = ParagraphError;
-
- fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
- {
- let parts = elements
- .into_iter()
- .filter_map(|element| {
- if matches!(element, Element::Comment(_)) {
- return None;
- }
-
- Some(ParagraphPart::from_elements(&Elements::from([
- element.clone()
- ])))
- })
- .collect::<Result<Vec<_>, _>>()?;
-
- Ok(Self { parts })
- }
-}
-
-/// [`Paragraph`] error.
-#[derive(Debug, thiserror::Error)]
-pub enum ParagraphError
-{
- /// Invalid reference description part.
- #[error("Invalid part")]
- InvalidPart(#[from] ParagraphPartError),
-}
-
-/// Reference entry description paragraph part.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum ParagraphPart
-{
- /// Text part.
- Text(String),
-
- /// <constant>..</constant> part.
- Constant(String),
-
- /// <function>..</function> part.
- Function(String),
-
- /// <parameter>..</parameter> part.
- Parameter(String),
-
- /// Emphasis part.
- Emphasis(Emphasis),
-
- /// Code part.
- Code(String),
-
- /// Inline equation part.
- InlineEquation(String),
-
- /// Program listing part.
- ProgramListing(String),
-
- /// Reference entry citation part.
- Entry(String),
-
- /// Variable list part.
- VariableList(VariableList),
-
- /// Itemized list part.
- ItemizedList(ItemizedList),
-
- /// Informal table part.
- InformalTable(Informal),
-
- /// Paragraph part.
- Paragraph(Paragraph),
-
- /// Footnote part.
- Footnote(Paragraph),
-
- /// Table part.
- Table(Table),
-
- /// Informal equation part.
- InformalEquation(String),
-
- /// Superscript part.
- Superscript(String),
-}
-
-impl FromElements for ParagraphPart
-{
- type Error = ParagraphPartError;
-
- fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
- {
- if let Some(tagged_element) = elements.get_first_tagged() {
- return Self::from_tagged_element(tagged_element);
- }
-
- let text = elements
- .get_first_text_element()
- .ok_or(Self::Error::ExpectedTextElement)?;
-
- Ok(Self::Text(text.clone()))
- }
-}
-
-impl ParagraphPart
-{
- fn from_tagged_element(
- tagged_element: &Tagged,
- ) -> Result<Self, <Self as FromElements>::Error>
- {
- let create: fn(String) -> Self = match tagged_element.name() {
- "constant" => Self::Constant,
- "function" => Self::Function,
- "parameter" => Self::Parameter,
- "code" => Self::Code,
- "inlineequation" => Self::InlineEquation,
- "programlisting" => Self::ProgramListing,
- "citerefentry" => Self::Entry,
- "superscript" => Self::Superscript,
- "variablelist" | "itemizedlist" | "informaltable" | "para" | "footnote"
- | "table" | "informalequation" | "emphasis" => |_| {
- unreachable!();
- },
- _ => {
- return Err(<Self as FromElements>::Error::UnknownPart(
- tagged_element.name().to_string(),
- ));
- }
- };
-
- if tagged_element.name() == "citerefentry" {
- let title_element = tagged_element
- .child_elements()
- .get_first_tagged_with_name("refentrytitle")
- .ok_or(<Self as FromElements>::Error::NoEntryTitleFound)?;
-
- let title = title_element
- .child_elements()
- .get_first_text_element()
- .ok_or(<Self as FromElements>::Error::NoTextInTagged)?;
-
- return Ok(Self::Entry(title.clone()));
- }
-
- if tagged_element.name() == "variablelist" {
- let variable_list =
- VariableList::from_elements(tagged_element.child_elements())?;
-
- return Ok(Self::VariableList(variable_list));
- }
-
- if tagged_element.name() == "itemizedlist" {
- let itemized_list =
- ItemizedList::from_elements(tagged_element.child_elements())?;
-
- return Ok(Self::ItemizedList(itemized_list));
- }
-
- if tagged_element.name() == "informaltable" {
- let informal_table = Informal::from_elements(tagged_element.child_elements())
- .map_err(|err| ParagraphPartError::InvalidInformalTable(Box::new(err)))?;
-
- return Ok(Self::InformalTable(informal_table));
- }
-
- if tagged_element.name() == "inlineequation" {
- return Ok(Self::InlineEquation(
- tagged_element
- .child_elements()
- .into_iter()
- .map(ToString::to_string)
- .collect::<String>(),
- ));
- }
-
- if tagged_element.name() == "para" {
- return Ok(Self::Paragraph(
- Paragraph::from_elements(tagged_element.child_elements())
- .map_err(|err| ParagraphPartError::InvalidParagraph(Box::new(err)))?,
- ));
- }
-
- if tagged_element.name() == "footnote" {
- return Ok(Self::Footnote(
- Paragraph::from_elements(tagged_element.child_elements())
- .map_err(|err| ParagraphPartError::InvalidFootnote(Box::new(err)))?,
- ));
- }
-
- if tagged_element.name() == "table" {
- let table = Table::from_elements(tagged_element.child_elements())
- .map_err(|err| ParagraphPartError::InvalidInformalTable(Box::new(err)))?;
-
- return Ok(Self::Table(table));
- }
-
- if tagged_element.name() == "informalequation" {
- return Ok(Self::InlineEquation(
- tagged_element
- .child_elements()
- .into_iter()
- .map(ToString::to_string)
- .collect::<String>(),
- ));
- }
-
- if tagged_element.name() == "emphasis" {
- return Ok(Self::Emphasis(Emphasis::from_tagged_element(
- tagged_element,
- )?));
- }
-
- let text_element = tagged_element
- .child_elements()
- .get_first_text_element()
- .ok_or(<Self as FromElements>::Error::NoTextInTagged)?;
-
- Ok(create(text_element.clone()))
- }
-}
-
-/// [`ParagraphPart`] error.
-#[derive(Debug, thiserror::Error)]
-pub enum ParagraphPartError
-{
- /// Expected a text element.
- #[error("Expected a text element")]
- ExpectedTextElement,
-
- /// A input element is a unknown paragraph part.
- #[error("Input element with name '{0}' is a unknown paragraph part")]
- UnknownPart(String),
-
- /// No text was found in tagged input element.
- #[error("No text was found in tagged input element")]
- NoTextInTagged,
-
- /// No entry title found.
- #[error("No entry title found")]
- NoEntryTitleFound,
-
- /// Invalid variable list.
- #[error("Invalid variable list")]
- InvalidVariableList(#[from] VariableListError),
-
- /// Invalid itemized list.
- #[error("Invalid itemized list")]
- InvalidItemizedList(#[from] ItemizedListError),
-
- /// Invalid informal table.
- #[error("Invalid informal table")]
- InvalidInformalTable(#[source] Box<TableError>),
-
- /// Invalid paragraph.
- #[error("Invalid paragraph")]
- InvalidParagraph(#[source] Box<ParagraphError>),
-
- /// Invalid footnote.
- #[error("Invalid footnote")]
- InvalidFootnote(#[source] Box<ParagraphError>),
-
- /// Invalid table.
- #[error("Invalid table")]
- InvalidTable(#[source] Box<TableError>),
-
- /// Invalid emphasis.
- #[error("Invalid emphasis")]
- InvalidEmphasis(#[from] EmphasisError),
-}
-
-/// Emphasis.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Emphasis
-{
- /// The emphasised text.
- pub text: String,
-
- /// Emphasis role.
- pub role: EmphasisRole,
-}
-
-impl Emphasis
-{
- fn from_tagged_element(tagged_element: &Tagged) -> Result<Self, EmphasisError>
- {
- return Ok(Emphasis {
- text: tagged_element
- .child_elements()
- .get_first_text_element()
- .cloned()
- .unwrap_or_default(),
- role: tagged_element
- .attributes()
- .iter()
- .find(|attr| attr.key == "role")
- .map(|attr| {
- let value = String::from_utf8(attr.value.clone())
- .map_err(|_| EmphasisError::EmphasisRoleNotUTF8)?;
-
- if value == "bold" {
- return Ok(EmphasisRole::Bold);
- }
-
- Err(EmphasisError::UnknownEmphasisRole(value))
- })
- .unwrap_or(Ok(EmphasisRole::None))?,
- });
- }
-}
-
-/// [`Emphasis`] error.
-#[derive(Debug, thiserror::Error)]
-pub enum EmphasisError
-{
- /// Emphasis role is not valid UTF-8.
- #[error("Emphasis role is not valid UTF-8")]
- EmphasisRoleNotUTF8,
-
- /// Unknown emphasis role.
- #[error("Unknown emphasis role '{0}'")]
- UnknownEmphasisRole(String),
-}
-
-/// Emphasis role.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum EmphasisRole
-{
- /// Bold.
- Bold,
-
- /// None.
- None,
-}
diff --git a/src/description/mod.rs b/src/description/mod.rs
new file mode 100644
index 0000000..a87c1cf
--- /dev/null
+++ b/src/description/mod.rs
@@ -0,0 +1,98 @@
+//! Reference entry description.
+use crate::xml::element::{Elements, FromElements};
+
+mod part;
+
+pub use part::{Error as PartError, Part};
+
+/// Reference entry description.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Description
+{
+ for_function: Option<String>,
+ parts: Vec<Part>,
+}
+
+impl Description
+{
+ /// Returns a new empty `Description`.
+ #[must_use]
+ pub fn new() -> Self
+ {
+ Self {
+ for_function: None,
+ parts: Vec::new(),
+ }
+ }
+
+ /// Returns what function this description is specific for.
+ #[must_use]
+ pub fn for_function(&self) -> &Option<String>
+ {
+ &self.for_function
+ }
+
+ /// Returns the description's parts.
+ #[must_use]
+ pub fn parts(&self) -> &[Part]
+ {
+ &self.parts
+ }
+}
+
+impl Default for Description
+{
+ fn default() -> Self
+ {
+ Self::new()
+ }
+}
+
+impl FromElements for Description
+{
+ type Error = Error;
+
+ fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
+ {
+ let for_function =
+ elements
+ .get_first_tagged_with_name("title")
+ .and_then(|title_element| {
+ let title_text =
+ title_element.child_elements().get_first_text_element()?;
+
+ if title_text != "Description for " {
+ return None;
+ }
+
+ let function_element = title_element
+ .child_elements()
+ .get_first_tagged_with_name("function")?;
+
+ function_element
+ .child_elements()
+ .get_first_text_element()
+ .cloned()
+ });
+
+ let parts = elements
+ .get_all_tagged_elements()
+ .into_iter()
+ .map(Part::from_tagged_element)
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Description {
+ for_function,
+ parts,
+ })
+ }
+}
+
+/// [`Description`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Invalid description part.
+ #[error("Invalid description part")]
+ InvalidPart(#[from] PartError),
+}
diff --git a/src/description/part.rs b/src/description/part.rs
new file mode 100644
index 0000000..2f1771f
--- /dev/null
+++ b/src/description/part.rs
@@ -0,0 +1,104 @@
+use crate::gloss_list::{Error as GlossListError, GlossList};
+use crate::itemized_list::{Error as ItemizedListError, ItemizedList};
+use crate::paragraph::{Error as ParagraphError, Paragraph};
+use crate::table::{Error as TableError, Informal, Table};
+use crate::variable_list::{Error as VariableListError, VariableList};
+use crate::xml::element::{FromElements, Tagged};
+
+/// Description part.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Part
+{
+ /// Paragraph.
+ Paragraph(Paragraph),
+
+ /// Variable list.
+ VariableList(VariableList),
+
+ /// Program listing.
+ ProgramListing(String),
+
+ /// Informal table.
+ InformalTable(Informal),
+
+ /// Itemized list.
+ ItemizedList(ItemizedList),
+
+ /// Table.
+ Table(Table),
+
+ /// Gloss list.
+ GlossList(GlossList),
+}
+
+impl Part
+{
+ pub(crate) fn from_tagged_element(tagged_element: &Tagged) -> Result<Self, Error>
+ {
+ match tagged_element.name() {
+ "para" => Paragraph::from_elements(tagged_element.child_elements())
+ .map(Part::Paragraph)
+ .map_err(Error::InvalidParagraph),
+ "variablelist" => {
+ VariableList::from_elements(tagged_element.child_elements())
+ .map(Part::VariableList)
+ .map_err(Error::InvalidVariableList)
+ }
+ "programlisting" => Ok(Part::ProgramListing(
+ tagged_element
+ .child_elements()
+ .get_first_text_element()
+ .cloned()
+ .unwrap_or_default(),
+ )),
+ "informaltable" => Informal::from_elements(tagged_element.child_elements())
+ .map(Part::InformalTable)
+ .map_err(Error::InvalidInformalTable),
+ "itemizedlist" => {
+ ItemizedList::from_elements(tagged_element.child_elements())
+ .map(Part::ItemizedList)
+ .map_err(Error::InvalidItemizedList)
+ }
+ "table" => Table::from_elements(tagged_element.child_elements())
+ .map(Part::Table)
+ .map_err(Error::InvalidTable),
+ "glosslist" => GlossList::from_elements(tagged_element.child_elements())
+ .map(Part::GlossList)
+ .map_err(Error::InvalidGlossList),
+ name => Err(Error::Unknown(name.to_string())),
+ }
+ }
+}
+
+/// [`Part`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Unknown description part.
+ #[error("Unknown description part '{0}'")]
+ Unknown(String),
+
+ /// Invalid paragraph.
+ #[error("Invalid paragraph")]
+ InvalidParagraph(#[source] ParagraphError),
+
+ /// Invalid variable list.
+ #[error("Invalid variable list")]
+ InvalidVariableList(#[source] VariableListError),
+
+ /// Invalid informal table.
+ #[error("Invalid informal table")]
+ InvalidInformalTable(#[source] TableError),
+
+ /// Invalid itemized list.
+ #[error("Invalid itemized list")]
+ InvalidItemizedList(#[source] ItemizedListError),
+
+ /// Invalid table.
+ #[error("Invalid table")]
+ InvalidTable(#[source] TableError),
+
+ /// Invalid gloss list.
+ #[error("Invalid gloss list")]
+ InvalidGlossList(#[source] GlossListError),
+}
diff --git a/src/emphasis.rs b/src/emphasis.rs
new file mode 100644
index 0000000..12502ad
--- /dev/null
+++ b/src/emphasis.rs
@@ -0,0 +1,66 @@
+//! Emphasis.
+use crate::xml::element::Tagged;
+
+/// Emphasis.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Emphasis
+{
+ /// The emphasised text.
+ pub text: String,
+
+ /// Emphasis role.
+ pub role: Role,
+}
+
+impl Emphasis
+{
+ pub(crate) fn from_tagged_element(tagged_element: &Tagged) -> Result<Self, Error>
+ {
+ return Ok(Emphasis {
+ text: tagged_element
+ .child_elements()
+ .get_first_text_element()
+ .cloned()
+ .unwrap_or_default(),
+ role: tagged_element
+ .attributes()
+ .iter()
+ .find(|attr| attr.key == "role")
+ .map(|attr| {
+ let value = String::from_utf8(attr.value.clone())
+ .map_err(|_| Error::EmphasisRoleNotUTF8)?;
+
+ if value == "bold" {
+ return Ok(Role::Bold);
+ }
+
+ Err(Error::UnknownEmphasisRole(value))
+ })
+ .unwrap_or(Ok(Role::None))?,
+ });
+ }
+}
+
+/// [`Emphasis`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Emphasis role is not valid UTF-8.
+ #[error("Emphasis role is not valid UTF-8")]
+ EmphasisRoleNotUTF8,
+
+ /// Unknown emphasis role.
+ #[error("Unknown emphasis role '{0}'")]
+ UnknownEmphasisRole(String),
+}
+
+/// Emphasis role.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Role
+{
+ /// Bold.
+ Bold,
+
+ /// None.
+ None,
+}
diff --git a/src/gloss_list.rs b/src/gloss_list.rs
index 74cf6f1..1b4febb 100644
--- a/src/gloss_list.rs
+++ b/src/gloss_list.rs
@@ -1,5 +1,5 @@
//! Gloss list.
-use crate::description::{Paragraph, ParagraphError};
+use crate::paragraph::{Error as ParagraphError, Paragraph};
use crate::xml::element::{Element, FromElements};
/// Gloss list.
@@ -150,7 +150,7 @@ mod tests
use pretty_assertions::assert_str_eq;
use super::*;
- use crate::description::ParagraphPart;
+ use crate::paragraph::Part as ParagraphPart;
use crate::xml::element::{Element, Elements, Tagged};
#[test]
diff --git a/src/itemized_list.rs b/src/itemized_list.rs
index efe43b5..36a2252 100644
--- a/src/itemized_list.rs
+++ b/src/itemized_list.rs
@@ -1,5 +1,9 @@
//! Itemized list.
-use crate::description::{Paragraph, ParagraphError, ParagraphPartError};
+use crate::paragraph::{
+ Error as ParagraphError,
+ Paragraph,
+ PartError as ParagraphPartError,
+};
use crate::xml::element::{Element, FromElements};
/// Itemized list.
@@ -128,7 +132,7 @@ pub enum ItemError
mod tests
{
use super::*;
- use crate::description::ParagraphPart;
+ use crate::paragraph::Part as ParagraphPart;
use crate::xml::element::{Element, Elements, Tagged};
#[test]
diff --git a/src/lib.rs b/src/lib.rs
index 9dfc888..e966648 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,8 +14,10 @@ use crate::xml::element::{Elements, FromElements};
use crate::xml::parser::{Error as ParserError, Parser};
pub mod description;
+pub mod emphasis;
pub mod gloss_list;
pub mod itemized_list;
+pub mod paragraph;
pub mod table;
pub mod variable_list;
diff --git a/src/paragraph/mod.rs b/src/paragraph/mod.rs
new file mode 100644
index 0000000..32ab429
--- /dev/null
+++ b/src/paragraph/mod.rs
@@ -0,0 +1,61 @@
+//! Paragraph.
+use crate::xml::element::{Element, Elements, FromElements};
+
+mod part;
+
+pub use part::{Error as PartError, Part};
+
+/// A paragraph.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Paragraph
+{
+ parts: Vec<Part>,
+}
+
+impl Paragraph
+{
+ /// Returns a new `Paragraph`.
+ pub fn new(parts: impl IntoIterator<Item = Part>) -> Self
+ {
+ Self {
+ parts: parts.into_iter().collect(),
+ }
+ }
+
+ /// Returns the parts of the paragraph.
+ #[must_use]
+ pub fn parts(&self) -> &[Part]
+ {
+ &self.parts
+ }
+}
+
+impl FromElements for Paragraph
+{
+ type Error = Error;
+
+ fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
+ {
+ let parts = elements
+ .into_iter()
+ .filter_map(|element| {
+ if matches!(element, Element::Comment(_)) {
+ return None;
+ }
+
+ Some(Part::from_elements(&Elements::from([element.clone()])))
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Self { parts })
+ }
+}
+
+/// [`Paragraph`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Invalid reference description part.
+ #[error("Invalid part")]
+ InvalidPart(#[from] PartError),
+}
diff --git a/src/paragraph/part.rs b/src/paragraph/part.rs
new file mode 100644
index 0000000..5e5a846
--- /dev/null
+++ b/src/paragraph/part.rs
@@ -0,0 +1,247 @@
+use crate::emphasis::{Emphasis, Error as EmphasisError};
+use crate::itemized_list::{Error as ItemizedListError, ItemizedList};
+use crate::paragraph::{Error as ParagraphError, Paragraph};
+use crate::table::{Error as TableError, Informal as InformalTable, Table};
+use crate::variable_list::{Error as VariableListError, VariableList};
+use crate::xml::element::{Elements, FromElements, Tagged};
+
+/// Paragraph part.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Part
+{
+ /// Text part.
+ Text(String),
+
+ /// <constant>..</constant> part.
+ Constant(String),
+
+ /// <function>..</function> part.
+ Function(String),
+
+ /// <parameter>..</parameter> part.
+ Parameter(String),
+
+ /// Emphasis part.
+ Emphasis(Emphasis),
+
+ /// Code part.
+ Code(String),
+
+ /// Inline equation part.
+ InlineEquation(String),
+
+ /// Program listing part.
+ ProgramListing(String),
+
+ /// Reference entry citation part.
+ Entry(String),
+
+ /// Variable list part.
+ VariableList(VariableList),
+
+ /// Itemized list part.
+ ItemizedList(ItemizedList),
+
+ /// Informal table part.
+ InformalTable(InformalTable),
+
+ /// Paragraph part.
+ Paragraph(Paragraph),
+
+ /// Footnote part.
+ Footnote(Paragraph),
+
+ /// Table part.
+ Table(Table),
+
+ /// Informal equation part.
+ InformalEquation(String),
+
+ /// Superscript part.
+ Superscript(String),
+}
+
+impl FromElements for Part
+{
+ type Error = Error;
+
+ fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
+ {
+ if let Some(tagged_element) = elements.get_first_tagged() {
+ return Self::from_tagged_element(tagged_element);
+ }
+
+ let text = elements
+ .get_first_text_element()
+ .ok_or(Self::Error::ExpectedTextElement)?;
+
+ Ok(Self::Text(text.clone()))
+ }
+}
+
+impl Part
+{
+ fn from_tagged_element(
+ tagged_element: &Tagged,
+ ) -> Result<Self, <Self as FromElements>::Error>
+ {
+ let create: fn(String) -> Self = match tagged_element.name() {
+ "constant" => Self::Constant,
+ "function" => Self::Function,
+ "parameter" => Self::Parameter,
+ "code" => Self::Code,
+ "inlineequation" => Self::InlineEquation,
+ "programlisting" => Self::ProgramListing,
+ "citerefentry" => Self::Entry,
+ "superscript" => Self::Superscript,
+ "variablelist" | "itemizedlist" | "informaltable" | "para" | "footnote"
+ | "table" | "informalequation" | "emphasis" => |_| {
+ unreachable!();
+ },
+ _ => {
+ return Err(<Self as FromElements>::Error::UnknownPart(
+ tagged_element.name().to_string(),
+ ));
+ }
+ };
+
+ if tagged_element.name() == "citerefentry" {
+ let title_element = tagged_element
+ .child_elements()
+ .get_first_tagged_with_name("refentrytitle")
+ .ok_or(<Self as FromElements>::Error::NoEntryTitleFound)?;
+
+ let title = title_element
+ .child_elements()
+ .get_first_text_element()
+ .ok_or(<Self as FromElements>::Error::NoTextInTagged)?;
+
+ return Ok(Self::Entry(title.clone()));
+ }
+
+ if tagged_element.name() == "variablelist" {
+ let variable_list =
+ VariableList::from_elements(tagged_element.child_elements())?;
+
+ return Ok(Self::VariableList(variable_list));
+ }
+
+ if tagged_element.name() == "itemizedlist" {
+ let itemized_list =
+ ItemizedList::from_elements(tagged_element.child_elements())?;
+
+ return Ok(Self::ItemizedList(itemized_list));
+ }
+
+ if tagged_element.name() == "informaltable" {
+ let informal_table =
+ InformalTable::from_elements(tagged_element.child_elements())
+ .map_err(|err| Error::InvalidInformalTable(Box::new(err)))?;
+
+ return Ok(Self::InformalTable(informal_table));
+ }
+
+ if tagged_element.name() == "inlineequation" {
+ return Ok(Self::InlineEquation(
+ tagged_element
+ .child_elements()
+ .into_iter()
+ .map(ToString::to_string)
+ .collect::<String>(),
+ ));
+ }
+
+ if tagged_element.name() == "para" {
+ return Ok(Self::Paragraph(
+ Paragraph::from_elements(tagged_element.child_elements())
+ .map_err(|err| Error::InvalidParagraph(Box::new(err)))?,
+ ));
+ }
+
+ if tagged_element.name() == "footnote" {
+ return Ok(Self::Footnote(
+ Paragraph::from_elements(tagged_element.child_elements())
+ .map_err(|err| Error::InvalidFootnote(Box::new(err)))?,
+ ));
+ }
+
+ if tagged_element.name() == "table" {
+ let table = Table::from_elements(tagged_element.child_elements())
+ .map_err(|err| Error::InvalidInformalTable(Box::new(err)))?;
+
+ return Ok(Self::Table(table));
+ }
+
+ if tagged_element.name() == "informalequation" {
+ return Ok(Self::InlineEquation(
+ tagged_element
+ .child_elements()
+ .into_iter()
+ .map(ToString::to_string)
+ .collect::<String>(),
+ ));
+ }
+
+ if tagged_element.name() == "emphasis" {
+ return Ok(Self::Emphasis(Emphasis::from_tagged_element(
+ tagged_element,
+ )?));
+ }
+
+ let text_element = tagged_element
+ .child_elements()
+ .get_first_text_element()
+ .ok_or(<Self as FromElements>::Error::NoTextInTagged)?;
+
+ Ok(create(text_element.clone()))
+ }
+}
+
+/// [`Part`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Expected a text element.
+ #[error("Expected a text element")]
+ ExpectedTextElement,
+
+ /// A input element is a unknown paragraph part.
+ #[error("Input element with name '{0}' is a unknown paragraph part")]
+ UnknownPart(String),
+
+ /// No text was found in tagged input element.
+ #[error("No text was found in tagged input element")]
+ NoTextInTagged,
+
+ /// No entry title found.
+ #[error("No entry title found")]
+ NoEntryTitleFound,
+
+ /// Invalid variable list.
+ #[error("Invalid variable list")]
+ InvalidVariableList(#[from] VariableListError),
+
+ /// Invalid itemized list.
+ #[error("Invalid itemized list")]
+ InvalidItemizedList(#[from] ItemizedListError),
+
+ /// Invalid informal table.
+ #[error("Invalid informal table")]
+ InvalidInformalTable(#[source] Box<TableError>),
+
+ /// Invalid paragraph.
+ #[error("Invalid paragraph")]
+ InvalidParagraph(#[source] Box<ParagraphError>),
+
+ /// Invalid footnote.
+ #[error("Invalid footnote")]
+ InvalidFootnote(#[source] Box<ParagraphError>),
+
+ /// Invalid table.
+ #[error("Invalid table")]
+ InvalidTable(#[source] Box<TableError>),
+
+ /// Invalid emphasis.
+ #[error("Invalid emphasis")]
+ InvalidEmphasis(#[from] EmphasisError),
+}
diff --git a/src/table.rs b/src/table.rs
index b549b95..e3ddd3d 100644
--- a/src/table.rs
+++ b/src/table.rs
@@ -1,5 +1,5 @@
//! Tables.
-use crate::description::{Paragraph, ParagraphError};
+use crate::paragraph::{Error as ParagraphError, Paragraph};
use crate::xml::element::{Elements, FromElements, Tagged};
/// Informal table.
diff --git a/src/variable_list.rs b/src/variable_list.rs
index 2c839f0..4b8cc65 100644
--- a/src/variable_list.rs
+++ b/src/variable_list.rs
@@ -1,6 +1,11 @@
//! Variable list.
-use crate::description::{Paragraph, ParagraphError, ParagraphPart, ParagraphPartError};
+use crate::paragraph::{
+ Error as ParagraphError,
+ Paragraph,
+ Part as ParagraphPart,
+ PartError as ParagraphPartError,
+};
use crate::xml::element::{Element, FromElements};
/// Variable list.