diff options
Diffstat (limited to 'src/paragraph')
-rw-r--r-- | src/paragraph/mod.rs | 61 | ||||
-rw-r--r-- | src/paragraph/part.rs | 247 |
2 files changed, 308 insertions, 0 deletions
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), +} |