1use std::fmt::{self, Write};
2
3use crate::formatter::{
4 ErrorKind, FormatTable, Render, TemplateError,
5 block::{Block, BlockBuilder, BlockMode},
6 condition::ConditionalToken,
7 lexer::{Lex, lex_str},
8 tag::Tag,
9};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Template<'a> {
14 args: Vec<TemplateItem<'a>>,
15}
16
17impl<'a> Template<'a> {
18 pub(super) fn from_lex(
19 lexes: impl IntoIterator<Item = Lex<'a>>,
20 ) -> Result<Template<'a>, TemplateError> {
21 let mut args = Vec::new();
22 let mut escaped = false;
23
24 let mut stack: Vec<ParserEntry<'_>> = Vec::new();
25
26 for lex in lexes {
27 if escaped {
28 push_arg(&mut stack, &mut args, TemplateItem::Text(lex.to_str()));
29 escaped = false;
30 } else {
31 match lex {
32 Lex::BlockStart => stack.push(ParserEntry::Block(BlockBuilder::default())),
33 Lex::BlockEnd => end_block(&mut stack, &mut args)?,
34 Lex::Variable => {
35 end_tag(&mut stack, &mut args);
36 stack.push(ParserEntry::Tag(Tag::default()));
37 }
38 Lex::Conditional | Lex::Prefix | Lex::Suffix | Lex::Fallback => block_mode_switch(
39 &mut stack,
40 &mut args,
41 lex.to_block_mode()
42 .expect("The lex was already matched on block modes. This should not fail"),
43 )?,
44 Lex::Or | Lex::And | Lex::Not => push_conditional_token(
45 &mut stack,
46 &mut args,
47 lex.to_condition_token().expect(
48 "The lex was already matched on conditional tokens. This should not fail",
49 ),
50 ),
51 Lex::Space => {
52 end_tag(&mut stack, &mut args);
53 push_arg(&mut stack, &mut args, TemplateItem::Text(lex.to_str()));
54 }
55 Lex::Escape => escaped = true,
56 Lex::Text(str) => push_arg(&mut stack, &mut args, TemplateItem::Text(str)),
57 }
58 }
59 }
60
61 end_tag(&mut stack, &mut args);
62
63 if !stack.is_empty() {
64 return Err(TemplateError::new(ErrorKind::BadClosure(
65 "Completed parsing all lexes, but the stack was not empty. This means a tag or block was left unclosed",
66 )));
67 }
68
69 Ok(Template { args })
70 }
71}
72
73impl<'a> TryFrom<&'a str> for Template<'a> {
74 type Error = TemplateError;
75
76 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
77 let lexes = lex_str(value);
78 Template::from_lex(lexes)
79 }
80}
81
82impl Render for Template<'_> {
83 fn render(&self, format_table: &FormatTable) -> String {
84 self.args.render(format_table)
85 }
86}
87
88impl fmt::Display for Template<'_> {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 for arg in &self.args {
91 arg.fmt(f)?;
92 }
93 Ok(())
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub(super) enum TemplateItem<'a> {
99 Text(&'a str),
100 Tag(Tag<'a>),
101 Block(Block<'a>),
102}
103
104impl Render for TemplateItem<'_> {
105 fn render(&self, format_table: &super::FormatTable) -> String {
106 match self {
107 TemplateItem::Text(str) => str.to_string(),
108 TemplateItem::Tag(tag) => tag.render(format_table),
109 TemplateItem::Block(block) => block.render(format_table),
110 }
111 }
112}
113
114impl Render for [TemplateItem<'_>] {
115 fn render(&self, format_table: &FormatTable) -> String {
116 self.iter().map(|arg| arg.render(format_table)).collect()
117 }
118}
119
120enum ParserEntry<'a> {
121 Tag(Tag<'a>),
122 Block(BlockBuilder<'a>),
123}
124
125#[inline(always)]
126fn push_arg<'a>(
127 stack: &mut Vec<ParserEntry<'a>>,
128 args: &mut Vec<TemplateItem<'a>>,
129 arg: TemplateItem<'a>,
130) {
131 match stack.last_mut() {
132 Some(ParserEntry::Block(block_builder)) => {
133 block_builder.push_arg(arg);
134 }
135 Some(ParserEntry::Tag(tag)) => tag.args.push(arg),
136 None => args.push(arg),
137 }
138}
139
140fn push_conditional_token<'a>(
141 stack: &mut Vec<ParserEntry<'a>>,
142 args: &mut Vec<TemplateItem<'a>>,
143 token: ConditionalToken<'a>,
144) {
145 if let Some(ParserEntry::Block(block)) = stack.last_mut() {
146 block.push_conditional_token(token);
147 } else {
148 push_arg(stack, args, TemplateItem::Text(token.to_str()));
149 }
150}
151
152fn end_tag<'a>(stack: &mut Vec<ParserEntry<'a>>, args: &mut Vec<TemplateItem<'a>>) {
153 if let Some(ParserEntry::Tag(_)) = stack.last() {
154 let tag = match stack.pop().unwrap() {
155 ParserEntry::Tag(tag) => tag,
156 _ => unreachable!(),
157 };
158
159 push_arg(stack, args, TemplateItem::Tag(tag));
160 }
161}
162
163fn end_tag_if_in_block<'a>(stack: &mut Vec<ParserEntry<'a>>, args: &mut Vec<TemplateItem<'a>>) {
164 if let Some(ParserEntry::Tag(_)) = stack.last()
165 && let Some(ParserEntry::Block(_)) = stack.get(stack.len() - 2)
166 {
167 let tag = match stack.pop().unwrap() {
168 ParserEntry::Tag(tag) => tag,
169 _ => unreachable!(),
170 };
171 push_arg(stack, args, TemplateItem::Tag(tag));
172 }
173}
174
175fn end_block<'a>(
176 stack: &mut Vec<ParserEntry<'a>>,
177 args: &mut Vec<TemplateItem<'a>>,
178) -> Result<(), TemplateError> {
179 end_tag(stack, args);
180
181 let block = match stack.pop() {
182 Some(ParserEntry::Block(block)) => block,
183 Some(_) => {
184 panic!("'}}' was found unescaped but it didn't close a block");
185 }
186 None => {
187 return Err(TemplateError::new(ErrorKind::BadClosure(
188 "'}' was found unescaped with nothing to close",
189 )));
190 }
191 };
192
193 push_arg(stack, args, TemplateItem::Block(block.build()));
194
195 Ok(())
196}
197
198fn block_mode_switch<'a>(
199 stack: &mut Vec<ParserEntry<'a>>,
200 args: &mut Vec<TemplateItem<'a>>,
201 mode: BlockMode,
202) -> Result<(), TemplateError> {
203 end_tag_if_in_block(stack, args);
204
205 if let Some(ParserEntry::Block(block)) = stack.last_mut() {
206 block.set_mode(mode)
207 } else {
208 args.push(TemplateItem::Text(mode.to_str()));
209 Ok(())
210 }
211}
212
213pub(super) fn write_escaped(f: &mut fmt::Formatter<'_>, s: &str, tag_context: bool) -> fmt::Result {
214 for byte in s.bytes() {
215 if byte == b' ' && !tag_context {
216 f.write_char(' ')?;
217 continue;
218 }
219
220 if matches!(
221 byte,
222 b'{' | b'}' | b'$' | b'@' | b'<' | b'>' | b'?' | b'|' | b'&' | b'!' | b'\\' | b' '
223 ) {
224 f.write_char('\\')?;
225 }
226
227 f.write_char(byte as char)?;
228 }
229 Ok(())
230}
231
232impl fmt::Display for TemplateItem<'_> {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 match self {
235 TemplateItem::Text(s) => write_escaped(f, s, false),
236 TemplateItem::Tag(tag) => tag.fmt(f),
237 TemplateItem::Block(block) => block.fmt(f),
238 }
239 }
240}
241
242#[derive(Debug)]
243pub struct TemplateOwned {
244 args: Template<'static>,
245 string: Box<str>,
246}
247
248impl TemplateOwned {
249 pub fn new(s: impl Into<String>) -> Result<Self, TemplateError> {
250 let string: Box<str> = s.into().into_boxed_str();
251 let ptr: *const str = &raw const *string;
252
253 let args = unsafe {
258 let str_ref: &'static str = &*ptr;
259 let lexes = lex_str(str_ref);
260 Template::from_lex(lexes)?
261 };
262
263 Ok(Self { args, string })
264 }
265
266 #[must_use]
267 pub fn as_arguments(&self) -> &Template<'_> {
268 &self.args
269 }
270
271 #[must_use]
272 pub fn as_str(&self) -> &str {
273 &self.string
274 }
275}
276
277impl Clone for TemplateOwned {
278 fn clone(&self) -> Self {
279 Self::new(self.string.clone()).expect("Previously valid string cannot suddenly fail")
280 }
281}
282
283impl PartialEq for TemplateOwned {
284 fn eq(&self, other: &Self) -> bool {
285 self.args == other.args
286 }
287}
288
289impl Eq for TemplateOwned {}
290
291impl From<Template<'_>> for TemplateOwned {
292 fn from(value: Template<'_>) -> Self {
293 TemplateOwned::new(value.to_string()).expect("Arguments to_string are always valid")
294 }
295}
296
297#[cfg(feature = "serde")]
298impl serde::Serialize for TemplateOwned {
299 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
300 s.serialize_str(&self.string)
301 }
302}
303
304#[cfg(feature = "serde")]
305impl<'de> serde::Deserialize<'de> for TemplateOwned {
306 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
307 let s = String::deserialize(d)?;
308 TemplateOwned::new(s).map_err(serde::de::Error::custom)
309 }
310}
311
312#[cfg(feature = "serde")]
313impl serde::Serialize for Template<'_> {
314 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
315 s.serialize_str(&self.to_string())
316 }
317}