Skip to main content

rd_ast/view/
list.rs

1use super::*;
2
3#[derive(Debug, Clone, PartialEq)]
4pub struct RdList<'a> {
5    path: RdPath,
6    kind: RdListKind,
7    children: &'a [RdNode],
8}
9
10impl<'a> RdList<'a> {
11    pub fn path(&self) -> &RdPath {
12        &self.path
13    }
14    pub fn kind(&self) -> RdListKind {
15        self.kind
16    }
17    pub fn children(&self) -> &'a [RdNode] {
18        self.children
19    }
20
21    pub fn items(&self) -> impl Iterator<Item = Result<RdListItem<'a>, RdShapeError>> + '_ {
22        ListItems {
23            list: self,
24            index: 0,
25        }
26    }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[non_exhaustive]
31pub enum RdListKind {
32    Itemize,
33    Enumerate,
34    Describe,
35}
36
37#[derive(Debug, Clone, PartialEq)]
38#[non_exhaustive]
39pub enum RdListItem<'a> {
40    Delimited(RdDelimitedItem<'a>),
41    Described(RdDescribedItem<'a>),
42}
43
44#[derive(Debug, Clone, PartialEq)]
45pub struct RdDelimitedItem<'a> {
46    path: RdPath,
47    body: &'a [RdNode],
48}
49
50impl<'a> RdDelimitedItem<'a> {
51    pub fn path(&self) -> &RdPath {
52        &self.path
53    }
54    pub fn body(&self) -> &'a [RdNode] {
55        self.body
56    }
57}
58
59#[derive(Debug, Clone, PartialEq)]
60pub struct RdDescribedItem<'a> {
61    path: RdPath,
62    label: &'a [RdNode],
63    body: &'a [RdNode],
64}
65
66impl<'a> RdDescribedItem<'a> {
67    pub fn path(&self) -> &RdPath {
68        &self.path
69    }
70    pub fn label(&self) -> &'a [RdNode] {
71        self.label
72    }
73    pub fn body(&self) -> &'a [RdNode] {
74        self.body
75    }
76}
77impl RdTagged {
78    /// Strictly inspects a single list container. Itemize and enumerate use
79    /// zero-child item markers; describe uses two positional groups per item.
80    pub fn inspect_list<'a>(&'a self, base_path: &RdPath) -> Result<RdList<'a>, RdShapeError> {
81        let kind = match self.tag() {
82            RdTag::Itemize => RdListKind::Itemize,
83            RdTag::Enumerate => RdListKind::Enumerate,
84            RdTag::Describe => RdListKind::Describe,
85            tag => {
86                return Err(shape(
87                    base_path.clone(),
88                    Some(tag.clone()),
89                    RdShapeErrorKind::UnexpectedNode {
90                        expected: RdExpectedNode::List,
91                        actual: RdNodeKind::Tagged,
92                    },
93                ));
94            }
95        };
96        if self.option().is_some() {
97            return Err(shape(
98                base_path.clone(),
99                Some(self.tag().clone()),
100                RdShapeErrorKind::UnexpectedOption,
101            ));
102        }
103        Ok(RdList {
104            path: base_path.clone(),
105            kind,
106            children: self.children(),
107        })
108    }
109}
110
111struct ListItems<'list, 'a> {
112    list: &'list RdList<'a>,
113    index: usize,
114}
115
116impl<'list, 'a> Iterator for ListItems<'list, 'a> {
117    type Item = Result<RdListItem<'a>, RdShapeError>;
118
119    fn next(&mut self) -> Option<Self::Item> {
120        if self.list.kind == RdListKind::Describe {
121            while self.index < self.list.children.len() {
122                let index = self.index;
123                self.index += 1;
124                let node = &self.list.children[index];
125                if is_inter_item_trivia(node) {
126                    continue;
127                }
128                let path = self.list.path.with_child(index);
129                let Some(tagged) = node.as_tagged() else {
130                    return Some(Err(shape(
131                        path,
132                        node_tag(node),
133                        RdShapeErrorKind::UnexpectedContent {
134                            actual: RdNodeKind::of(node),
135                        },
136                    )));
137                };
138                if tagged.tag() != &RdTag::Item {
139                    return Some(Err(shape(
140                        path,
141                        Some(tagged.tag().clone()),
142                        RdShapeErrorKind::UnexpectedContent {
143                            actual: RdNodeKind::Tagged,
144                        },
145                    )));
146                }
147                return Some(inspect_two_group_item(tagged, &path).map(|(label, body)| {
148                    RdListItem::Described(RdDescribedItem { path, label, body })
149                }));
150            }
151            return None;
152        }
153
154        while self.index < self.list.children.len() {
155            let index = self.index;
156            let node = &self.list.children[index];
157            if is_inter_item_trivia(node) {
158                self.index += 1;
159                continue;
160            }
161            let path = self.list.path.with_child(index);
162            let Some(tagged) = node.as_tagged() else {
163                self.index += 1;
164                return Some(Err(shape(
165                    path,
166                    node_tag(node),
167                    RdShapeErrorKind::UnexpectedContent {
168                        actual: RdNodeKind::of(node),
169                    },
170                )));
171            };
172            if tagged.tag() != &RdTag::Item {
173                self.index += 1;
174                return Some(Err(shape(
175                    path,
176                    Some(tagged.tag().clone()),
177                    RdShapeErrorKind::UnexpectedContent {
178                        actual: RdNodeKind::Tagged,
179                    },
180                )));
181            }
182            self.index += 1;
183            let body_start = self.index;
184            while self.index < self.list.children.len() {
185                let candidate = &self.list.children[self.index];
186                if candidate
187                    .as_tagged()
188                    .is_some_and(|item| item.tag() == &RdTag::Item)
189                {
190                    break;
191                }
192                self.index += 1;
193            }
194            let body = &self.list.children[body_start..self.index];
195            if tagged.option().is_some() {
196                return Some(Err(shape(
197                    path,
198                    Some(RdTag::Item),
199                    RdShapeErrorKind::UnexpectedOption,
200                )));
201            }
202            if !tagged.children().is_empty() {
203                // Delimiter markers must have no children; this deliberately
204                // reports Exactly(0) while retaining the marker boundary.
205                return Some(Err(shape(
206                    path,
207                    Some(RdTag::Item),
208                    RdShapeErrorKind::WrongArity {
209                        expected: RdArity::Exactly(0),
210                        actual: tagged.children().len(),
211                    },
212                )));
213            }
214            return Some(Ok(RdListItem::Delimited(RdDelimitedItem { path, body })));
215        }
216        None
217    }
218}
219
220pub(super) fn inspect_two_group_item<'a>(
221    tagged: &'a RdTagged,
222    item_path: &RdPath,
223) -> Result<(&'a [RdNode], &'a [RdNode]), RdShapeError> {
224    if tagged.option().is_some() {
225        return Err(shape(
226            item_path.clone(),
227            Some(RdTag::Item),
228            RdShapeErrorKind::UnexpectedOption,
229        ));
230    }
231    if tagged.children().len() != 2 {
232        return Err(shape(
233            item_path.clone(),
234            Some(RdTag::Item),
235            RdShapeErrorKind::WrongArity {
236                expected: RdArity::Exactly(2),
237                actual: tagged.children().len(),
238            },
239        ));
240    }
241    let [label, body] = tagged.children() else {
242        unreachable!()
243    };
244    for (index, child) in tagged.children().iter().enumerate() {
245        if child.as_group().is_none() {
246            return Err(shape(
247                item_path.with_child(index),
248                Some(RdTag::Item),
249                RdShapeErrorKind::UnexpectedNode {
250                    expected: RdExpectedNode::Group,
251                    actual: RdNodeKind::of(child),
252                },
253            ));
254        }
255    }
256    Ok((
257        label.as_group().unwrap().children(),
258        body.as_group().unwrap().children(),
259    ))
260}