simple_ternary/
lib.rs

1
2extern crate proc_macro;
3
4use quote::quote;
5use syn::{self, parse::{Parse, ParseStream}, parse_macro_input, Token};
6
7// ternary: expression '?' expression ':' expression
8// expression: ternary | simple
9
10struct Ternary {
11    condition: syn::Expr,
12    true_branch: Expression,
13    false_branch: Expression,
14}
15
16impl Parse for Ternary {
17    fn parse(input: ParseStream) -> syn::Result<Self> {
18        let condition = input.parse()?;
19
20        input.parse::<Token![=>]>()?;
21        
22        let true_branch = input.parse()?;
23        input.parse::<Token![:]>()?;
24        let false_branch = input.parse()?;
25
26        Ok(Self {
27            condition,
28            true_branch,
29            false_branch,
30        })
31    }
32}
33
34impl quote::ToTokens for Ternary {
35    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
36        let condition = &self.condition;
37
38        let true_branch = match &self.true_branch {
39            Expression::Ternary(ternary) => quote! { #ternary },
40            Expression::Simple(simple) => quote! { #simple },
41        };
42
43        let false_branch = match &self.false_branch {
44            Expression::Ternary(ternary) => quote! { #ternary },
45            Expression::Simple(simple) => quote! { #simple },
46        };
47
48        tokens.extend(quote! { if #condition { #true_branch } else { #false_branch } });
49    }
50}
51struct SimpleExpression {
52    expr: syn::Expr,
53}
54
55impl Parse for SimpleExpression {
56    fn parse(input: ParseStream) -> syn::Result<Self> {
57        Ok(Self {
58            expr: input.parse()?,
59        })
60    }
61}
62
63impl quote::ToTokens for SimpleExpression {
64    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
65        let expr = &self.expr;
66        tokens.extend(quote! { #expr });
67    }
68}
69
70enum Expression {
71    Ternary(Box<Ternary>),
72    Simple(SimpleExpression),
73}
74
75impl Parse for Expression {
76    fn parse(input: ParseStream) -> syn::Result<Self> {
77        let expr: syn::Expr = input.parse()?;
78
79        if input.peek(Token![=>]) {
80            input.parse::<Token![=>]>()?;
81            let true_branch = input.parse()?;
82            input.parse::<Token![:]>()?;
83            let false_branch = input.parse()?;
84
85            return Ok(Self::Ternary(Box::new(Ternary {
86                condition: expr,
87                true_branch,
88                false_branch,
89            })))
90        }
91    
92        Ok(Self::Simple(SimpleExpression { expr }))
93    }
94}
95
96/// A macro that evaluates a ternary expression, similar to the ternary operator in other languages.
97///
98/// # Syntax
99///
100/// ```rust
101/// tnr!{ condition => true_expr : false_expr }
102/// ```
103///
104/// - `condition`: An expression that evaluates to a boolean.
105/// - `true_expr`: The expression to return if the condition is `true`.
106/// - `false_expr`: The expression to return if the condition is `false`.
107///
108/// # Examples
109///
110/// ## Basic Usage
111///
112/// ```rust
113/// let age = 20;
114/// let category = tnr!{ age < 18 => "child" : "adult" };
115/// assert_eq!(category, "adult");
116/// ```
117///
118/// ## Chaining Ternary Expressions
119///
120/// Ternary expressions can be chained to handle multiple conditions:
121///
122/// ```rust
123/// let score = 85;
124/// let grade = tnr! {
125///     score >= 90 => "A" :
126///     score >= 80 => "B" :
127///     score >= 70 => "C" :
128///     score >= 60 => "D" : "F"
129/// };
130/// assert_eq!(grade, "B");
131/// ```
132/// This macro evaluates the condition and returns the corresponding expression based on its truth value.
133///
134/// # Note
135/// * The `?` operator is replaced with the `=>` operator because `?`` is reserved for error handling in Rust.
136/// * Ensure that both `true_expr` and `false_expr` are of the same type or can be converted to a common type to avoid type mismatches.
137#[proc_macro]
138pub fn tnr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
139    let tern = parse_macro_input!(input as Ternary);
140    quote! { #tern }.into()
141}