diff options
| author | HampusM <hampus@hampusmat.com> | 2023-05-16 21:59:46 +0200 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2023-05-16 21:59:46 +0200 | 
| commit | cc30a537284871d668911353bd121e38d0353eb0 (patch) | |
| tree | 121976a44346f5979447d9d9052372246d4ff66c | |
| parent | b336066d6067a0eb9ff9fc34c5aa062b86e56c62 (diff) | |
| -rw-r--r-- | src/description.rs | 550 | ||||
| -rw-r--r-- | src/description/mod.rs | 98 | ||||
| -rw-r--r-- | src/description/part.rs | 104 | ||||
| -rw-r--r-- | src/emphasis.rs | 66 | ||||
| -rw-r--r-- | src/gloss_list.rs | 4 | ||||
| -rw-r--r-- | src/itemized_list.rs | 8 | ||||
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/paragraph/mod.rs | 61 | ||||
| -rw-r--r-- | src/paragraph/part.rs | 247 | ||||
| -rw-r--r-- | src/table.rs | 2 | ||||
| -rw-r--r-- | src/variable_list.rs | 7 | 
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] @@ -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.  | 
