macroex_extras/
angle.rs

1
2use macroex::{*, proc_macro2::*};
3/// Parses an angle.
4/// # Cases
5/// ## from_one
6/// * `f32`
7/// * `pi`
8/// * `[from_many]`
9
10/// ## from_many
11
12/// * `45 degrees` or `45.0 deg`
13/// * `1.2 rad` or `0 radians`
14/// * `pi`
15/// * `pi/2` or  `pi/2.0`
16/// * `2 pi` or  `2.0 pi`
17/// * `2/3 pi` or  `2.0 / 3.0 pi`
18#[derive(Debug, Clone, Copy, PartialEq)]
19pub struct Angle(pub f32);
20
21pub const PI: f32 = std::f32::consts::PI;
22ident_validator!(LitPi "pi");
23impl FromMacro for Angle {
24
25    fn from_one(tt: proc_macro2::TokenTree) -> Result<Self, Error> {
26        match Either3::from_one(tt)? {
27            Either3::A(Number(f)) => Ok(Self(f)),
28            Either3::B(LitPi) => Ok(Self(PI)),
29            Either3::C(Bracketed(tokens)) => Self::from_many(tokens)
30        }
31    }
32
33    fn from_many(tokens: proc_macro2::TokenStream) -> Result<Self, Error> {
34        let mut iter = tokens.into_iter();
35        match iter.extract()? {
36            Either3::A(LitPi) => {
37                if let Ok(PunctOf::<'/'>) = iter.extract(){
38                    let Number::<f32>(numer) = iter.extract()?;
39                    let EndOfStream = iter.extract()?;
40                    Ok(Self(PI / numer))
41                } else {
42                    let EndOfStream = iter.extract()?;
43                    Ok(Self(PI))
44                }
45            },
46            Either3::B(Number(value)) => {
47                match iter.extract()? {
48                    OrEndOfStream(Some(Either::A(Spanned(span, IdentString(s))))) => {
49                        match s.as_str() {
50                            "pi" => Ok(Self(value * PI)),
51                            "rad" | "radians" => Ok(Self(value)),
52                            "deg" | "degrees" => Ok(Self(value * PI / 180.0)),
53                            _ => bail!(span, r#"Expected "pi", "rad", "radians", "deg" or "degrees", found {}"#, s)
54                        }
55                    },
56                    OrEndOfStream(Some(Either::B(PunctOf::<'/'>))) => {
57                        let Number::<f32>(numer) = iter.extract()?;
58                        let LitPi = iter.extract()?;
59                        Ok(Self(value / numer * PI))
60                    },
61                    OrEndOfStream(None) => {
62                        Ok(Self(value))
63                    }
64                }
65            },
66            Either3::C(PunctOf::<'-'>) => {
67                let angle = Self::from_many(iter.collect())?;
68                Ok(Self(-angle.0))
69            }
70        }
71    }
72
73    /// The brace case will be treated as an expression.
74    fn peek(tt: &proc_macro2::TokenTree) -> bool {
75        match tt {
76            proc_macro2::TokenTree::Group(g) => {
77                g.delimiter() != Delimiter::Brace
78            },
79            _ => true,
80        }
81    }
82}
83
84impl quote::ToTokens for Angle {
85    fn to_tokens(&self, tokens: &mut TokenStream) {
86        self.0.to_tokens(tokens)
87    }
88}