odra_ir/
mapping.rs

1use proc_macro2::{TokenStream, TokenTree};
2use quote::ToTokens;
3use syn::{bracketed, parse::ParseStream};
4
5#[derive(Debug)]
6pub struct MapExpr {
7    pub root_mapping: syn::Expr,
8    pub segments: Vec<syn::Expr>,
9    pub assign_token: Option<syn::Token![=]>,
10    pub assigned_value: Option<syn::Expr>
11}
12
13impl syn::parse::Parse for MapExpr {
14    fn parse(input: ParseStream) -> syn::Result<Self> {
15        let mut root_expr_stream = TokenStream::new();
16        input.step(|cursor| {
17            let mut rest = *cursor;
18            while let Some((tt, next)) = rest.token_tree() {
19                if matches!(tt, TokenTree::Group(_)) {
20                    return Ok(((), rest));
21                } else {
22                    tt.to_tokens(&mut root_expr_stream);
23                    rest = next;
24                }
25            }
26            Err(cursor.error("no `TokenTree::Group` was found after this point"))
27        })?;
28
29        let mut segments = Vec::new();
30        while !input.is_empty() && !input.lookahead1().peek(syn::Token![=]) {
31            let content;
32            let _bracket_token = bracketed!(content in input);
33
34            while !content.is_empty() {
35                segments.push(content.parse()?);
36            }
37        }
38
39        if segments.is_empty() {
40            return Err(input.error("expected at least one segment"));
41        }
42
43        let root_mapping = syn::parse2(root_expr_stream)?;
44
45        if !input.is_empty() {
46            let assign_token = input.parse()?;
47            let assigned_value = input.parse()?;
48            Ok(Self {
49                root_mapping,
50                segments,
51                assign_token: Some(assign_token),
52                assigned_value: Some(assigned_value)
53            })
54        } else {
55            Ok(Self {
56                root_mapping,
57                segments,
58                assign_token: None,
59                assigned_value: None
60            })
61        }
62    }
63}
64
65#[cfg(test)]
66mod test {
67    use crate::MapExpr;
68
69    #[test]
70    fn basic_parsing_works() {
71        let simple_expr = syn::parse_str::<MapExpr>("self.tokens[a]").unwrap();
72
73        assert_eq!(simple_expr.segments.len(), 1);
74        assert_eq!(simple_expr.assign_token, None);
75        assert_eq!(simple_expr.assigned_value, None);
76
77        let complex_expr = syn::parse_str::<MapExpr>("self.tokens[a][b][c][d][e]").unwrap();
78
79        assert_eq!(complex_expr.segments.len(), 5);
80        assert_eq!(complex_expr.assign_token, None);
81        assert_eq!(complex_expr.assigned_value, None);
82
83        let invalid_expr = syn::parse_str::<MapExpr>("self.tokens[a][b"); // missing closing bracket
84        assert!(invalid_expr.is_err());
85
86        let invalid_expr = syn::parse_str::<MapExpr>("self.tokens.get(a)"); // invalid syntax
87        assert!(invalid_expr.is_err());
88
89        let invalid_expr = syn::parse_str::<MapExpr>("self.tokens(a)(b)"); // parenthesis found, brackets expected
90        assert!(invalid_expr.is_err());
91
92        let invalid_expr = syn::parse_str::<MapExpr>("self.tokens"); // no brackets found
93        assert!(invalid_expr.is_err());
94    }
95
96    #[test]
97    fn assigning_parsing_works() {
98        let simple_expr = syn::parse_str::<MapExpr>("self.tokens[a] = 1").unwrap();
99
100        assert_eq!(simple_expr.segments.len(), 1);
101        assert!(simple_expr.assign_token.is_some());
102        assert!(simple_expr.assigned_value.is_some());
103
104        let complex_expr =
105            syn::parse_str::<MapExpr>("self.tokens[a][b][c][d][e] = String::from(3)").unwrap();
106
107        assert_eq!(complex_expr.segments.len(), 5);
108        assert!(complex_expr.assign_token.is_some());
109        assert!(complex_expr.assigned_value.is_some());
110    }
111
112    #[test]
113    fn parsing_complex_expressions_works() {
114        let simple_expr = syn::parse_str::<MapExpr>(
115            "get_mapping[self.build_key()][String::from(1)] = calculate_value()"
116        )
117        .unwrap();
118
119        assert_eq!(simple_expr.segments.len(), 2);
120        assert!(simple_expr.assign_token.is_some());
121        assert!(simple_expr.assigned_value.is_some());
122    }
123}