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