irox_derive_helpers/
lib.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3
4//!
5//! Helper traits & functions for the [`proc_macro`] crate to aid in writing less complex derive macros.
6//!
7
8#![forbid(unsafe_code)]
9
10extern crate proc_macro;
11
12use irox_tools::iterators::Itertools;
13use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
14
15///
16/// Adds a bunch of helper methods to the [`TokenStream`] and [`TokenTree`] types.
17pub trait DeriveMethods: Extend<TokenStream> + Extend<TokenTree> {
18    /// Appends the specified name as an [`Ident`]
19    fn add_ident(&mut self, name: &str) {
20        self.extend(Self::create_ident(name))
21    }
22    /// Creates a literal [`Punct`] type using the provided character
23    fn create_punct(ch: char) -> TokenStream {
24        TokenStream::from_iter([TokenTree::Punct(Punct::new(ch, Spacing::Alone))])
25    }
26    /// Creates two literal adjoining [`Punct`] types using the provided characters
27    fn create_punct2(ch: char, ch2: char) -> TokenStream {
28        TokenStream::from_iter([
29            TokenTree::Punct(Punct::new(ch, Spacing::Joint)),
30            TokenTree::Punct(Punct::new(ch2, Spacing::Alone)),
31        ])
32    }
33    /// Creates a [`Literal`] using the provided string
34    fn create_literal(val: &str) -> TokenStream {
35        TokenStream::from_iter([TokenTree::Literal(Literal::string(val))])
36    }
37    /// Creates a [`Ident`] using the provided name
38    fn create_ident(name: &str) -> TokenStream {
39        TokenStream::from_iter([TokenTree::Ident(Ident::new(name, Span::call_site()))])
40    }
41    /// Creates a `&[name]` token stream.
42    fn create_ref_ident(name: &str) -> TokenStream {
43        TokenStream::from_iter([Self::create_punct('&'), Self::create_ident(name)])
44    }
45    /// Creates a `&'lifetime [name]` token stream.
46    fn create_ref_ident_lifetime(name: &str, lifetime: &str) -> TokenStream {
47        TokenStream::from_iter([
48            TokenStream::from_iter([
49                TokenTree::Punct(Punct::new('&', Spacing::Alone)),
50                TokenTree::Punct(Punct::new('\'', Spacing::Joint)),
51                TokenTree::Ident(Ident::new(lifetime, Span::call_site())),
52            ]),
53            Self::create_ident(name),
54        ])
55    }
56    /// Creates a `&'static [name]` token stream.
57    fn create_ref_ident_static(name: &str) -> TokenStream {
58        Self::create_ref_ident_lifetime(name, "static")
59    }
60    /// Creates a `&mut [name]` token stream;
61    fn create_mut_ref_ident(name: &str) -> TokenStream {
62        TokenStream::from_iter([
63            Self::create_punct('&'),
64            Self::create_ident("mut"),
65            Self::create_ident(name),
66        ])
67    }
68    /// Creates a `&'lifetime mut [name]` token stream;
69    fn create_mut_ref_ident_lifetime(name: &str, lifetime: &str) -> TokenStream {
70        TokenStream::from_iter([
71            TokenStream::from_iter([
72                TokenTree::Punct(Punct::new('&', Spacing::Alone)),
73                TokenTree::Punct(Punct::new('\'', Spacing::Joint)),
74                TokenTree::Ident(Ident::new(lifetime, Span::call_site())),
75            ]),
76            Self::create_ident("mut"),
77            Self::create_ident(name),
78        ])
79    }
80    /// Wraps the provided token stream with braces: `{ [inner] }`
81    fn create_wrapped_braces(inner: TokenStream) -> TokenStream {
82        TokenStream::from_iter([TokenTree::Group(Group::new(Delimiter::Brace, inner))])
83    }
84    /// Appends the specified character as a [`Punct`] type.
85    fn add_punc(&mut self, ch: char) {
86        self.extend(Self::create_punct(ch))
87    }
88    /// Appends 2 characters as sequential [`Punct`] types
89    fn add_punc2(&mut self, ch: char, ch2: char) {
90        self.extend([
91            TokenTree::Punct(Punct::new(ch, Spacing::Joint)),
92            TokenTree::Punct(Punct::new(ch2, Spacing::Alone)),
93        ])
94    }
95    /// Appends a comma (`,`) as a [`Punct`]
96    fn add_comma(&mut self) {
97        self.add_punc(',')
98    }
99    /// Wraps the provided token stream in generics (`<`...`>`)
100    fn wrap_generics(&mut self, inner: TokenStream) {
101        self.add_punc('<');
102        self.extend([inner]);
103        self.add_punc('>');
104    }
105
106    /// Appends two idents: `fn` and `name`
107    fn add_fn(&mut self, name: &str) {
108        self.add_ident("fn");
109        self.add_ident(name);
110    }
111    /// Appends named generics: `< {id} : {stream..} >`
112    fn add_generics(&mut self, id: &str, generics: TokenStream) {
113        self.add_punc('<');
114        self.add_ident(id);
115        self.add_punc(':');
116        self.extend(generics);
117        self.add_punc('>');
118    }
119    /// Creates the elements as a path, a series of [`Ident`]s separated by `::`
120    fn create_path(elems: &[&str]) -> TokenStream {
121        elems
122            .iter()
123            .map(|e| TokenTree::Ident(Ident::new(e, Span::call_site())))
124            .joining_multi(&[
125                TokenTree::Punct(Punct::new(':', Spacing::Joint)),
126                TokenTree::Punct(Punct::new(':', Spacing::Alone)),
127            ])
128            .collect()
129    }
130    /// Appends the elements as a path, a series of [`Ident`]s separated by `::`
131    fn add_path(&mut self, elems: &[&str]) {
132        self.extend(Self::create_path(elems))
133    }
134    /// Wraps the provided stream with parens: `( {stream} )`
135    fn add_parens(&mut self, inside_parens: TokenStream) {
136        self.extend([TokenTree::Group(Group::new(
137            Delimiter::Parenthesis,
138            inside_parens,
139        ))]);
140    }
141    /// Appends the single bar arrow: `->`
142    fn add_single_arrow(&mut self) {
143        self.add_punc2('-', '>')
144    }
145    /// Appends the double bar arrow: `=>`
146    fn add_double_arrow(&mut self) {
147        self.add_punc2('=', '>')
148    }
149    /// Appends a single match row, `[matching] => [result],`
150    fn append_match_item(&mut self, matching: TokenStream, result: TokenStream) {
151        self.extend([
152            matching,
153            TokenStream::from_iter([
154                TokenTree::Punct(Punct::new('=', Spacing::Joint)),
155                TokenTree::Punct(Punct::new('>', Spacing::Alone)),
156            ]),
157            TokenStream::create_wrapped_braces(result),
158            TokenStream::create_punct(','),
159        ])
160    }
161    /// Appends a `-> Result< {ok} , {err} >` stream
162    fn return_result(&mut self, ok: TokenStream, err: TokenStream) {
163        self.add_single_arrow();
164        self.add_ident("Result");
165        self.add_punc('<');
166        self.extend(ok);
167        self.add_punc(',');
168        self.extend(err);
169        self.add_punc('>');
170    }
171
172    /// Appends a `()` empty type.
173    fn create_empty_type() -> TokenStream {
174        TokenStream::from_iter([TokenTree::Group(Group::new(
175            Delimiter::Parenthesis,
176            TokenStream::new(),
177        ))])
178    }
179
180    /// Wraps the provided stream in braces/staches: `{ {inner} }`
181    fn wrap_braces(&mut self, inner: TokenStream) {
182        self.extend([TokenTree::Group(Group::new(Delimiter::Brace, inner))])
183    }
184    /// Wraps the provided stream in brackets: `[ {inner} ]`
185    fn wrap_brackets(&mut self, inner: TokenStream) {
186        self.extend([TokenTree::Group(Group::new(Delimiter::Bracket, inner))])
187    }
188
189    /// Appends the provided [`Literal`]
190    fn add_literal(&mut self, literal: Literal) {
191        self.extend([TokenTree::Literal(literal)])
192    }
193
194    /// Appends the provided [`Ident`]
195    fn add_ident_type(&mut self, ident: Ident) {
196        self.extend([TokenTree::Ident(ident)])
197    }
198    /// Appends the stream `#[must_use]`
199    fn add_must_use(&mut self) {
200        self.extend([
201            TokenTree::Punct(Punct::new('#', Spacing::Alone)),
202            TokenTree::Group(Group::new(
203                Delimiter::Bracket,
204                TokenStream::create_ident("must_use"),
205            )),
206        ])
207    }
208    fn add_getter(&mut self, name: &str, output_type: TokenStream) {
209        self.add_ident("pub");
210        self.add_ident("fn");
211        self.add_ident(name);
212        self.add_parens(TokenStream::create_ref_ident("self"));
213        self.add_single_arrow();
214        self.extend([output_type])
215    }
216}
217
218impl DeriveMethods for TokenStream {}