Skip to main content

zyn_core/ast/
pipe_node.rs

1use 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}