deborrow_macro/
lib.rs

1use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
2
3/// A safe way to borrow multiple fields of a struct mutably at once.
4///
5/// This is just as safe to having an impl on the struct which takes &mut self
6/// and then accesses multiple of its own fields mutably, aka. completely safe.
7#[proc_macro]
8pub fn deborrow(ts: TokenStream) -> TokenStream {
9    let mut ts = ts.into_iter();
10    let item = ts
11        .next()
12        .expect("expected item in deborrow!($item $(,$|:) $($field $(,)? )*)");
13    if !matches!(
14        ts.next(),
15        Some(TokenTree::Punct(x))
16        if x.as_char() == ',' || x.as_char() == ':'
17    ) {
18        panic!("expected , in deborrow!($item $(,$|:) $($field $(,)? )*)");
19    }
20    let fields = ts
21        .filter(|x| !matches!(x, TokenTree::Punct(_)))
22        .collect::<Vec<_>>();
23    let mut result = Vec::new();
24    result.append(
25        &mut ("fn __deborrow_unify<'a, "
26            .parse::<TokenStream>()
27            .unwrap()
28            .into_iter()
29            .collect::<Vec<_>>()),
30    );
31    result.append(
32        &mut fields
33            .iter()
34            .flat_map(|x| {
35                vec![
36                    TokenTree::Ident(Ident::new(
37                        &("T".to_owned() + &x.to_string()),
38                        Span::mixed_site(),
39                    )),
40                    TokenTree::Punct(Punct::new(',', Spacing::Alone)),
41                ]
42            })
43            .collect::<Vec<_>>(),
44    );
45    result.push(TokenTree::Punct(Punct::new('>', Spacing::Alone)));
46    result.push(TokenTree::Group(Group::new(
47        Delimiter::Parenthesis,
48        TokenStream::from_iter(fields.iter().flat_map(|x| {
49            vec![
50                x.to_owned(),
51                TokenTree::Punct(Punct::new(':', Spacing::Alone)),
52                TokenTree::Punct(Punct::new('&', Spacing::Joint)),
53                TokenTree::Punct(Punct::new('\'', Spacing::Joint)),
54                TokenTree::Ident(Ident::new("a", Span::mixed_site())),
55                TokenTree::Ident(Ident::new("mut", Span::mixed_site())),
56                TokenTree::Ident(Ident::new(
57                    &("T".to_owned() + &x.to_string()),
58                    Span::mixed_site(),
59                )),
60                TokenTree::Punct(Punct::new(',', Spacing::Alone)),
61            ]
62        })),
63    )));
64    result.append(
65        &mut ("->"
66            .parse::<TokenStream>()
67            .unwrap()
68            .into_iter()
69            .collect::<Vec<_>>()),
70    );
71    result.push(TokenTree::Group(Group::new(
72        Delimiter::Parenthesis,
73        TokenStream::from_iter(fields.iter().flat_map(|x| {
74            vec![
75                TokenTree::Punct(Punct::new('&', Spacing::Joint)),
76                TokenTree::Punct(Punct::new('\'', Spacing::Joint)),
77                TokenTree::Ident(Ident::new("a", Span::mixed_site())),
78                TokenTree::Ident(Ident::new("mut", Span::mixed_site())),
79                TokenTree::Ident(Ident::new(
80                    &("T".to_owned() + &x.to_string()),
81                    Span::mixed_site(),
82                )),
83                TokenTree::Punct(Punct::new(',', Spacing::Alone)),
84            ]
85        })),
86    )));
87    result.push(TokenTree::Group(Group::new(
88        Delimiter::Brace,
89        TokenTree::Group(Group::new(
90            Delimiter::Parenthesis,
91            TokenStream::from_iter(fields.iter().flat_map(|x| {
92                vec![
93                    x.to_owned(),
94                    TokenTree::Punct(Punct::new(',', Spacing::Alone)),
95                ]
96            })),
97        ))
98        .into(),
99    )));
100    let mut tuple = vec![];
101    for field in &fields[0..fields.len() - 1] {
102        tuple.push(TokenTree::Ident(Ident::new("unsafe", Span::mixed_site())));
103        let mut e = ("::deborrow::deborrow_mut".parse::<TokenStream>().unwrap())
104            .into_iter()
105            .collect::<Vec<_>>();
106        e.push(TokenTree::Group(Group::new(
107            Delimiter::Parenthesis,
108            TokenStream::from_iter(vec![
109                TokenTree::Punct(Punct::new('&', Spacing::Joint)),
110                TokenTree::Ident(Ident::new("mut", Span::mixed_site())),
111                item.clone(),
112                TokenTree::Punct(Punct::new('.', Spacing::Alone)),
113                field.clone(),
114            ]),
115        )));
116        tuple.push(TokenTree::Group(Group::new(
117            Delimiter::Brace,
118            TokenStream::from_iter(e.into_iter()),
119        )));
120        tuple.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
121    }
122    if !fields.is_empty() {
123        tuple.push(TokenTree::Group(Group::new(
124            Delimiter::Parenthesis,
125            TokenStream::from_iter(vec![
126                TokenTree::Punct(Punct::new('&', Spacing::Joint)),
127                TokenTree::Ident(Ident::new("mut", Span::mixed_site())),
128                item,
129                TokenTree::Punct(Punct::new('.', Spacing::Alone)),
130                fields[fields.len() - 1].clone(),
131            ]),
132        )));
133        tuple.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
134    }
135    result.push(TokenTree::Ident(Ident::new(
136        "__deborrow_unify",
137        Span::mixed_site(),
138    )));
139    result.push(TokenTree::Group(Group::new(
140        Delimiter::Parenthesis,
141        TokenStream::from_iter(tuple.into_iter()),
142    )));
143    TokenStream::from(TokenTree::Group(Group::new(
144        Delimiter::Brace,
145        TokenStream::from_iter(result.into_iter()),
146    )))
147}