//! Reference entry description. use crate::util::enum_with_get_inner; use crate::xml::element::{Elements, FromElements, Tagged}; /// Reference entry description. #[derive(Debug)] pub struct Description { paragraphs: Vec, } impl Description { /// Returns a new `ReferenceDescription`. #[must_use] pub fn new() -> Self { Self { paragraphs: Vec::new(), } } /// Returns the reference description's paragraphs. #[must_use] pub fn paragraphs(&self) -> &[Paragraph] { &self.paragraphs } } impl Default for Description { fn default() -> Self { Self::new() } } impl FromElements for Description { type Error = Error; fn from_elements(elements: &Elements) -> Result { let paragraphs = elements .get_all_tagged_elements_with_name("para") .into_iter() .map(|paragraph_element| { Paragraph::from_elements(paragraph_element.child_elements()) }) .collect::, _>>()?; Ok(Description { paragraphs }) } } /// [`Description`] error. #[derive(Debug, thiserror::Error)] pub enum Error { /// Invalid paragraph. #[error("Invalid paragraph")] InvalidParagraph(#[from] ParagraphError), } /// Reference entry description paragraph. #[derive(Debug)] pub struct Paragraph { parts: Vec, } impl Paragraph { /// 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 { let parts = elements .into_iter() .map(|element| { ParagraphPart::from_elements(&Elements::from([element.clone()])) }) .collect::, _>>()?; Ok(Self { parts }) } } /// [`Paragraph`] error. #[derive(Debug, thiserror::Error)] pub enum ParagraphError { /// Invalid reference description part. #[error("Invalid part")] InvalidPart(#[from] ParagraphPartError), } enum_with_get_inner! { inner = String; /// Reference entry description paragraph part. #[derive(Debug)] pub enum ParagraphPart { /// Text part. Text(String), /// .. part. Constant(String), /// .. part. Function(String), /// .. part. Parameter(String), /// Reference entry citation part. Entry(String), } } impl FromElements for ParagraphPart { type Error = ParagraphPartError; fn from_elements(elements: &Elements) -> Result { 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::InputIsComment)?; Ok(Self::Text(text.clone())) } } impl ParagraphPart { fn from_tagged_element( tagged_element: &Tagged, ) -> Result::Error> { let create: fn(String) -> Self = match tagged_element.name() { "constant" => Self::Constant, "function" => Self::Function, "parameter" => Self::Parameter, "citerefentry" => Self::Entry, _ => { return Err(::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(::Error::NoEntryTitleFound)?; let title = title_element .child_elements() .get_first_text_element() .ok_or(::Error::NoTextInTagged)?; return Ok(Self::Entry(title.clone())); } let text_element = tagged_element .child_elements() .get_first_text_element() .ok_or(::Error::NoTextInTagged)?; Ok(create(text_element.clone())) } } /// [`ParagraphPart`] error. #[derive(Debug, thiserror::Error)] pub enum ParagraphPartError { /// Input element is a comment. #[error("Input element is a comment")] InputIsComment, /// A input element is a unknown reference description part. #[error("Input element with name '{0}' is a unknown reference description 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, }