1use alloc::string::String;
21
22use mf2_model::{
23 Attributes, Declaration, Expression, FunctionRef, Key, Literal, Markup, MarkupKind, Message,
24 OptionValue, Options, Pattern, PatternPart,
25};
26
27use crate::chars;
28use crate::error::{Error, NameRole};
29
30pub fn serialize(message: &Message<'_>) -> Result<String, Error> {
36 let mut w = Writer(String::new());
37 match message {
38 Message::Pattern(m) if m.declarations.is_empty() => {
39 w.pattern(&m.pattern)?;
40 if reads_as_complex(&w.0) {
41 let mut quoted = String::with_capacity(w.0.len() + 4);
42 quoted.push_str("{{");
43 quoted.push_str(&w.0);
44 quoted.push_str("}}");
45 return Ok(quoted);
46 }
47 }
48 Message::Pattern(m) => {
49 w.declarations(&m.declarations)?;
50 w.quoted_pattern(&m.pattern)?;
51 }
52 Message::Select(m) => {
53 if m.selectors.is_empty() {
54 return Err(Error::NoSelectors);
55 }
56 if m.variants.is_empty() {
57 return Err(Error::NoVariants);
58 }
59 w.declarations(&m.declarations)?;
60 w.0.push_str(".match");
61 for s in &m.selectors {
62 w.0.push_str(" $");
63 w.name(&s.name, NameRole::Variable)?;
64 }
65 for v in &m.variants {
66 if v.keys.is_empty() {
67 return Err(Error::NoKeys);
68 }
69 w.0.push('\n');
70 for (i, k) in v.keys.iter().enumerate() {
71 if i > 0 {
72 w.0.push(' ');
73 }
74 match k {
75 Key::CatchAll(_) => w.0.push('*'),
76 Key::Literal(l) => w.literal(l)?,
77 _ => return Err(Error::UnknownNode),
78 }
79 }
80 w.0.push(' ');
81 w.quoted_pattern(&v.value)?;
82 }
83 }
84 _ => return Err(Error::UnknownNode),
85 }
86 Ok(w.0)
87}
88
89fn reads_as_complex(s: &str) -> bool {
92 let b = s.as_bytes();
93 let mut i = 0;
94 while let Some((n, _)) = chars::trivia_at(b, i) {
95 i += n;
96 }
97 b.get(i) == Some(&b'.')
98}
99
100struct Writer(String);
101
102impl Writer {
103 fn declarations(&mut self, declarations: &[Declaration<'_>]) -> Result<(), Error> {
104 for d in declarations {
105 match d {
106 Declaration::Input(x) => {
107 if x.name != x.value.arg.name {
108 return Err(Error::InputNameMismatch);
109 }
110 self.0.push_str(".input {$");
111 self.name(&x.value.arg.name, NameRole::Variable)?;
112 self.expression_tail(x.value.function.as_ref(), &x.value.attributes)?;
113 }
114 Declaration::Local(x) => {
115 self.0.push_str(".local $");
116 self.name(&x.name, NameRole::Variable)?;
117 self.0.push_str(" = ");
118 self.expression(&x.value)?;
119 }
120 _ => return Err(Error::UnknownNode),
121 }
122 self.0.push('\n');
123 }
124 Ok(())
125 }
126
127 fn quoted_pattern(&mut self, p: &Pattern<'_>) -> Result<(), Error> {
128 self.0.push_str("{{");
129 self.pattern(p)?;
130 self.0.push_str("}}");
131 Ok(())
132 }
133
134 fn pattern(&mut self, p: &Pattern<'_>) -> Result<(), Error> {
135 for part in p.parts() {
136 match part {
137 PatternPart::Text(t) => self.text(t)?,
138 PatternPart::Expression(e) => self.expression(e)?,
139 PatternPart::Markup(m) => self.markup(m)?,
140 _ => return Err(Error::UnknownNode),
141 }
142 }
143 Ok(())
144 }
145
146 fn text(&mut self, t: &str) -> Result<(), Error> {
147 for c in t.chars() {
148 match c {
149 '\0' => return Err(Error::Nul),
150 '\\' | '{' | '}' => {
151 self.0.push('\\');
152 self.0.push(c);
153 }
154 c => self.0.push(c),
155 }
156 }
157 Ok(())
158 }
159
160 fn expression(&mut self, e: &Expression<'_>) -> Result<(), Error> {
161 self.0.push('{');
162 match e {
163 Expression::Literal(x) => {
164 self.literal(&x.arg)?;
165 self.expression_tail(x.function.as_ref(), &x.attributes)
166 }
167 Expression::Variable(x) => {
168 self.0.push('$');
169 self.name(&x.arg.name, NameRole::Variable)?;
170 self.expression_tail(x.function.as_ref(), &x.attributes)
171 }
172 Expression::Function(x) => {
173 self.function(&x.function)?;
174 self.attributes(&x.attributes)?;
175 self.0.push('}');
176 Ok(())
177 }
178 _ => Err(Error::UnknownNode),
179 }
180 }
181
182 fn expression_tail(
184 &mut self,
185 function: Option<&FunctionRef<'_>>,
186 attributes: &Attributes<'_>,
187 ) -> Result<(), Error> {
188 if let Some(f) = function {
189 self.0.push(' ');
190 self.function(f)?;
191 }
192 self.attributes(attributes)?;
193 self.0.push('}');
194 Ok(())
195 }
196
197 fn function(&mut self, f: &FunctionRef<'_>) -> Result<(), Error> {
198 self.0.push(':');
199 self.name(&f.name, NameRole::Function)?;
200 self.options(&f.options)
201 }
202
203 fn options(&mut self, options: &Options<'_>) -> Result<(), Error> {
204 for (name, value) in options.iter() {
205 self.0.push(' ');
206 self.name(name, NameRole::Option)?;
207 self.0.push('=');
208 match value {
209 OptionValue::Literal(l) => self.literal(l)?,
210 OptionValue::Variable(v) => {
211 self.0.push('$');
212 self.name(&v.name, NameRole::Variable)?;
213 }
214 _ => return Err(Error::UnknownNode),
215 }
216 }
217 Ok(())
218 }
219
220 fn attributes(&mut self, attributes: &Attributes<'_>) -> Result<(), Error> {
221 for (name, value) in attributes.iter() {
222 self.0.push_str(" @");
223 self.name(name, NameRole::Attribute)?;
224 if let Some(l) = value {
225 self.0.push('=');
226 self.literal(l)?;
227 }
228 }
229 Ok(())
230 }
231
232 fn markup(&mut self, m: &Markup<'_>) -> Result<(), Error> {
233 self.0.push_str(match m.kind {
234 MarkupKind::Close => "{/",
235 MarkupKind::Open | MarkupKind::Standalone => "{#",
236 });
237 self.name(&m.name, NameRole::Markup)?;
238 self.options(&m.options)?;
239 self.attributes(&m.attributes)?;
240 self.0.push_str(match m.kind {
241 MarkupKind::Standalone => " /}",
242 MarkupKind::Open | MarkupKind::Close => "}",
243 });
244 Ok(())
245 }
246
247 fn literal(&mut self, l: &Literal<'_>) -> Result<(), Error> {
248 if chars::is_unquoted_literal(&l.value) {
249 self.0.push_str(&l.value);
250 return Ok(());
251 }
252 self.0.push('|');
253 for c in l.value.chars() {
254 match c {
255 '\0' => return Err(Error::Nul),
256 '\\' | '|' => {
257 self.0.push('\\');
258 self.0.push(c);
259 }
260 c => self.0.push(c),
261 }
262 }
263 self.0.push('|');
264 Ok(())
265 }
266
267 fn name(&mut self, name: &str, role: NameRole) -> Result<(), Error> {
270 let ok = match role {
271 NameRole::Variable => chars::is_name(name),
272 _ => chars::is_identifier(name),
273 };
274 if !ok {
275 return Err(Error::InvalidName(role));
276 }
277 self.0.push_str(name);
278 Ok(())
279 }
280}