1use super::framework::*;
4
5#[derive(Debug)]
11pub struct Accumulator {
12 def_span: Span,
13 out_span: Option<Span>,
14 text: String,
15 errors: ErrorAccumulator,
16}
17
18impl Accumulator {
19 pub fn finish_literal(self) -> syn::Result<syn::LitStr> {
20 let out_span = self.out_span.unwrap_or(self.def_span);
21 let text = self.errors.finish_with(self.text)?;
22 let lit = syn::LitStr::new(&text, out_span);
23 Ok(lit)
24 }
25
26 pub fn finish_onto<O>(self, np: &O::NotInPaste, out: &mut O)
27 where
28 O: ExpansionOutput,
29 {
30 let _: O::NotInPaste = *np;
32
33 (|| {
34 let lit = self.finish_literal()?;
35 out.append_syn_litstr(&lit);
36 Ok(())
37 })()
38 .unwrap_or_else(|err| out.record_error(err))
39 }
40
41 fn append_display(&mut self, span: Span, plain: impl Display + Spanned) {
42 self.out_span.get_or_insert(span);
43 write!(&mut self.text, "{}", plain).expect("Display onto String")
44 }
45
46 fn append_type_like(&mut self, tokens: TokenStream) {
70 recurse_display_type_like(
71 &mut self.text,
72 &mut Prev::NeverSpace,
73 tokens,
74 )
75 .expect("formatting type-like tokens into String failed");
76 }
77}
78
79enum Prev {
81 NeverSpace,
83 OpenBrace,
85 Other,
87}
88
89fn recurse_display_type_like(
91 out: &mut String,
92 prev: &mut Prev,
93 tokens: TokenStream,
94) -> fmt::Result {
95 for tt in tokens {
96 let def_spc = match &prev {
97 Prev::NeverSpace => "",
98 _other => " ",
99 };
100
101 let wr; let cat; match tt {
104 TT::Punct(p) => {
105 let p = p.as_char();
106 match p {
107 '<' | '>' | ':' => {
108 wr = write!(out, "{}", p);
109 cat = Prev::NeverSpace;
110 }
111 '&' | '\'' => {
112 wr = write!(out, "{}{}", def_spc, p);
113 cat = Prev::NeverSpace;
114 }
115 ',' => {
116 wr = write!(out, "{}", p);
117 cat = Prev::Other;
118 }
119 other => {
120 wr = write!(out, "{}{}", def_spc, other);
121 cat = Prev::Other;
122 }
123 }
124 }
125 TT::Group(g) => {
126 let delim = g.delimiter();
127 let mut normal = |open, close| {
128 write!(out, "{}{}", def_spc, open)?;
129 recurse_display_type_like(
130 out,
131 &mut Prev::NeverSpace,
132 g.stream(),
133 )?;
134 write!(out, "{}", close)
135 };
136 match delim {
137 Delimiter::Brace => {
138 write!(out, "{}{{", def_spc)?;
139 let mut inner_prev = Prev::OpenBrace;
140 recurse_display_type_like(
141 out,
142 &mut inner_prev,
143 g.stream(),
144 )?;
145 wr = match inner_prev {
146 Prev::OpenBrace => write!(out, "}}"),
147 _other => write!(out, " }}"),
148 };
149 cat = Prev::Other;
150 }
151 Delimiter::Parenthesis => {
152 wr = normal('(', ')');
153 cat = Prev::Other;
154 }
155 Delimiter::Bracket => {
156 wr = normal('[', ']');
157 cat = Prev::Other;
158 }
159 Delimiter::None => {
160 wr = normal('«', '»');
161 cat = Prev::Other;
162 }
163 }
164 }
165 other => {
166 wr = write!(out, "{}{}", def_spc, other);
167 cat = Prev::Other;
168 }
169 }
170
171 let () = wr?;
172 *prev = cat;
173 }
174 Ok(())
175}
176
177impl SubstParseContext for Accumulator {
178 type NotInPaste = ();
179 type NotInConcat = Void;
180 type NotInBool = ();
181 type BoolOnly = Void;
182 type ConcatOnly = ();
183 type DbgContent = Template<Self>;
184 type SpecialParseContext = ();
185
186 fn not_in_paste(_: &impl Spanned) -> syn::Result<()> {
187 Ok(())
188 }
189 fn not_in_concat(span: &impl Spanned) -> syn::Result<Void> {
190 Err(span.error("not allowed in within ${concat ...}"))
191 }
192 fn not_in_bool(_: &impl Spanned) -> syn::Result<()> {
193 Ok(())
194 }
195 fn concat_only(_: &impl Spanned) -> syn::Result<()> {
196 Ok(())
197 }
198}
199
200impl ExpansionOutput for Accumulator {
201 fn append_identfrag_toks<I: IdentFrag>(
202 &mut self,
203 ident: &I,
204 ) -> Result<(), I::BadIdent> {
205 self.append_display(ident.span(), ident.fragment());
206 Ok(())
207 }
208 fn append_idpath<A, B, I>(
209 &mut self,
210 _te_span: Span,
211 pre: A,
212 ident: &I,
213 post: B,
214 _grouping: Grouping,
215 ) -> Result<(), I::BadIdent>
216 where
217 A: FnOnce(&mut TokenAccumulator),
218 B: FnOnce(&mut TokenAccumulator),
219 I: IdentFrag,
220 {
221 match (|| {
222 let mut ta = TokenAccumulator::new();
223 pre(&mut ta);
224 ta.with_tokens(|ts| ident.frag_to_tokens(ts));
225 post(&mut ta);
226 ta.tokens()
227 })() {
228 Ok(y) => self.append_type_like(y),
229 Err(e) => self.errors.push(e),
230 }
231 Ok(())
232 }
233 fn append_syn_litstr(&mut self, lit: &syn::LitStr) {
234 if lit.suffix() != "" {
235 self.errors
236 .push(lit.error("string suffix forbidden in ${concat }"));
237 return;
238 }
239 self.append_display(lit.span(), lit.value());
240 }
241 fn append_syn_type(
242 &mut self,
243 te_span: Span,
244 v: syn::Type,
245 grouping: Grouping,
246 ) {
247 self.append_syn_type_inner(te_span, v, grouping);
248 }
249 fn append_syn_type_inner(
250 &mut self,
251 _te_span: Span,
252 ty: syn::Type,
253 _grouping: Grouping,
254 ) {
255 self.append_type_like(ty.to_token_stream());
256 }
257 fn dbg_expand<'c>(
258 &mut self,
259 kw_span: Span,
260 ctx: GeneralContext<'c>,
261 msg: &mut String,
262 content: &Template<Accumulator>,
263 ) -> fmt::Result {
264 let mut child = Accumulator::new_with_span(kw_span);
265 content.expand(ctx, &mut child);
266 match child.errors.examine() {
267 Some(e) => write!(msg, "/* ERROR: {} */", e)?,
268 None => write!(msg, "{}", child.text)?,
269 }
270 child.finish_onto(&(), self);
271 Ok(())
272 }
273
274 fn new_with_span(kw_span: Span) -> Self {
275 Accumulator {
276 def_span: kw_span,
277 out_span: None,
278 text: String::new(),
279 errors: ErrorAccumulator::default(),
280 }
281 }
282 fn ignore_impl(self) -> syn::Result<()> {
283 self.errors.finish()
284 }
285
286 fn append_tokens_with(
287 &mut self,
288 (_not_in_paste, not_in_concat): &((), Void),
289 _: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
290 ) -> syn::Result<()> {
291 void::unreachable(*not_in_concat)
292 }
293
294 fn append_bool_only(&mut self, bool_only: &Self::BoolOnly) -> ! {
295 void::unreachable(*bool_only)
296 }
297
298 fn record_error(&mut self, err: syn::Error) {
299 self.errors.push(err);
300 }
301}
302
303impl Expand<Accumulator> for TemplateElement<Accumulator> {
304 fn expand<'c>(
305 &self,
306 ctx: GeneralContext<'c>,
307 out: &mut Accumulator,
308 ) -> syn::Result<()> {
309 match self {
310 TE::Ident(_ident, not_in_concat) => {
311 void::unreachable(*not_in_concat);
314 }
315 TE::LitStr(lit) => out.append_syn_litstr(&lit),
316 TE::Subst(e) => e.expand(ctx, out)?,
317 TE::Repeat(e) => e.expand(ctx, out),
318 TE::Literal(_, allow_tokens)
319 | TE::Punct(_, allow_tokens)
320 | TE::Group { allow_tokens, .. } => {
321 void::unreachable(allow_tokens.1)
322 }
323 }
324 Ok(())
325 }
326}