Skip to main content

rd_ast/view/
conditional.rs

1use super::*;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4#[non_exhaustive]
5pub enum RdConditionalKind {
6    If,
7    IfElse,
8}
9
10#[derive(Debug, Clone, PartialEq)]
11pub struct RdConditional<'a> {
12    path: RdPath,
13    kind: RdConditionalKind,
14    format_nodes: &'a [RdNode],
15    format: String,
16    then_branch: &'a [RdNode],
17    else_branch: Option<&'a [RdNode]>,
18}
19
20impl<'a> RdConditional<'a> {
21    pub fn path(&self) -> &RdPath {
22        &self.path
23    }
24    pub fn kind(&self) -> RdConditionalKind {
25        self.kind
26    }
27    pub fn format_nodes(&self) -> &'a [RdNode] {
28        self.format_nodes
29    }
30    /// An exact Text-leaf scalar; branch selection is consumer policy.
31    pub fn format(&self) -> &str {
32        &self.format
33    }
34    pub fn then_branch(&self) -> &'a [RdNode] {
35        self.then_branch
36    }
37    pub fn else_branch(&self) -> Option<&'a [RdNode]> {
38        self.else_branch
39    }
40}
41
42fn kind(tag: &RdTag) -> Option<RdConditionalKind> {
43    match tag {
44        RdTag::If => Some(RdConditionalKind::If),
45        RdTag::IfElse => Some(RdConditionalKind::IfElse),
46        _ => None,
47    }
48}
49
50impl RdNode {
51    pub fn conditional(&self, base_path: &RdPath) -> Option<RdConditional<'_>> {
52        self.inspect_conditional(base_path).ok().flatten()
53    }
54
55    pub fn inspect_conditional(
56        &self,
57        base_path: &RdPath,
58    ) -> Result<Option<RdConditional<'_>>, RdShapeError> {
59        let tagged = match self {
60            RdNode::Tagged(tagged) => tagged,
61            RdNode::Raw(raw) => {
62                let Some(tag) = raw.tag().map(RdTag::from_rd_tag) else {
63                    return Ok(None);
64                };
65                if kind(&tag).is_some() {
66                    return Err(shape(
67                        base_path.clone(),
68                        Some(tag),
69                        RdShapeErrorKind::UnexpectedNode {
70                            expected: RdExpectedNode::Tagged,
71                            actual: RdNodeKind::Raw,
72                        },
73                    ));
74                }
75                return Ok(None);
76            }
77            _ => return Ok(None),
78        };
79        let Some(kind) = kind(tagged.tag()) else {
80            return Ok(None);
81        };
82        if tagged.option().is_some() {
83            return Err(shape(
84                base_path.clone(),
85                Some(tagged.tag().clone()),
86                RdShapeErrorKind::UnexpectedOption,
87            ));
88        }
89        let expected = match kind {
90            RdConditionalKind::If => 2,
91            RdConditionalKind::IfElse => 3,
92        };
93        let children = tagged.children();
94        if children.len() != expected {
95            return Err(shape(
96                base_path.clone(),
97                Some(tagged.tag().clone()),
98                RdShapeErrorKind::WrongArity {
99                    expected: RdArity::Exactly(expected),
100                    actual: children.len(),
101                },
102            ));
103        }
104        for (index, child) in children.iter().enumerate() {
105            if child.as_group().is_none() {
106                return Err(shape(
107                    base_path.with_child(index),
108                    Some(tagged.tag().clone()),
109                    RdShapeErrorKind::UnexpectedNode {
110                        expected: RdExpectedNode::Group,
111                        actual: RdNodeKind::of(child),
112                    },
113                ));
114            }
115        }
116        let format_nodes = children[0].as_group().unwrap().children();
117        let format = concat_exact(
118            format_nodes,
119            RdNodeKind::Text,
120            &base_path.with_child(0),
121            tagged.tag(),
122        )?;
123        Ok(Some(RdConditional {
124            path: base_path.clone(),
125            kind,
126            format_nodes,
127            format,
128            then_branch: children[1].as_group().unwrap().children(),
129            else_branch: (kind == RdConditionalKind::IfElse)
130                .then(|| children[2].as_group().unwrap().children()),
131        }))
132    }
133}