zyn_core/ast/
interp_node.rs1use proc_macro2::Ident;
2use proc_macro2::Span;
3use proc_macro2::TokenStream;
4use proc_macro2::TokenTree;
5
6use quote::ToTokens;
7use quote::quote;
8
9use syn::Token;
10use syn::parse::Parse;
11use syn::parse::ParseStream;
12
13use super::PipeNode;
14
15use crate::Expand;
16
17pub struct InterpNode {
22 pub span: Span,
24 pub expr: TokenStream,
26 pub pipes: Vec<PipeNode>,
28}
29
30impl InterpNode {
31 pub fn span(&self) -> Span {
32 self.span
33 }
34
35 pub fn to_display_stream(&self, injections: &[(String, TokenStream)]) -> TokenStream {
36 let key = self.expr.to_string();
37
38 if let Some((_, val)) = injections.iter().find(|(k, _)| *k == key) {
39 if self.pipes.is_empty() {
40 return val.clone();
41 }
42
43 let mut s = val.to_string();
44
45 for pipe in &self.pipes {
46 s = pipe.apply_display(s);
47 }
48
49 return syn::parse_str::<TokenStream>(&s).unwrap_or_else(|_| val.clone());
50 }
51
52 let expr = &self.expr;
54
55 if self.pipes.is_empty() {
56 quote::quote! { {{ #expr }} }
57 } else {
58 let pipe_tokens: TokenStream = self
59 .pipes
60 .iter()
61 .flat_map(|p| {
62 let name = &p.name;
63 std::iter::once(quote::quote! { | #name })
64 })
65 .collect();
66
67 quote::quote! { {{ #expr #pipe_tokens }} }
68 }
69 }
70}
71
72impl Parse for InterpNode {
73 fn parse(input: ParseStream) -> syn::Result<Self> {
74 let content;
75 let brace = syn::braced!(content in input);
76 let span = brace.span.join();
77
78 let inner;
79 syn::braced!(inner in content);
80
81 let mut expr = TokenStream::new();
82 let mut pipes = Vec::new();
83
84 while !inner.is_empty() {
85 if inner.peek(Token![|]) {
86 inner.parse::<Token![|]>()?;
87 pipes.push(inner.parse::<PipeNode>()?);
88 } else if pipes.is_empty() {
89 let tt: TokenTree = inner.parse()?;
90 tt.to_tokens(&mut expr);
91 } else {
92 break;
93 }
94 }
95
96 if expr.is_empty() {
97 return Err(syn::Error::new(span, "empty interpolation"));
98 }
99
100 Ok(Self { span, expr, pipes })
101 }
102}
103
104impl Expand for InterpNode {
105 fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
106 let expr = &self.expr;
107
108 if self.pipes.is_empty() {
109 return quote! {
110 ::zyn::quote::ToTokens::to_tokens(&(#expr), &mut #output);
111 };
112 }
113
114 let mut steps = vec![quote! { let __zyn_val = (#expr).to_string(); }];
115
116 for (i, pipe) in self.pipes.iter().enumerate() {
117 if i > 0 {
118 steps.push(quote! { let __zyn_val = __zyn_val.to_string(); });
119 }
120
121 steps.push(pipe.expand(output, idents));
122 }
123
124 quote! {
125 {
126 #(#steps)*
127 ::zyn::quote::ToTokens::to_tokens(&__zyn_val, &mut #output);
128 }
129 }
130 }
131}