1use indexmap::IndexMap;
32
33use crate::ast::Value;
34
35#[non_exhaustive]
52pub enum Scalar {
53 Null,
55 Boolean(bool),
57 Number(f64),
60 String(String),
62 Array(Vec<Scalar>),
64 Object(IndexMap<String, Scalar>),
69}
70
71impl Clone for Scalar {
99 fn clone(&self) -> Self {
100 let mut plan = vec![Step::Open(self)];
101 let mut done: Vec<Scalar> = Vec::new();
102
103 while let Some(step) = plan.pop() {
104 match step {
105 Step::Open(scalar) => match scalar {
106 Scalar::Array(items) => {
107 plan.push(Step::Close(scalar));
108 for item in items.iter().rev() {
111 plan.push(Step::Open(item));
112 }
113 }
114 Scalar::Object(entries) => {
115 plan.push(Step::Close(scalar));
116 for (_, value) in entries.iter().rev() {
117 plan.push(Step::Open(value));
118 }
119 }
120 Scalar::Null => done.push(Scalar::Null),
121 Scalar::Boolean(value) => done.push(Scalar::Boolean(*value)),
122 Scalar::Number(value) => done.push(Scalar::Number(*value)),
123 Scalar::String(value) => done.push(Scalar::String(value.clone())),
124 },
125 Step::Close(scalar) => match scalar {
126 Scalar::Array(items) => {
127 let start = done.len().saturating_sub(items.len());
128 let children = done.split_off(start);
129 done.push(Scalar::Array(children));
130 }
131 Scalar::Object(entries) => {
132 let start = done.len().saturating_sub(entries.len());
133 let values = done.split_off(start);
134 done.push(Scalar::Object(
135 entries.keys().cloned().zip(values).collect(),
136 ));
137 }
138 _ => {}
140 },
141 }
142 }
143
144 done.pop().unwrap_or(Scalar::Null)
145 }
146}
147
148enum Step<'s> {
150 Open(&'s Scalar),
151 Close(&'s Scalar),
152}
153
154impl PartialEq for Scalar {
155 fn eq(&self, other: &Self) -> bool {
156 let mut work: Vec<(&Scalar, &Scalar)> = vec![(self, other)];
157 while let Some((left, right)) = work.pop() {
158 match (left, right) {
159 (Scalar::Null, Scalar::Null) => {}
160 (Scalar::Boolean(a), Scalar::Boolean(b)) => {
161 if a != b {
162 return false;
163 }
164 }
165 (Scalar::Number(a), Scalar::Number(b)) => {
166 if a != b {
167 return false;
168 }
169 }
170 (Scalar::String(a), Scalar::String(b)) => {
171 if a != b {
172 return false;
173 }
174 }
175 (Scalar::Array(a), Scalar::Array(b)) => {
176 if a.len() != b.len() {
177 return false;
178 }
179 work.extend(a.iter().zip(b.iter()));
180 }
181 (Scalar::Object(a), Scalar::Object(b)) => {
182 if a.len() != b.len() {
186 return false;
187 }
188 for (key, value) in a {
189 match b.get(key) {
190 Some(other) => work.push((value, other)),
191 None => return false,
192 }
193 }
194 }
195 _ => return false,
196 }
197 }
198 true
199 }
200}
201
202impl std::fmt::Debug for Scalar {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 let alternate = f.alternate();
205 let mut stack: Vec<DebugTok<'_>> = vec![DebugTok::Node(self, 0)];
206
207 while let Some(token) = stack.pop() {
208 match token {
209 DebugTok::Text(text) => f.write_str(text)?,
210 DebugTok::Owned(text) => f.write_str(&text)?,
211 DebugTok::Line(depth) => {
212 f.write_str("\n")?;
213 for _ in 0..depth {
214 f.write_str(" ")?;
215 }
216 }
217 DebugTok::Node(scalar, depth) => {
218 expand_scalar(f, &mut stack, scalar, depth, alternate)?;
219 }
220 }
221 }
222 Ok(())
223 }
224}
225
226enum DebugTok<'s> {
231 Node(&'s Scalar, usize),
232 Text(&'static str),
233 Owned(String),
234 Line(usize),
235}
236
237fn expand_scalar<'s>(
242 f: &mut std::fmt::Formatter<'_>,
243 stack: &mut Vec<DebugTok<'s>>,
244 scalar: &'s Scalar,
245 depth: usize,
246 alternate: bool,
247) -> std::fmt::Result {
248 fn leaf(
251 f: &mut std::fmt::Formatter<'_>,
252 name: &str,
253 body: &str,
254 depth: usize,
255 alternate: bool,
256 ) -> std::fmt::Result {
257 if alternate {
258 let pad = " ".repeat(depth);
259 write!(f, "{name}(\n{pad} {body},\n{pad})")
260 } else {
261 write!(f, "{name}({body})")
262 }
263 }
264
265 match scalar {
266 Scalar::Null => f.write_str("Null"),
267 Scalar::Boolean(value) => leaf(f, "Boolean", &format!("{value:?}"), depth, alternate),
268 Scalar::Number(value) => leaf(f, "Number", &format!("{value:?}"), depth, alternate),
269 Scalar::String(value) => leaf(f, "String", &format!("{value:?}"), depth, alternate),
270 Scalar::Array(items) => {
271 if items.is_empty() {
272 return leaf(f, "Array", "[]", depth, alternate);
273 }
274 f.write_str("Array(")?;
275 let mut queued: Vec<DebugTok<'s>> = Vec::new();
277 if alternate {
278 queued.push(DebugTok::Line(depth + 1));
279 queued.push(DebugTok::Text("["));
280 for item in items {
281 queued.push(DebugTok::Line(depth + 2));
282 queued.push(DebugTok::Node(item, depth + 2));
283 queued.push(DebugTok::Text(","));
284 }
285 queued.push(DebugTok::Line(depth + 1));
286 queued.push(DebugTok::Text("],"));
287 queued.push(DebugTok::Line(depth));
288 queued.push(DebugTok::Text(")"));
289 } else {
290 queued.push(DebugTok::Text("["));
291 for (index, item) in items.iter().enumerate() {
292 if index > 0 {
293 queued.push(DebugTok::Text(", "));
294 }
295 queued.push(DebugTok::Node(item, depth));
296 }
297 queued.push(DebugTok::Text("])"));
298 }
299 stack.extend(queued.into_iter().rev());
300 Ok(())
301 }
302 Scalar::Object(entries) => {
303 if entries.is_empty() {
304 return leaf(f, "Object", "{}", depth, alternate);
305 }
306 f.write_str("Object(")?;
307 let mut queued: Vec<DebugTok<'s>> = Vec::new();
308 if alternate {
309 queued.push(DebugTok::Line(depth + 1));
310 queued.push(DebugTok::Text("{"));
311 for (key, value) in entries {
312 queued.push(DebugTok::Line(depth + 2));
313 queued.push(DebugTok::Owned(format!("{key:?}: ")));
314 queued.push(DebugTok::Node(value, depth + 2));
315 queued.push(DebugTok::Text(","));
316 }
317 queued.push(DebugTok::Line(depth + 1));
318 queued.push(DebugTok::Text("},"));
319 queued.push(DebugTok::Line(depth));
320 queued.push(DebugTok::Text(")"));
321 } else {
322 queued.push(DebugTok::Text("{"));
323 for (index, (key, value)) in entries.iter().enumerate() {
324 if index > 0 {
325 queued.push(DebugTok::Text(", "));
326 }
327 queued.push(DebugTok::Owned(format!("{key:?}: ")));
328 queued.push(DebugTok::Node(value, depth));
329 }
330 queued.push(DebugTok::Text("})"));
331 }
332 stack.extend(queued.into_iter().rev());
333 Ok(())
334 }
335 }
336}
337
338impl Scalar {
339 #[must_use]
347 pub fn from_value(value: &Value) -> Option<Scalar> {
348 match value {
349 Value::Null => Some(Scalar::Null),
350 Value::Boolean(b) => Some(Scalar::Boolean(*b)),
351 Value::Number(n) => Some(Scalar::Number(*n)),
352 Value::String(s) => Some(Scalar::String(s.clone())),
353 Value::Array(items) => items
354 .iter()
355 .map(Scalar::from_value)
356 .collect::<Option<Vec<_>>>()
357 .map(Scalar::Array),
358 Value::Hash(entries) => entries
359 .iter()
360 .map(|(key, value)| Scalar::from_value(value).map(|value| (key.clone(), value)))
361 .collect::<Option<IndexMap<_, _>>>()
362 .map(Scalar::Object),
363 Value::Function(_) | Value::Variable(_) => None,
364 }
365 }
366}
367
368#[derive(Clone, Debug, PartialEq)]
375#[non_exhaustive]
376pub enum RenderableTreeNode {
377 Tag(Box<Tag>),
382 Scalar(Scalar),
384}
385
386impl RenderableTreeNode {
387 #[must_use]
389 pub fn tag(tag: Tag) -> RenderableTreeNode {
390 RenderableTreeNode::Tag(Box::new(tag))
391 }
392
393 #[must_use]
395 pub fn text(text: impl Into<String>) -> RenderableTreeNode {
396 RenderableTreeNode::Scalar(Scalar::String(text.into()))
397 }
398}
399
400#[derive(Clone, Debug, PartialEq)]
409#[non_exhaustive]
410pub enum RenderableTreeNodes {
411 One(RenderableTreeNode),
413 Many(Vec<RenderableTreeNode>),
415}
416
417impl RenderableTreeNodes {
418 #[must_use]
424 pub fn into_vec(self) -> Vec<RenderableTreeNode> {
425 match self {
426 RenderableTreeNodes::One(node) => vec![node],
427 RenderableTreeNodes::Many(nodes) => nodes,
428 }
429 }
430}
431
432impl From<RenderableTreeNode> for RenderableTreeNodes {
433 fn from(node: RenderableTreeNode) -> RenderableTreeNodes {
434 RenderableTreeNodes::One(node)
435 }
436}
437
438impl From<Vec<RenderableTreeNode>> for RenderableTreeNodes {
439 fn from(nodes: Vec<RenderableTreeNode>) -> RenderableTreeNodes {
440 RenderableTreeNodes::Many(nodes)
441 }
442}
443
444impl From<Tag> for RenderableTreeNodes {
445 fn from(tag: Tag) -> RenderableTreeNodes {
446 RenderableTreeNodes::One(RenderableTreeNode::tag(tag))
447 }
448}
449
450pub struct Tag {
467 pub name: String,
469 pub attributes: IndexMap<String, RenderableTreeNodes>,
471 pub children: Vec<RenderableTreeNode>,
473}
474
475fn nested_tags(tag: &Tag) -> Vec<&Tag> {
481 let mut out = Vec::new();
482 for (_, nodes) in &tag.attributes {
483 for node in nodes_slice(nodes) {
484 if let RenderableTreeNode::Tag(inner) = node {
485 out.push(inner.as_ref());
486 }
487 }
488 }
489 for node in &tag.children {
490 if let RenderableTreeNode::Tag(inner) = node {
491 out.push(inner.as_ref());
492 }
493 }
494 out
495}
496
497fn nodes_slice(nodes: &RenderableTreeNodes) -> &[RenderableTreeNode] {
499 match nodes {
500 RenderableTreeNodes::One(node) => std::slice::from_ref(node),
501 RenderableTreeNodes::Many(many) => many.as_slice(),
502 }
503}
504
505impl Clone for Tag {
517 fn clone(&self) -> Self {
518 enum Step<'t> {
519 Open(&'t Tag),
520 Close(&'t Tag),
521 }
522
523 let mut plan = vec![Step::Open(self)];
524 let mut done: Vec<Tag> = Vec::new();
525
526 while let Some(step) = plan.pop() {
527 match step {
528 Step::Open(tag) => {
529 plan.push(Step::Close(tag));
530 for nested in nested_tags(tag).into_iter().rev() {
531 plan.push(Step::Open(nested));
532 }
533 }
534 Step::Close(tag) => {
535 let count = nested_tags(tag).len();
536 let start = done.len().saturating_sub(count);
537 let mut finished = done.split_off(start).into_iter();
538
539 let mut attributes = IndexMap::new();
540 for (key, nodes) in &tag.attributes {
541 let rebuilt = match nodes {
542 RenderableTreeNodes::One(node) => {
543 RenderableTreeNodes::One(clone_node_taking(node, &mut finished))
544 }
545 RenderableTreeNodes::Many(many) => RenderableTreeNodes::Many(
546 many.iter()
547 .map(|node| clone_node_taking(node, &mut finished))
548 .collect(),
549 ),
550 };
551 attributes.insert(key.clone(), rebuilt);
552 }
553 let children = tag
554 .children
555 .iter()
556 .map(|node| clone_node_taking(node, &mut finished))
557 .collect();
558
559 done.push(Tag {
560 name: tag.name.clone(),
561 attributes,
562 children,
563 });
564 }
565 }
566 }
567
568 done.pop().unwrap_or_else(|| Tag::new("div"))
569 }
570}
571
572fn clone_node_taking(
577 node: &RenderableTreeNode,
578 finished: &mut impl Iterator<Item = Tag>,
579) -> RenderableTreeNode {
580 match node {
581 RenderableTreeNode::Tag(_) => finished.next().map_or_else(
582 || RenderableTreeNode::tag(Tag::new("div")),
583 |tag| RenderableTreeNode::Tag(Box::new(tag)),
584 ),
585 RenderableTreeNode::Scalar(scalar) => RenderableTreeNode::Scalar(scalar.clone()),
587 }
588}
589
590impl PartialEq for Tag {
591 fn eq(&self, other: &Self) -> bool {
592 let mut work: Vec<(&Tag, &Tag)> = vec![(self, other)];
593 while let Some((left, right)) = work.pop() {
594 if left.name != right.name
595 || left.attributes.len() != right.attributes.len()
596 || left.children.len() != right.children.len()
597 {
598 return false;
599 }
600 for (key, nodes) in &left.attributes {
602 let Some(other_nodes) = right.attributes.get(key) else {
603 return false;
604 };
605 if !push_node_pairs(nodes, other_nodes, &mut work) {
606 return false;
607 }
608 }
609 for (a, b) in left.children.iter().zip(right.children.iter()) {
610 if !push_node_pair(a, b, &mut work) {
611 return false;
612 }
613 }
614 }
615 true
616 }
617}
618
619fn push_node_pairs<'t>(
624 left: &'t RenderableTreeNodes,
625 right: &'t RenderableTreeNodes,
626 work: &mut Vec<(&'t Tag, &'t Tag)>,
627) -> bool {
628 match (left, right) {
629 (RenderableTreeNodes::One(a), RenderableTreeNodes::One(b)) => push_node_pair(a, b, work),
630 (RenderableTreeNodes::Many(a), RenderableTreeNodes::Many(b)) => {
631 if a.len() != b.len() {
632 return false;
633 }
634 a.iter()
635 .zip(b.iter())
636 .all(|(x, y)| push_node_pair(x, y, work))
637 }
638 _ => false,
639 }
640}
641
642fn push_node_pair<'t>(
644 left: &'t RenderableTreeNode,
645 right: &'t RenderableTreeNode,
646 work: &mut Vec<(&'t Tag, &'t Tag)>,
647) -> bool {
648 match (left, right) {
649 (RenderableTreeNode::Tag(a), RenderableTreeNode::Tag(b)) => {
650 work.push((a.as_ref(), b.as_ref()));
651 true
652 }
653 (RenderableTreeNode::Scalar(a), RenderableTreeNode::Scalar(b)) => a == b,
655 _ => false,
656 }
657}
658
659impl std::fmt::Debug for Tag {
660 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661 let alternate = f.alternate();
662 let mut stack: Vec<TagTok<'_>> = vec![TagTok::Tag(self, 0)];
663
664 while let Some(token) = stack.pop() {
665 match token {
666 TagTok::Text(text) => f.write_str(text)?,
667 TagTok::Owned(text) => f.write_str(&text)?,
668 TagTok::Line(depth) => {
669 f.write_str("\n")?;
670 for _ in 0..depth {
671 f.write_str(" ")?;
672 }
673 }
674 TagTok::Tag(tag, depth) => expand_tag(f, &mut stack, tag, depth, alternate)?,
675 TagTok::Nodes(nodes, depth) => {
676 expand_nodes(&mut stack, nodes, depth, alternate);
677 }
678 TagTok::Node(node, depth) => {
679 expand_node(f, &mut stack, node, depth, alternate)?;
680 }
681 }
682 }
683 Ok(())
684 }
685}
686
687enum TagTok<'t> {
689 Tag(&'t Tag, usize),
690 Nodes(&'t RenderableTreeNodes, usize),
691 Node(&'t RenderableTreeNode, usize),
692 Text(&'static str),
693 Owned(String),
694 Line(usize),
695}
696
697fn indent_block(body: &str, depth: usize) -> String {
700 let pad = " ".repeat(depth);
701 body.replace('\n', &format!("\n{pad}"))
702}
703
704fn expand_tag<'t>(
706 f: &mut std::fmt::Formatter<'_>,
707 stack: &mut Vec<TagTok<'t>>,
708 tag: &'t Tag,
709 depth: usize,
710 alternate: bool,
711) -> std::fmt::Result {
712 let name = format!("{:?}", tag.name);
713 let mut queued: Vec<TagTok<'t>> = Vec::new();
714
715 if alternate {
716 f.write_str("Tag {")?;
717 queued.push(TagTok::Line(depth + 1));
718 queued.push(TagTok::Owned(format!("name: {name},")));
719 queued.push(TagTok::Line(depth + 1));
720 if tag.attributes.is_empty() {
721 queued.push(TagTok::Text("attributes: {},"));
722 } else {
723 queued.push(TagTok::Text("attributes: {"));
724 for (key, nodes) in &tag.attributes {
725 queued.push(TagTok::Line(depth + 2));
726 queued.push(TagTok::Owned(format!("{key:?}: ")));
727 queued.push(TagTok::Nodes(nodes, depth + 2));
728 queued.push(TagTok::Text(","));
729 }
730 queued.push(TagTok::Line(depth + 1));
731 queued.push(TagTok::Text("},"));
732 }
733 queued.push(TagTok::Line(depth + 1));
734 if tag.children.is_empty() {
735 queued.push(TagTok::Text("children: [],"));
736 } else {
737 queued.push(TagTok::Text("children: ["));
738 for child in &tag.children {
739 queued.push(TagTok::Line(depth + 2));
740 queued.push(TagTok::Node(child, depth + 2));
741 queued.push(TagTok::Text(","));
742 }
743 queued.push(TagTok::Line(depth + 1));
744 queued.push(TagTok::Text("],"));
745 }
746 queued.push(TagTok::Line(depth));
747 queued.push(TagTok::Text("}"));
748 } else {
749 write!(f, "Tag {{ name: {name}, attributes: ")?;
750 if tag.attributes.is_empty() {
751 queued.push(TagTok::Text("{}"));
752 } else {
753 queued.push(TagTok::Text("{"));
754 for (index, (key, nodes)) in tag.attributes.iter().enumerate() {
755 if index > 0 {
756 queued.push(TagTok::Text(", "));
757 }
758 queued.push(TagTok::Owned(format!("{key:?}: ")));
759 queued.push(TagTok::Nodes(nodes, depth));
760 }
761 queued.push(TagTok::Text("}"));
762 }
763 queued.push(TagTok::Text(", children: ["));
764 for (index, child) in tag.children.iter().enumerate() {
765 if index > 0 {
766 queued.push(TagTok::Text(", "));
767 }
768 queued.push(TagTok::Node(child, depth));
769 }
770 queued.push(TagTok::Text("] }"));
771 }
772
773 stack.extend(queued.into_iter().rev());
774 Ok(())
775}
776
777fn expand_nodes<'t>(
779 stack: &mut Vec<TagTok<'t>>,
780 nodes: &'t RenderableTreeNodes,
781 depth: usize,
782 alternate: bool,
783) {
784 let mut queued: Vec<TagTok<'t>> = Vec::new();
785 match nodes {
786 RenderableTreeNodes::One(node) => {
787 queued.push(TagTok::Text("One("));
788 if alternate {
789 queued.push(TagTok::Line(depth + 1));
790 queued.push(TagTok::Node(node, depth + 1));
791 queued.push(TagTok::Text(","));
792 queued.push(TagTok::Line(depth));
793 } else {
794 queued.push(TagTok::Node(node, depth));
795 }
796 queued.push(TagTok::Text(")"));
797 }
798 RenderableTreeNodes::Many(many) if many.is_empty() => {
799 queued.push(TagTok::Text(if alternate { "Many(" } else { "Many([])" }));
800 if alternate {
801 queued.push(TagTok::Line(depth + 1));
802 queued.push(TagTok::Text("[],"));
803 queued.push(TagTok::Line(depth));
804 queued.push(TagTok::Text(")"));
805 }
806 }
807 RenderableTreeNodes::Many(many) => {
808 queued.push(TagTok::Text("Many("));
809 if alternate {
810 queued.push(TagTok::Line(depth + 1));
811 queued.push(TagTok::Text("["));
812 for node in many {
813 queued.push(TagTok::Line(depth + 2));
814 queued.push(TagTok::Node(node, depth + 2));
815 queued.push(TagTok::Text(","));
816 }
817 queued.push(TagTok::Line(depth + 1));
818 queued.push(TagTok::Text("],"));
819 queued.push(TagTok::Line(depth));
820 } else {
821 queued.push(TagTok::Text("["));
822 for (index, node) in many.iter().enumerate() {
823 if index > 0 {
824 queued.push(TagTok::Text(", "));
825 }
826 queued.push(TagTok::Node(node, depth));
827 }
828 queued.push(TagTok::Text("]"));
829 }
830 queued.push(TagTok::Text(")"));
831 }
832 }
833 stack.extend(queued.into_iter().rev());
834}
835
836fn expand_node<'t>(
838 f: &mut std::fmt::Formatter<'_>,
839 stack: &mut Vec<TagTok<'t>>,
840 node: &'t RenderableTreeNode,
841 depth: usize,
842 alternate: bool,
843) -> std::fmt::Result {
844 match node {
845 RenderableTreeNode::Tag(inner) => {
846 let mut queued: Vec<TagTok<'t>> = Vec::new();
847 f.write_str("Tag(")?;
848 if alternate {
849 queued.push(TagTok::Line(depth + 1));
850 queued.push(TagTok::Tag(inner.as_ref(), depth + 1));
851 queued.push(TagTok::Text(","));
852 queued.push(TagTok::Line(depth));
853 } else {
854 queued.push(TagTok::Tag(inner.as_ref(), depth));
855 }
856 queued.push(TagTok::Text(")"));
857 stack.extend(queued.into_iter().rev());
858 Ok(())
859 }
860 RenderableTreeNode::Scalar(scalar) => {
864 if alternate {
865 let pad = " ".repeat(depth);
866 let block = indent_block(&format!("{scalar:#?}"), depth + 1);
867 write!(f, "Scalar(\n{pad} {block},\n{pad})")
868 } else {
869 write!(f, "Scalar({scalar:?})")
870 }
871 }
872 }
873}
874
875impl Tag {
876 #[must_use]
878 pub fn new(name: impl Into<String>) -> Tag {
879 Tag {
880 name: name.into(),
881 attributes: IndexMap::new(),
882 children: Vec::new(),
883 }
884 }
885
886 #[must_use]
891 pub fn with(
892 name: impl Into<String>,
893 attributes: IndexMap<String, RenderableTreeNodes>,
894 children: Vec<RenderableTreeNode>,
895 ) -> Tag {
896 Tag {
897 name: name.into(),
898 attributes,
899 children,
900 }
901 }
902
903 pub fn set(&mut self, name: impl Into<String>, value: impl Into<RenderableTreeNodes>) {
905 self.attributes.insert(name.into(), value.into());
906 }
907
908 pub fn push(&mut self, child: RenderableTreeNode) {
910 self.children.push(child);
911 }
912}
913
914impl Default for Tag {
915 fn default() -> Tag {
918 Tag::new("div")
919 }
920}
921
922impl Drop for Tag {
948 fn drop(&mut self) {
949 let mut pending: Vec<Tag> = Vec::new();
950 unlink(self, &mut pending);
951 while let Some(mut tag) = pending.pop() {
952 unlink(&mut tag, &mut pending);
953 }
955 }
956}
957
958fn unlink(tag: &mut Tag, pending: &mut Vec<Tag>) {
964 let children = std::mem::take(&mut tag.children);
965 let attributes = std::mem::take(&mut tag.attributes);
966 pending.extend(children.into_iter().filter_map(into_tag));
967 for (_, value) in attributes {
968 pending.extend(value.into_vec().into_iter().filter_map(into_tag));
969 }
970}
971
972fn into_tag(node: RenderableTreeNode) -> Option<Tag> {
974 match node {
975 RenderableTreeNode::Tag(tag) => Some(*tag),
976 RenderableTreeNode::Scalar(_) => None,
977 }
978}
979
980impl Drop for Scalar {
994 fn drop(&mut self) {
995 let mut pending: Vec<Scalar> = Vec::new();
996 unlink_scalar(self, &mut pending);
997 while let Some(mut scalar) = pending.pop() {
998 unlink_scalar(&mut scalar, &mut pending);
999 }
1001 }
1002}
1003
1004fn unlink_scalar(scalar: &mut Scalar, pending: &mut Vec<Scalar>) {
1006 match scalar {
1007 Scalar::Array(items) => pending.append(items),
1008 Scalar::Object(entries) => pending.extend(entries.drain(..).map(|(_, value)| value)),
1009 _ => {}
1010 }
1011}
1012
1013#[cfg(test)]
1022mod debug_parity {
1023 use super::*;
1024
1025 mod mirror {
1026 #![allow(dead_code)]
1029
1030 use indexmap::IndexMap;
1031
1032 #[derive(Debug)]
1033 pub enum Scalar {
1034 Null,
1035 Boolean(bool),
1036 Number(f64),
1037 String(String),
1038 Array(Vec<Scalar>),
1039 Object(IndexMap<String, Scalar>),
1040 }
1041 }
1042
1043 fn to_mirror(scalar: &Scalar) -> mirror::Scalar {
1044 match scalar {
1045 Scalar::Null => mirror::Scalar::Null,
1046 Scalar::Boolean(value) => mirror::Scalar::Boolean(*value),
1047 Scalar::Number(value) => mirror::Scalar::Number(*value),
1048 Scalar::String(value) => mirror::Scalar::String(value.clone()),
1049 Scalar::Array(items) => mirror::Scalar::Array(items.iter().map(to_mirror).collect()),
1050 Scalar::Object(entries) => mirror::Scalar::Object(
1051 entries
1052 .iter()
1053 .map(|(key, value)| (key.clone(), to_mirror(value)))
1054 .collect(),
1055 ),
1056 }
1057 }
1058
1059 fn assert_parity(scalar: &Scalar) {
1060 let reference = to_mirror(scalar);
1061 assert_eq!(
1062 format!("{scalar:?}"),
1063 format!("{reference:?}"),
1064 "plain Debug diverged from the derive"
1065 );
1066 assert_eq!(
1067 format!("{scalar:#?}"),
1068 format!("{reference:#?}"),
1069 "alternate Debug diverged from the derive"
1070 );
1071 }
1072
1073 fn object(pairs: Vec<(&str, Scalar)>) -> Scalar {
1074 Scalar::Object(
1075 pairs
1076 .into_iter()
1077 .map(|(key, value)| (key.to_owned(), value))
1078 .collect(),
1079 )
1080 }
1081
1082 mod tag_mirror {
1083 #![allow(dead_code)]
1084
1085 use indexmap::IndexMap;
1086
1087 #[derive(Debug)]
1088 pub enum RenderableTreeNode {
1089 Tag(Box<Tag>),
1090 Scalar(crate::renderable::Scalar),
1091 }
1092
1093 #[derive(Debug)]
1094 pub enum RenderableTreeNodes {
1095 One(RenderableTreeNode),
1096 Many(Vec<RenderableTreeNode>),
1097 }
1098
1099 #[derive(Debug)]
1100 pub struct Tag {
1101 pub name: String,
1102 pub attributes: IndexMap<String, RenderableTreeNodes>,
1103 pub children: Vec<RenderableTreeNode>,
1104 }
1105 }
1106
1107 fn tag_to_mirror(tag: &Tag) -> tag_mirror::Tag {
1108 tag_mirror::Tag {
1109 name: tag.name.clone(),
1110 attributes: tag
1111 .attributes
1112 .iter()
1113 .map(|(key, nodes)| (key.clone(), nodes_to_mirror(nodes)))
1114 .collect(),
1115 children: tag.children.iter().map(node_to_mirror).collect(),
1116 }
1117 }
1118
1119 fn nodes_to_mirror(nodes: &RenderableTreeNodes) -> tag_mirror::RenderableTreeNodes {
1120 match nodes {
1121 RenderableTreeNodes::One(node) => {
1122 tag_mirror::RenderableTreeNodes::One(node_to_mirror(node))
1123 }
1124 RenderableTreeNodes::Many(many) => {
1125 tag_mirror::RenderableTreeNodes::Many(many.iter().map(node_to_mirror).collect())
1126 }
1127 }
1128 }
1129
1130 fn node_to_mirror(node: &RenderableTreeNode) -> tag_mirror::RenderableTreeNode {
1131 match node {
1132 RenderableTreeNode::Tag(inner) => {
1133 tag_mirror::RenderableTreeNode::Tag(Box::new(tag_to_mirror(inner)))
1134 }
1135 RenderableTreeNode::Scalar(scalar) => {
1136 tag_mirror::RenderableTreeNode::Scalar(scalar.clone())
1137 }
1138 }
1139 }
1140
1141 fn assert_tag_parity(tag: &Tag) {
1142 let reference = tag_to_mirror(tag);
1143 assert_eq!(format!("{tag:?}"), format!("{reference:?}"), "plain Debug");
1144 assert_eq!(
1145 format!("{tag:#?}"),
1146 format!("{reference:#?}"),
1147 "alternate Debug"
1148 );
1149 }
1150
1151 #[test]
1152 fn every_tag_shape_formats_as_the_derive_would() {
1153 let leaf = Tag::new("leaf");
1154
1155 let mut with_scalar_attr = Tag::new("p");
1156 with_scalar_attr.set("k", RenderableTreeNode::text("hi"));
1157
1158 let mut with_tag_attr = Tag::new("slotted");
1159 with_tag_attr.set("slot", RenderableTreeNode::tag(Tag::new("inner")));
1160
1161 let mut many_attr = Tag::new("many");
1162 many_attr.set(
1163 "list",
1164 vec![
1165 RenderableTreeNode::text("a"),
1166 RenderableTreeNode::tag(Tag::new("b")),
1167 ],
1168 );
1169
1170 let mut empty_many = Tag::new("emptymany");
1171 empty_many.set("list", Vec::new());
1172
1173 let nested = Tag::with(
1174 "outer",
1175 IndexMap::new(),
1176 vec![
1177 RenderableTreeNode::tag(Tag::with(
1178 "middle",
1179 IndexMap::new(),
1180 vec![RenderableTreeNode::tag(leaf.clone())],
1181 )),
1182 RenderableTreeNode::Scalar(Scalar::Array(vec![Scalar::Null])),
1183 ],
1184 );
1185
1186 for shape in &[
1187 leaf,
1188 with_scalar_attr,
1189 with_tag_attr,
1190 many_attr,
1191 empty_many,
1192 nested,
1193 ] {
1194 assert_tag_parity(shape);
1195 }
1196 }
1197
1198 #[test]
1199 fn a_deep_tag_survives_all_three_traversals() {
1200 let mut tag = Tag::new("leaf");
1201 for _ in 0..100_000 {
1202 tag = Tag::with("a", IndexMap::new(), vec![RenderableTreeNode::tag(tag)]);
1203 }
1204 let copy = tag.clone();
1205 assert!(copy == tag, "an iterative clone must equal its source");
1206 assert!(format!("{tag:?}").starts_with("Tag { name: \"a\""));
1207 }
1208
1209 #[test]
1210 fn a_tag_deep_through_attributes_survives_all_three() {
1211 let mut tag = Tag::new("leaf");
1214 for _ in 0..100_000 {
1215 let mut outer = Tag::new("a");
1216 outer.set("slot", RenderableTreeNode::tag(tag));
1217 tag = outer;
1218 }
1219 let copy = tag.clone();
1220 assert_eq!(copy, tag);
1221 }
1222
1223 #[test]
1224 fn tag_equality_distinguishes_one_from_many() {
1225 let mut one = Tag::new("t");
1227 one.set("k", RenderableTreeNode::text("x"));
1228 let mut many = Tag::new("t");
1229 many.set("k", vec![RenderableTreeNode::text("x")]);
1230 assert_ne!(one, many);
1231 }
1232
1233 #[test]
1234 fn every_scalar_shape_formats_as_the_derive_would() {
1235 let shapes = vec![
1236 Scalar::Null,
1237 Scalar::Boolean(true),
1238 Scalar::Boolean(false),
1239 Scalar::Number(1.0),
1240 Scalar::Number(-0.5),
1241 Scalar::String("hi".to_owned()),
1242 Scalar::String("a \"quote\" and a \\ and a \n".to_owned()),
1244 Scalar::Array(Vec::new()),
1245 object(Vec::new()),
1246 Scalar::Array(vec![Scalar::Null]),
1247 Scalar::Array(vec![Scalar::Null, Scalar::Boolean(true)]),
1248 object(vec![("a", Scalar::Null)]),
1249 object(vec![("a", Scalar::Null), ("b", Scalar::Number(2.0))]),
1250 Scalar::Array(vec![
1252 Scalar::Array(vec![Scalar::Number(1.0)]),
1253 object(vec![("k", Scalar::Array(Vec::new()))]),
1254 Scalar::String("x".to_owned()),
1255 ]),
1256 object(vec![(
1257 "outer",
1258 object(vec![("inner", Scalar::Array(vec![Scalar::Null]))]),
1259 )]),
1260 ];
1261 for shape in &shapes {
1262 assert_parity(shape);
1263 }
1264 }
1265
1266 #[test]
1267 fn a_deep_scalar_formats_without_aborting() {
1268 let mut scalar = Scalar::Null;
1272 for _ in 0..100_000 {
1273 scalar = Scalar::Array(vec![scalar]);
1274 }
1275 let rendered = format!("{scalar:?}");
1276 assert!(rendered.starts_with("Array([Array("));
1277 assert!(rendered.ends_with(")])"));
1278 }
1279
1280 #[test]
1281 fn a_deep_scalar_clones_and_compares_without_aborting() {
1282 let mut scalar = Scalar::Null;
1283 for _ in 0..100_000 {
1284 scalar = Scalar::Array(vec![scalar]);
1285 }
1286 let copy = scalar.clone();
1287 assert!(copy == scalar, "an iterative clone must equal its source");
1288 }
1289
1290 #[test]
1291 fn cloning_preserves_order_and_shape() {
1292 let original = object(vec![
1293 ("z", Scalar::Array(vec![Scalar::Number(1.0), Scalar::Null])),
1294 ("a", Scalar::String("x".to_owned())),
1295 ]);
1296 let copy = original.clone();
1297 assert_eq!(format!("{copy:?}"), format!("{original:?}"));
1298 let Scalar::Object(entries) = © else {
1299 panic!("expected an object")
1300 };
1301 assert_eq!(entries.keys().collect::<Vec<_>>(), ["z", "a"]);
1302 }
1303
1304 #[test]
1305 fn equality_ignores_object_order_as_indexmap_does() {
1306 let left = object(vec![("a", Scalar::Null), ("b", Scalar::Number(1.0))]);
1309 let right = object(vec![("b", Scalar::Number(1.0)), ("a", Scalar::Null)]);
1310 assert_eq!(left, right);
1311
1312 let different = object(vec![("a", Scalar::Null), ("b", Scalar::Number(2.0))]);
1313 assert_ne!(left, different);
1314 assert_ne!(left, object(vec![("a", Scalar::Null)]));
1315 }
1316}
1317
1318#[cfg(test)]
1319mod tests {
1320 use super::*;
1321
1322 #[test]
1323 fn the_default_element_is_a_div() {
1324 assert_eq!(Tag::default().name, "div");
1325 }
1326
1327 #[test]
1328 fn an_attribute_may_hold_a_rendered_subtree() {
1329 let mut foo = Tag::new("foo");
1332 let paragraph = Tag::with("p", IndexMap::new(), vec![RenderableTreeNode::text("hi")]);
1333 foo.set("bar", vec![RenderableTreeNode::tag(paragraph)]);
1334 assert!(matches!(
1335 foo.attributes.get("bar"),
1336 Some(RenderableTreeNodes::Many(nodes)) if nodes.len() == 1
1337 ));
1338 }
1339
1340 #[test]
1341 fn attribute_order_is_authored_order() {
1342 let mut tag = Tag::new("foo");
1343 tag.set("z", RenderableTreeNode::text("1"));
1344 tag.set("a", RenderableTreeNode::text("2"));
1345 let keys: Vec<&str> = tag.attributes.keys().map(String::as_str).collect();
1346 assert_eq!(keys, ["z", "a"]);
1347 }
1348
1349 #[test]
1350 fn scalars_come_from_resolved_values_only() {
1351 use crate::ast::Variable;
1352
1353 assert_eq!(
1354 Scalar::from_value(&Value::String("x".into())),
1355 Some(Scalar::String("x".into()))
1356 );
1357 assert_eq!(
1358 Scalar::from_value(&Value::Array(vec![Value::Number(1.0)])),
1359 Some(Scalar::Array(vec![Scalar::Number(1.0)]))
1360 );
1361 assert_eq!(
1363 Scalar::from_value(&Value::Variable(Variable::default())),
1364 None
1365 );
1366 assert_eq!(
1368 Scalar::from_value(&Value::Array(vec![Value::Variable(Variable::default())])),
1369 None
1370 );
1371 }
1372
1373 #[test]
1374 fn dropping_a_deep_tree_does_not_abort() {
1375 let mut tag = Tag::new("leaf");
1379 for _ in 0..100_000 {
1380 tag = Tag::with("a", IndexMap::new(), vec![RenderableTreeNode::tag(tag)]);
1381 }
1382 drop(tag);
1383 }
1384
1385 #[test]
1386 fn dropping_a_tree_nested_through_attributes_does_not_abort() {
1387 let mut tag = Tag::new("leaf");
1390 for _ in 0..100_000 {
1391 let mut outer = Tag::new("a");
1392 outer.set("slot", RenderableTreeNode::tag(tag));
1393 tag = outer;
1394 }
1395 drop(tag);
1396 }
1397
1398 #[test]
1399 fn dropping_a_deep_scalar_array_does_not_abort() {
1400 let mut scalar = Scalar::Null;
1405 for _ in 0..100_000 {
1406 scalar = Scalar::Array(vec![scalar]);
1407 }
1408 drop(scalar);
1409 }
1410
1411 #[test]
1412 fn dropping_a_deep_scalar_object_does_not_abort() {
1413 let mut scalar = Scalar::Null;
1415 for _ in 0..100_000 {
1416 let mut object = IndexMap::new();
1417 object.insert("k".to_string(), scalar);
1418 scalar = Scalar::Object(object);
1419 }
1420 drop(scalar);
1421 }
1422
1423 #[test]
1424 fn many_and_one_flatten_the_same_way() {
1425 let one = RenderableTreeNodes::One(RenderableTreeNode::text("a"));
1426 assert_eq!(one.into_vec().len(), 1);
1427 let many = RenderableTreeNodes::Many(vec![
1428 RenderableTreeNode::text("a"),
1429 RenderableTreeNode::text("b"),
1430 ]);
1431 assert_eq!(many.into_vec().len(), 2);
1432 }
1433}