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
64fn is_builtin(name: &str) -> bool {
65 matches!(
66 name,
67 "upper"
68 | "lower"
69 | "snake"
70 | "camel"
71 | "pascal"
72 | "kebab"
73 | "screaming"
74 | "ident"
75 | "fmt"
76 | "str"
77 | "trim"
78 | "plural"
79 | "singular"
80 )
81}
82
83impl Expand for PipeNode {
84 fn expand(&self, _output: &Ident, _idents: &mut crate::ident::Iter) -> TokenStream {
85 let pascal_name = pascal!(self.name => ident);
86 let name_str = self.name.to_string();
87
88 if is_builtin(&name_str) {
89 if name_str == "trim" {
90 match self.args.as_slice() {
91 [] => {
92 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::Trim(" ", " ")), __zyn_val); }
93 }
94 [a] => {
95 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::Trim(#a, #a)), __zyn_val); }
96 }
97 [a, b] => {
98 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::Trim(#a, #b)), __zyn_val); }
99 }
100 _ => quote! { compile_error!("trim pipe accepts at most 2 arguments"); },
101 }
102 } else if self.args.is_empty() {
103 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::#pascal_name), __zyn_val); }
104 } else {
105 let args = &self.args;
106 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(::zyn::pipes::#pascal_name(#(#args),*)), __zyn_val); }
107 }
108 } else if self.args.is_empty() {
109 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(#pascal_name), __zyn_val); }
110 } else {
111 let args = &self.args;
112 quote! { let __zyn_val = ::zyn::Pipe::pipe(&(#pascal_name(#(#args),*)), __zyn_val); }
113 }
114 }
115}