Skip to main content

rd_ast/view/
equation.rs

1use super::*;
2
3/// Whether an equation is rendered inline by `\eqn` or as a block by `\deqn`.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[non_exhaustive]
6pub enum RdEquationDisplay {
7    Inline,
8    Block,
9}
10
11/// A borrowed, structurally valid `\eqn{latex}{ascii}` or `\deqn{latex}{ascii}` view.
12#[derive(Debug, Clone, PartialEq)]
13pub struct RdEquation<'a> {
14    path: RdPath,
15    display: RdEquationDisplay,
16    latex: &'a [RdNode],
17    ascii: Option<&'a [RdNode]>,
18}
19
20impl<'a> RdEquation<'a> {
21    pub fn path(&self) -> &RdPath {
22        &self.path
23    }
24    pub fn display(&self) -> RdEquationDisplay {
25        self.display
26    }
27    pub fn latex(&self) -> &'a [RdNode] {
28        self.latex
29    }
30    pub fn ascii(&self) -> Option<&'a [RdNode]> {
31        self.ascii
32    }
33}
34impl RdTagged {
35    /// Strictly inspects a `\eqn` or `\deqn` equation. The first argument is
36    /// LaTeX and the optional second argument is its ASCII/text fallback.
37    pub fn inspect_equation<'a>(
38        &'a self,
39        base_path: &RdPath,
40    ) -> Result<RdEquation<'a>, RdShapeError> {
41        let display = match self.tag() {
42            RdTag::Eqn => RdEquationDisplay::Inline,
43            RdTag::Deqn => RdEquationDisplay::Block,
44            tag => {
45                return Err(shape(
46                    base_path.clone(),
47                    Some(tag.clone()),
48                    RdShapeErrorKind::UnexpectedNode {
49                        expected: RdExpectedNode::Equation,
50                        actual: RdNodeKind::Tagged,
51                    },
52                ));
53            }
54        };
55        if self.option().is_some() {
56            return Err(shape(
57                base_path.clone(),
58                Some(self.tag().clone()),
59                RdShapeErrorKind::UnexpectedOption,
60            ));
61        }
62        let children = self.children();
63        if !(1..=2).contains(&children.len()) {
64            return Err(shape(
65                base_path.clone(),
66                Some(self.tag().clone()),
67                RdShapeErrorKind::WrongArity {
68                    expected: RdArity::Range { min: 1, max: 2 },
69                    actual: children.len(),
70                },
71            ));
72        }
73        for (index, child) in children.iter().enumerate() {
74            if child.as_group().is_none() {
75                return Err(shape(
76                    base_path.with_child(index),
77                    Some(self.tag().clone()),
78                    RdShapeErrorKind::UnexpectedNode {
79                        expected: RdExpectedNode::Group,
80                        actual: RdNodeKind::of(child),
81                    },
82                ));
83            }
84        }
85        Ok(RdEquation {
86            path: base_path.clone(),
87            display,
88            latex: children[0].as_group().unwrap().children(),
89            ascii: children
90                .get(1)
91                .map(|child| child.as_group().unwrap().children()),
92        })
93    }
94}