1use crate::{RawRdNode, RdTag};
7
8#[derive(Debug, Clone, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct RdDocument {
18 nodes: Vec<RdNode>,
20}
21
22impl RdDocument {
23 pub fn new(nodes: Vec<RdNode>) -> Self {
24 Self { nodes }
25 }
26
27 pub fn nodes(&self) -> &[RdNode] {
28 &self.nodes
29 }
30
31 pub fn into_nodes(self) -> Vec<RdNode> {
32 self.nodes
33 }
34}
35
36impl From<Vec<RdNode>> for RdDocument {
37 fn from(nodes: Vec<RdNode>) -> Self {
38 Self::new(nodes)
39 }
40}
41
42impl IntoIterator for RdDocument {
43 type Item = RdNode;
44 type IntoIter = std::vec::IntoIter<RdNode>;
45
46 fn into_iter(self) -> Self::IntoIter {
47 self.nodes.into_iter()
48 }
49}
50
51impl<'a> IntoIterator for &'a RdDocument {
52 type Item = &'a RdNode;
53 type IntoIter = std::slice::Iter<'a, RdNode>;
54
55 fn into_iter(self) -> Self::IntoIter {
56 self.nodes.iter()
57 }
58}
59
60#[derive(Debug, Clone, PartialEq)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63pub struct RdTagged {
64 tag: RdTag,
65 option: Option<Vec<RdNode>>,
66 children: Vec<RdNode>,
67}
68
69impl RdTagged {
70 pub fn new(tag: RdTag, option: Option<Vec<RdNode>>, children: Vec<RdNode>) -> Self {
71 Self {
72 tag,
73 option,
74 children,
75 }
76 }
77
78 pub fn tag(&self) -> &RdTag {
79 &self.tag
80 }
81
82 pub fn option(&self) -> Option<&[RdNode]> {
83 self.option.as_deref()
84 }
85
86 pub fn children(&self) -> &[RdNode] {
87 &self.children
88 }
89
90 pub fn into_parts(self) -> (RdTag, Option<Vec<RdNode>>, Vec<RdNode>) {
91 (self.tag, self.option, self.children)
92 }
93}
94
95#[derive(Debug, Clone, PartialEq)]
97#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
98pub struct RdGroup {
99 children: Vec<RdNode>,
100}
101
102impl RdGroup {
103 pub fn new(children: Vec<RdNode>) -> Self {
104 Self { children }
105 }
106
107 pub fn children(&self) -> &[RdNode] {
108 &self.children
109 }
110
111 pub fn into_children(self) -> Vec<RdNode> {
112 self.children
113 }
114}
115
116impl From<Vec<RdNode>> for RdGroup {
117 fn from(children: Vec<RdNode>) -> Self {
118 Self::new(children)
119 }
120}
121
122#[derive(Debug, Clone, PartialEq)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
132#[non_exhaustive]
133pub enum RdNode {
134 Text(String),
136 RCode(String),
139 Verb(String),
142 Comment(String),
147 Tagged(RdTagged),
149 Group(RdGroup),
151 Raw(RawRdNode),
155}
156
157impl RdNode {
158 pub fn tagged(tag: RdTag, option: Option<Vec<RdNode>>, children: Vec<RdNode>) -> Self {
159 Self::Tagged(RdTagged::new(tag, option, children))
160 }
161
162 pub fn group(children: Vec<RdNode>) -> Self {
163 Self::Group(RdGroup::new(children))
164 }
165
166 pub fn as_tagged(&self) -> Option<&RdTagged> {
167 match self {
168 Self::Tagged(node) => Some(node),
169 _ => None,
170 }
171 }
172 pub fn as_group(&self) -> Option<&RdGroup> {
173 match self {
174 Self::Group(node) => Some(node),
175 _ => None,
176 }
177 }
178 pub fn as_raw(&self) -> Option<&RawRdNode> {
179 match self {
180 Self::Raw(node) => Some(node),
181 _ => None,
182 }
183 }
184
185 pub fn into_tagged(self) -> Result<RdTagged, Self> {
186 match self {
187 Self::Tagged(node) => Ok(node),
188 other => Err(other),
189 }
190 }
191 pub fn into_group(self) -> Result<RdGroup, Self> {
192 match self {
193 Self::Group(node) => Ok(node),
194 other => Err(other),
195 }
196 }
197 pub fn into_raw(self) -> Result<RawRdNode, Self> {
198 match self {
199 Self::Raw(node) => Ok(node),
200 other => Err(other),
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use crate::{RawRdValue, producer};
209
210 fn sample_document() -> RdDocument {
214 RdDocument::new(vec![
215 RdNode::tagged(RdTag::Name, None, vec![RdNode::Text("example".to_string())]),
216 RdNode::Text("\n".to_string()),
217 RdNode::tagged(
218 RdTag::Title,
219 None,
220 vec![RdNode::Text("An example topic".to_string())],
221 ),
222 RdNode::Text("\n".to_string()),
223 RdNode::tagged(
224 RdTag::Description,
225 None,
226 vec![
227 RdNode::Text("See ".to_string()),
228 RdNode::tagged(
229 RdTag::Link,
230 Some(vec![RdNode::Text("base".to_string())]),
231 vec![RdNode::Text("print".to_string())],
232 ),
233 RdNode::Text(" for details.".to_string()),
234 ],
235 ),
236 ])
237 }
238
239 #[test]
240 fn builds_and_matches_expected_shape() {
241 let doc = sample_document();
242 assert_eq!(doc.nodes().len(), 5);
243 let Some(tagged) = doc.nodes()[0].as_tagged() else {
244 panic!("expected a Tagged node");
245 };
246 assert_eq!(tagged.tag(), &RdTag::Name);
247 assert_eq!(tagged.children(), &[RdNode::Text("example".to_string())]);
248 }
249
250 #[test]
251 fn tagged_option_carries_markup() {
252 let doc = sample_document();
253 let Some(description) = doc.nodes()[4].as_tagged() else {
254 panic!("expected the description's Tagged node");
255 };
256 let Some(link) = description.children()[1].as_tagged() else {
257 panic!(r"expected the \link node");
258 };
259 assert_eq!(link.tag(), &RdTag::Link);
260 assert_eq!(link.option(), Some(&[RdNode::Text("base".to_string())][..]));
261 }
262
263 #[test]
266 fn raw_node_preserves_untagged_group_and_attributes() {
267 let raw = producer::raw_node(
268 None,
269 None,
270 vec![RdNode::Text("print".to_string())],
271 None,
272 vec![producer::raw_attribute(
273 "class".to_string(),
274 producer::raw_object(
275 RawRdValue::Character(vec![Some("weird".to_string())]),
276 Vec::new(),
277 ),
278 )],
279 );
280 let node = RdNode::Raw(raw.clone());
281 let RdNode::Raw(inner) = &node else {
282 panic!("expected Raw");
283 };
284 assert_eq!(inner, &raw);
285 assert_eq!(inner.tag(), None);
286 assert_eq!(inner.attributes().len(), 1);
287 }
288
289 #[test]
290 fn equal_documents_compare_equal() {
291 assert_eq!(sample_document(), sample_document());
292 }
293}