summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-03-01 21:50:13 +0100
committerHampusM <hampus@hampusmat.com>2023-03-01 21:50:13 +0100
commit656ca92bc5958752d6a12e2a659fe17809fe5e86 (patch)
treecdfbc60fe24e865a46b800ff3506db6a6640283f /src
parentb1424312a932f345f86a93aba662add645fcf921 (diff)
feat: add informal table support
Diffstat (limited to 'src')
-rw-r--r--src/description.rs31
-rw-r--r--src/informal_table.rs347
-rw-r--r--src/lib.rs1
-rw-r--r--src/xml/element.rs5
4 files changed, 383 insertions, 1 deletions
diff --git a/src/description.rs b/src/description.rs
index f6e0f70..5d0cb1f 100644
--- a/src/description.rs
+++ b/src/description.rs
@@ -1,4 +1,5 @@
//! Reference entry description.
+use crate::informal_table::{Error as InformalTableError, InformalTable};
use crate::itemized_list::{Error as ItemizedListError, ItemizedList};
use crate::variable_list::{Error as VariableListError, VariableList};
use crate::xml::element::{Element, Elements, FromElements, Tagged};
@@ -95,6 +96,11 @@ impl FromElements for Description
.cloned()
.unwrap_or_default(),
))),
+ "informaltable" => Some(
+ InformalTable::from_elements(part_elem.child_elements())
+ .map(Part::InformalTable)
+ .map_err(Self::Error::InvalidInformalTable),
+ ),
"title" => None,
name => Some(Err(Self::Error::UnknownPartFound(name.to_string()))),
})
@@ -122,6 +128,10 @@ pub enum Error
/// Invalid variable list.
#[error("Invalid variable list")]
InvalidVariableList(#[source] VariableListError),
+
+ /// Invalid informal table.
+ #[error("Invalid informal table")]
+ InvalidInformalTable(#[source] InformalTableError),
}
/// Description part.
@@ -136,6 +146,9 @@ pub enum Part
/// Program listing.
ProgramListing(String),
+
+ /// Informal table.
+ InformalTable(InformalTable),
}
/// Reference entry description paragraph.
@@ -231,6 +244,9 @@ pub enum ParagraphPart
/// Itemized list part.
ItemizedList(ItemizedList),
+
+ /// Informal table part.
+ InformalTable(InformalTable),
}
impl FromElements for ParagraphPart
@@ -266,7 +282,7 @@ impl ParagraphPart
"inlineequation" => Self::InlineEquation,
"programlisting" => Self::ProgramListing,
"citerefentry" => Self::Entry,
- "variablelist" | "itemizedlist" => |_| {
+ "variablelist" | "itemizedlist" | "informaltable" => |_| {
unreachable!();
},
_ => {
@@ -304,6 +320,15 @@ impl ParagraphPart
return Ok(Self::ItemizedList(itemized_list));
}
+ if tagged_element.name() == "informaltable" {
+ let informal_table = InformalTable::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
@@ -350,4 +375,8 @@ pub enum ParagraphPartError
/// Invalid itemized list.
#[error("Invalid itemized list")]
InvalidItemizedList(#[from] ItemizedListError),
+
+ /// Invalid informal table.
+ #[error("Invalid informal table")]
+ InvalidInformalTable(#[source] Box<InformalTableError>),
}
diff --git a/src/informal_table.rs b/src/informal_table.rs
new file mode 100644
index 0000000..bd633fc
--- /dev/null
+++ b/src/informal_table.rs
@@ -0,0 +1,347 @@
+//! Informal table.
+use crate::description::{Paragraph, ParagraphError};
+use crate::xml::element::{Elements, FromElements, Tagged};
+
+/// Informal table.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct InformalTable
+{
+ groups: Vec<TableGroup>,
+}
+
+impl InformalTable
+{
+ /// Returns the table groups.
+ #[must_use]
+ pub fn groups(&self) -> &[TableGroup]
+ {
+ &self.groups
+ }
+}
+
+impl FromElements for InformalTable
+{
+ type Error = Error;
+
+ fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
+ {
+ let groups = elements
+ .get_all_tagged_elements_with_name("tgroup")
+ .into_iter()
+ .map(TableGroup::from_tagged_element)
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Self { groups })
+ }
+}
+
+/// [`InformalTable`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ /// Invalid group.
+ #[error("Invalid group")]
+ InvalidGroup(#[from] TableGroupError),
+}
+
+/// Table group
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TableGroup
+{
+ cols: u32,
+ align: String,
+ column_specs: Vec<ColumnSpec>,
+ head: Vec<TableRow>,
+ body: Vec<TableRow>,
+}
+
+impl TableGroup
+{
+ /// Returns the column count.
+ #[must_use]
+ pub fn cols(&self) -> u32
+ {
+ self.cols
+ }
+
+ /// Returns the column alignment.
+ #[must_use]
+ pub fn align(&self) -> &str
+ {
+ &self.align
+ }
+
+ /// Returns the column specifications.
+ #[must_use]
+ pub fn column_specs(&self) -> &[ColumnSpec]
+ {
+ &self.column_specs
+ }
+
+ /// Returns the head.
+ #[must_use]
+ pub fn head(&self) -> &[TableRow]
+ {
+ &self.head
+ }
+
+ /// Returns the body.
+ #[must_use]
+ pub fn body(&self) -> &[TableRow]
+ {
+ &self.body
+ }
+}
+
+impl TableGroup
+{
+ fn from_tagged_element(element: &Tagged) -> Result<Self, TableGroupError>
+ {
+ let cols = String::from_utf8(
+ element
+ .attributes()
+ .iter()
+ .find(|attr| attr.key == "cols")
+ .ok_or(TableGroupError::MissingCols)?
+ .value
+ .clone(),
+ )
+ .map_err(|_| TableGroupError::ColsNotUTF8)?
+ .parse::<u32>()
+ .map_err(|_| TableGroupError::ColsNotNumber)?;
+
+ let align = String::from_utf8(
+ element
+ .attributes()
+ .iter()
+ .find(|attr| attr.key == "align")
+ .ok_or(TableGroupError::MissingAlign)?
+ .value
+ .clone(),
+ )
+ .map_err(|_| TableGroupError::AlignNotUTF8)?;
+
+ let column_specs = element
+ .child_elements()
+ .get_all_tagged_elements_with_name("colspec")
+ .into_iter()
+ .map(|column_spec_element| {
+ ColumnSpec::from_tagged_element(column_spec_element)
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let head = element
+ .child_elements()
+ .get_first_tagged_with_name("thead")
+ .ok_or(TableGroupError::MissingHead)?
+ .child_elements()
+ .get_all_tagged_elements_with_name("row")
+ .into_iter()
+ .map(|row_element| TableRow::from_elements(row_element.child_elements()))
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let body = element
+ .child_elements()
+ .get_first_tagged_with_name("tbody")
+ .ok_or(TableGroupError::MissingBody)?
+ .child_elements()
+ .get_all_tagged_elements_with_name("row")
+ .into_iter()
+ .map(|row_element| TableRow::from_elements(row_element.child_elements()))
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Self {
+ cols,
+ align,
+ column_specs,
+ head,
+ body,
+ })
+ }
+}
+
+/// [`TableGroup`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum TableGroupError
+{
+ /// Missing cols element attribute.
+ #[error("Missing cols")]
+ MissingCols,
+
+ /// Missing align element attribute.
+ #[error("Missing align")]
+ MissingAlign,
+
+ /// Invalid column specification.
+ #[error("Invalid column specification")]
+ InvalidColumnSpec(#[from] ColumnSpecError),
+
+ /// Missing head element.
+ #[error("Missing head")]
+ MissingHead,
+
+ /// Missing body element.
+ #[error("Missing body")]
+ MissingBody,
+
+ /// Invalid row.
+ #[error("Invalid row")]
+ InvalidRow(#[from] TableRowError),
+
+ /// Cols is not valid UTF-8.
+ #[error("Cols is not valid UTF-8")]
+ ColsNotUTF8,
+
+ /// Cols is not a number
+ #[error("Cols is not a number")]
+ ColsNotNumber,
+
+ /// Align is not valid UTF-8.
+ #[error("Align is not valid UTF-8")]
+ AlignNotUTF8,
+}
+
+/// Column specification.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ColumnSpec
+{
+ width: String,
+ num: Option<u32>,
+ name: Option<String>,
+}
+
+impl ColumnSpec
+{
+ /// Returns the width.
+ #[must_use]
+ pub fn width(&self) -> &str
+ {
+ &self.width
+ }
+
+ /// Returns the number.
+ #[must_use]
+ pub fn num(&self) -> &Option<u32>
+ {
+ &self.num
+ }
+
+ /// Returns the name.
+ #[must_use]
+ pub fn name(&self) -> Option<&str>
+ {
+ self.name.as_deref()
+ }
+}
+
+impl ColumnSpec
+{
+ fn from_tagged_element(element: &Tagged) -> Result<Self, ColumnSpecError>
+ {
+ let width = String::from_utf8(
+ element
+ .attributes()
+ .iter()
+ .find(|attr| attr.key == "colwidth")
+ .ok_or(ColumnSpecError::MissingWidth)?
+ .value
+ .clone(),
+ )
+ .map_err(|_| ColumnSpecError::WidthNotUTF8)?;
+
+ let num_result = element
+ .attributes()
+ .iter()
+ .find(|attr| attr.key == "colnum")
+ .map(|attr| String::from_utf8(attr.value.clone()));
+
+ let num = match num_result {
+ Some(num) => Some(
+ num.map_err(|_| ColumnSpecError::NumNotUTF8)?
+ .parse::<u32>()
+ .map_err(|_| ColumnSpecError::NumNotNumber)?,
+ ),
+ None => None,
+ };
+
+ let name_result = element
+ .attributes()
+ .iter()
+ .find(|attr| attr.key == "colname")
+ .map(|attr| String::from_utf8(attr.value.clone()));
+
+ let name = match name_result {
+ Some(name) => Some(name.map_err(|_| ColumnSpecError::NameNotUTF8)?),
+ None => None,
+ };
+
+ Ok(Self { width, num, name })
+ }
+}
+
+/// [`ColumnSpec`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum ColumnSpecError
+{
+ /// Missing width element.
+ #[error("Missing width")]
+ MissingWidth,
+
+ /// Width is not valid UTF-8.
+ #[error("Width is not valid UTF-8")]
+ WidthNotUTF8,
+
+ /// Num is not valid UTF-8.
+ #[error("Num is not valid UTF-8")]
+ NumNotUTF8,
+
+ /// Name is not valid UTF-8.
+ #[error("Name is not valid UTF-8")]
+ NameNotUTF8,
+
+ /// Num is not a number.
+ #[error("Num is not a number")]
+ NumNotNumber,
+}
+
+/// Table row.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TableRow
+{
+ entries: Vec<Paragraph>,
+}
+
+impl TableRow
+{
+ /// Returns the entries.
+ #[must_use]
+ pub fn entries(&self) -> &[Paragraph]
+ {
+ &self.entries
+ }
+}
+
+impl FromElements for TableRow
+{
+ type Error = TableRowError;
+
+ fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
+ {
+ let entries = elements
+ .get_all_tagged_elements_with_name("entry")
+ .into_iter()
+ .map(|entry_element| Paragraph::from_elements(entry_element.child_elements()))
+ .collect::<Result<Vec<_>, _>>()?;
+
+ Ok(Self { entries })
+ }
+}
+
+/// [`TableRow`] error.
+#[derive(Debug, thiserror::Error)]
+pub enum TableRowError
+{
+ /// Invalid paragraph.
+ #[error("Invalid paragraph")]
+ InvalidParagraph(#[from] ParagraphError),
+}
diff --git a/src/lib.rs b/src/lib.rs
index 7a715f1..fddc1fb 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 informal_table;
pub mod itemized_list;
pub mod variable_list;
diff --git a/src/xml/element.rs b/src/xml/element.rs
index 0f44182..db2878b 100644
--- a/src/xml/element.rs
+++ b/src/xml/element.rs
@@ -216,6 +216,11 @@ impl Tagged
&self.name
}
+ pub fn attributes(&self) -> &[Attribute]
+ {
+ &self.attributes
+ }
+
pub fn child_elements(&self) -> &Elements
{
&self.child_elements