zyn_core/ast/
pipe_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 crate::Expand;
14use crate::pascal;
15
16pub struct PipeNode {
26 pub span: Span,
28 pub name: syn::Ident,
30 pub args: Vec<TokenStream>,
32}
33
34impl PipeNode {
35 pub fn span(&self) -> Span {
36 self.span
37 }
38}
39
40impl Parse for PipeNode {
41 fn parse(input: ParseStream) -> syn::Result<Self> {
42 let name: syn::Ident = input.parse()?;
43 let span = name.span();
44
45 let mut args = Vec::new();
46
47 while input.peek(Token![:]) {
48 input.parse::<Token![:]>()?;
49
50 let mut arg = TokenStream::new();
51
52 while !input.is_empty() && !input.peek(Token![:]) && !input.peek(Token![|]) {
53 let tt: TokenTree = input.parse()?;
54 tt.to_tokens(&mut arg);
55 }
56
57 args.push(arg);
58 }
59
60 Ok(Self { span, name, args })
61 }
62}
63
64const BUILTIN_PIPES: &[&str] = &[
65 "upper",
66 "lower",
67 "snake",
68 "camel",
69 "pascal",
70 "kebab",
71 "screaming",
72 "ident",
73 "fmt",
74 "str",
75 "trim",
76 "plural",
77 "singular",
78];
79
80impl Expand for PipeNode {
81 fn expand(&self, _output: &Ident, _idents: &mut crate::ident::Iter) -> TokenStream {
82 let pascal_name = pascal!(self.name => ident);
83 let is_builtin = BUILTIN_PIPES.contains(&self.name.to_string().as_str());
84
85 if is_builtin {
86 if self.name == "trim" {
87 match self.args.as_slice() {
88 [] => {
89 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::Trim(" ", " ")), __zyn_val); }
90 }
91 [a] => {
92 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::Trim(#a, #a)), __zyn_val); }
93 }
94 [a, b] => {
95 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::Trim(#a, #b)), __zyn_val); }
96 }
97 _ => quote! { compile_error!("trim pipe accepts at most 2 arguments"); },
98 }
99 } else if self.args.is_empty() {
100 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::#pascal_name), __zyn_val); }
101 } else {
102 let args = &self.args;
103 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::#pascal_name(#(#args),*)), __zyn_val); }
104 }
105 } else if self.args.is_empty() {
106 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(#pascal_name), __zyn_val); }
107 } else {
108 let args = &self.args;
109 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(#pascal_name(#(#args),*)), __zyn_val); }
110 }
111 }
112}