lunar_lib/formatter/
format_args.rs1use crate::formatter::{
2 ErrorKind, FormatError, FormatTable, Render,
3 block::{Block, BlockBuilder, BlockMode},
4 condition::ConditionalToken,
5 lexer::Lex,
6 tag::Tag,
7};
8
9#[derive(Debug, Clone)]
11pub struct Arguments<'a> {
12 args: Vec<FormatArg<'a>>,
13}
14
15impl<'a> Arguments<'a> {
16 pub(super) fn from_lex(
17 lexes: impl IntoIterator<Item = Lex<'a>>,
18 ) -> Result<Arguments<'a>, FormatError> {
19 let mut args = Vec::new();
20 let mut escaped = false;
21
22 let mut stack: Vec<ParserEntry<'_>> = Vec::new();
23
24 for lex in lexes.into_iter() {
25 if escaped {
26 push_arg(&mut stack, &mut args, FormatArg::Text(lex.to_str()));
27 escaped = false;
28 } else {
29 match lex {
30 Lex::BlockStart => stack.push(ParserEntry::Block(BlockBuilder::default())),
31 Lex::BlockEnd => end_block(&mut stack, &mut args)?,
32 Lex::Variable => {
33 end_tag(&mut stack, &mut args);
34 stack.push(ParserEntry::Tag(Tag::default()));
35 }
36 Lex::Conditional | Lex::Prefix | Lex::Suffix | Lex::Fallback => block_mode_switch(
37 &mut stack,
38 &mut args,
39 lex.to_block_mode()
40 .expect("The lex was already matched on block modes. This should not fail"),
41 )?,
42 Lex::Or | Lex::And | Lex::Not => push_conditional_token(
43 &mut stack,
44 &mut args,
45 lex.to_condition_token().expect(
46 "The lex was already matched on conditional tokens. This should not fail",
47 ),
48 ),
49 Lex::Space => {
50 end_tag(&mut stack, &mut args);
51 push_arg(&mut stack, &mut args, FormatArg::Text(lex.to_str()));
52 }
53 Lex::Escape => escaped = true,
54 Lex::Text(str) => push_arg(&mut stack, &mut args, FormatArg::Text(str)),
55 }
56 }
57 }
58
59 end_tag(&mut stack, &mut args);
60
61 if !stack.is_empty() {
62 return Err(FormatError::new(ErrorKind::BadClosure(
63 "Completed parsing all lexes, but the stack was not empty. This means a tag or block was left unclosed",
64 )));
65 }
66
67 Ok(Arguments { args })
68 }
69}
70
71impl<'a> Render for Arguments<'a> {
72 fn render(&self, format_table: &FormatTable) -> String {
73 self.args.render(format_table)
74 }
75}
76
77#[derive(Debug, Clone)]
78pub(super) enum FormatArg<'a> {
79 Text(&'a str),
80 Tag(Tag<'a>),
81 Block(Block<'a>),
82}
83
84impl<'a> Render for FormatArg<'a> {
85 fn render(&self, format_table: &super::FormatTable) -> String {
86 match self {
87 FormatArg::Text(str) => str.to_string(),
88 FormatArg::Tag(tag) => tag.render(format_table),
89 FormatArg::Block(block) => block.render(format_table),
90 }
91 }
92}
93
94impl<'a> Render for [FormatArg<'a>] {
95 fn render(&self, format_table: &FormatTable) -> String {
96 self.iter().map(|arg| arg.render(format_table)).collect()
97 }
98}
99
100enum ParserEntry<'a> {
101 Tag(Tag<'a>),
102 Block(BlockBuilder<'a>),
103}
104
105#[inline(always)]
106fn push_arg<'a>(
107 stack: &mut Vec<ParserEntry<'a>>,
108 args: &mut Vec<FormatArg<'a>>,
109 arg: FormatArg<'a>,
110) {
111 match stack.last_mut() {
112 Some(ParserEntry::Block(block_builder)) => {
113 block_builder.push_arg(arg);
114 }
115 Some(ParserEntry::Tag(tag)) => tag.args.push(arg),
116 None => args.push(arg),
117 }
118}
119
120fn push_conditional_token<'a>(
121 stack: &mut Vec<ParserEntry<'a>>,
122 args: &mut Vec<FormatArg<'a>>,
123 token: ConditionalToken<'a>,
124) {
125 if let Some(ParserEntry::Block(block)) = stack.last_mut() {
126 block.push_conditional_token(token);
127 } else {
128 push_arg(stack, args, FormatArg::Text(token.to_str()));
129 }
130}
131
132fn end_tag<'a>(stack: &mut Vec<ParserEntry<'a>>, args: &mut Vec<FormatArg<'a>>) {
133 if let Some(ParserEntry::Tag(_)) = stack.last() {
134 let tag = match stack.pop().unwrap() {
135 ParserEntry::Tag(tag) => tag,
136 _ => unreachable!(),
137 };
138
139 push_arg(stack, args, FormatArg::Tag(tag));
140 }
141}
142
143fn end_tag_if_in_block<'a>(stack: &mut Vec<ParserEntry<'a>>, args: &mut Vec<FormatArg<'a>>) {
144 if let Some(ParserEntry::Tag(_)) = stack.last()
145 && let Some(ParserEntry::Block(_)) = stack.get(stack.len() - 2)
146 {
147 let tag = match stack.pop().unwrap() {
148 ParserEntry::Tag(tag) => tag,
149 _ => unreachable!(),
150 };
151 push_arg(stack, args, FormatArg::Tag(tag));
152 }
153}
154
155fn end_block<'a>(
156 stack: &mut Vec<ParserEntry<'a>>,
157 args: &mut Vec<FormatArg<'a>>,
158) -> Result<(), FormatError> {
159 end_tag(stack, args);
160
161 let block = match stack.pop() {
162 Some(ParserEntry::Block(block)) => block,
163 Some(_) => {
164 panic!("'}}' was found unescaped but it didn't close a block");
165 }
166 None => {
167 return Err(FormatError::new(ErrorKind::BadClosure(
168 "'}' was found unescaped with nothing to close",
169 )));
170 }
171 };
172
173 push_arg(stack, args, FormatArg::Block(block.build()));
174
175 Ok(())
176}
177
178fn block_mode_switch<'a>(
179 stack: &mut Vec<ParserEntry<'a>>,
180 args: &mut Vec<FormatArg<'a>>,
181 mode: BlockMode,
182) -> Result<(), FormatError> {
183 end_tag_if_in_block(stack, args);
184
185 if let Some(ParserEntry::Block(block)) = stack.last_mut() {
186 block.set_mode(mode)
187 } else {
188 args.push(FormatArg::Text(mode.to_str()));
189 Ok(())
190 }
191}