ftml/includes/includer/
debug.rs1use super::prelude::*;
22use crate::tree::VariableMap;
23#[cfg(test)]
24use std::collections::HashMap;
25use std::convert::Infallible;
26use std::fmt::{self, Display};
27
28#[derive(Debug)]
33pub struct DebugIncluder;
34
35impl<'t> Includer<'t> for DebugIncluder {
36 type Error = Infallible;
37
38 #[inline]
39 fn include_pages(
40 &mut self,
41 includes: &[IncludeRef<'t>],
42 ) -> Result<Vec<FetchedPage<'t>>, Infallible> {
43 let mut first = true;
44 let mut pages = Vec::new();
45
46 for include in includes {
47 let content = if first && includes.len() > 1 {
48 first = false;
55 None
56 } else {
57 let content = format!(
58 "<INCLUDED-PAGE {} {}>",
59 include.page_ref(),
60 MapWrap(include.variables()),
61 );
62
63 Some(Cow::Owned(content))
64 };
65
66 let page_ref = include.page_ref().clone();
67 pages.push(FetchedPage { page_ref, content });
68 }
69
70 Ok(pages)
71 }
72
73 #[inline]
74 fn no_such_include(
75 &mut self,
76 page_ref: &PageRef,
77 ) -> Result<Cow<'t, str>, Infallible> {
78 Ok(Cow::Owned(format!("<MISSING-PAGE {page_ref}>")))
79 }
80}
81
82#[derive(Debug)]
87struct MapWrap<'m, 't>(&'m VariableMap<'t>);
88
89impl<'t> Display for MapWrap<'_, 't> {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 let mut entries: Vec<(&Cow<'t, str>, &Cow<'t, str>)> = self.0.iter().collect();
93 entries.sort_by(|(key1, _), (key2, _)| key1.cmp(key2));
94
95 write!(f, "{{")?;
97
98 for (i, (key, value)) in entries.iter().enumerate() {
99 write!(f, "{key:?} => {value:?}")?;
100
101 if i < entries.len() - 1 {
102 write!(f, ", ")?;
103 }
104 }
105
106 write!(f, "}}")?;
107
108 Ok(())
110 }
111}
112
113#[test]
114fn map_wrap() {
115 macro_rules! test {
116 ($input:expr, $expected:expr $(,)?) => {{
117 let raw_input = &stringify!($input)[9..];
120
121 let input = {
123 let original = $input;
124 let mut map = HashMap::new();
125
126 for (key, value) in original {
127 let key = Cow::Borrowed(key);
128 let value = Cow::Borrowed(value);
129
130 map.insert(key, value);
131 }
132
133 map
134 };
135
136 let actual = MapWrap(&input).to_string();
137 let expected = $expected;
138
139 println!("Input: {raw_input}");
140 println!("Actual: {actual}");
141 println!("Expected: {expected}");
142 println!();
143
144 assert_eq!(
145 &actual, $expected,
146 "Actual format string didn't match expected"
147 );
148 }};
149 }
150
151 test!(hashmap! {}, "{}");
152 test!(hashmap! { "apple" => "1" }, r#"{"apple" => "1"}"#);
153 test!(
154 hashmap! { "apple" => "1", "banana" => "2" },
155 r#"{"apple" => "1", "banana" => "2"}"#,
156 );
157 test!(
158 hashmap! { "banana" => "2", "apple" => "1" },
159 r#"{"apple" => "1", "banana" => "2"}"#,
160 );
161 test!(
162 hashmap! { "apple" => "1", "banana" => "2", "cherry" => "3" },
163 r#"{"apple" => "1", "banana" => "2", "cherry" => "3"}"#,
164 );
165 test!(
166 hashmap! { "banana" => "2", "apple" => "1", "cherry" => "3" },
167 r#"{"apple" => "1", "banana" => "2", "cherry" => "3"}"#,
168 );
169 test!(
170 hashmap! { "cherry" => "3", "banana" => "2", "apple" => "1" },
171 r#"{"apple" => "1", "banana" => "2", "cherry" => "3"}"#,
172 );
173 test!(
174 hashmap! { "apple" => "1", "cherry" => "3", "banana" => "2" },
175 r#"{"apple" => "1", "banana" => "2", "cherry" => "3"}"#,
176 );
177}