Skip to main content

newtype_tools_derive/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(test), no_std)]
3extern crate alloc;
4use alloc::vec::Vec;
5use proc_macro::TokenStream;
6
7mod expand;
8mod parse;
9
10/// Parses and expands a `newtype` attribute kind into a token stream.
11///
12/// The crate supports predefined sets of newtype properties. The concept is similar
13/// to the `phantom_newtype` crate but avoids its limitations, as the newtype
14/// generated here is a distinct Rust type. This allows new traits
15/// to be implemented easily for the type and makes the set of derived traits
16/// simple to extend.
17///
18/// ```ignore
19/// #[newtype(Amount)]
20/// #[derive(serde::Serialize)]
21/// struct Apples(u64);
22/// ```
23#[proc_macro_attribute]
24pub fn newtype(attr: TokenStream, item: TokenStream) -> TokenStream {
25    let kind = match parse::parse_newtype_kind(attr.into()) {
26        Ok(kind) => kind,
27        Err(err) => return err.to_compile_error().into(),
28    };
29    let input = item.clone();
30    let newtype = match parse::parse_newtype(input) {
31        Ok(newtype) => newtype,
32        Err(err) => return err.to_compile_error().into(),
33    };
34    expand::expand_newtype(newtype, kind, item)
35}
36
37/// Parses and expands a `Newtype` derive into a token stream.
38///
39/// ```ignore
40/// #[derive(Newtype)]
41/// #[newtype(from(Oranges, with =  "|oranges| Apples(oranges.0 as u64 * 2)"))]
42/// struct Apples(u64);
43/// ```
44#[proc_macro_derive(Newtype, attributes(newtype))]
45pub fn newtype_derive(input: TokenStream) -> TokenStream {
46    let newtype_derives = match parse::parse_newtype_derives(input.into()) {
47        Ok(newtype_derives) => newtype_derives,
48        Err(err) => return err.to_compile_error().into(),
49    };
50    expand::expand_newtype_derives(newtype_derives)
51}
52
53/// Structured representation of a `newtype`.
54///
55/// ```ignore
56/// #[newtype(Amount)]
57/// struct Apples(u64);
58/// ```
59struct Newtype {
60    /// Top-level newtype identifier.
61    newtype: syn::Ident,
62    /// Inner type field type.
63    inner_ty: syn::Type,
64    /// Newtype generics.
65    generics: syn::Generics,
66}
67
68impl Newtype {
69    /// Creates a new `Newtype` instance.
70    fn new(newtype: syn::Ident, inner_ty: syn::Type, generics: syn::Generics) -> Self {
71        Self {
72            newtype,
73            inner_ty,
74            generics,
75        }
76    }
77}
78
79/// Structured representation of all `Newtype` derives.
80///
81/// ```ignore
82/// #[derive(Newtype)]
83/// #[newtype(from(Oranges, with =  "|oranges| Apples(oranges.0 as u64 * 2)"))]
84/// struct Apples(u64);
85/// ```
86#[derive(Default)]
87struct NewtypeDerives {
88    /// Tuples of `(from type, conversion expression)`.
89    from: Vec<(syn::Type, syn::Expr)>,
90    /// Tuples of `(from type, error type, conversion expression)`.
91    try_from: Vec<(syn::Type, syn::Type, syn::Expr)>,
92    /// Tuples of `(into type, conversion expression)`.
93    into: Vec<(syn::Type, syn::Expr)>,
94    /// Tuples of `(into type, error type, conversion expression)`.
95    try_into: Vec<(syn::Type, syn::Type, syn::Expr)>,
96    /// Tuples of `(rhs type, output type, add expression)`.
97    add: Vec<(syn::Type, syn::Type, syn::Expr)>,
98    /// Tuples of `(rhs type, add-assign expression)`.
99    add_assign: Vec<(syn::Type, syn::Expr)>,
100    /// Tuples of `(rhs type, output type, add expression)`.
101    bitand: Vec<(syn::Type, syn::Type, syn::Expr)>,
102    /// Tuples of `(rhs type, add-assign expression)`.
103    bitand_assign: Vec<(syn::Type, syn::Expr)>,
104    /// Tuples of `(rhs type, output type, add expression)`.
105    bitor: Vec<(syn::Type, syn::Type, syn::Expr)>,
106    /// Tuples of `(rhs type, add-assign expression)`.
107    bitor_assign: Vec<(syn::Type, syn::Expr)>,
108    /// Tuples of `(rhs type, output type, add expression)`.
109    bitxor: Vec<(syn::Type, syn::Type, syn::Expr)>,
110    /// Tuples of `(rhs type, add-assign expression)`.
111    bitxor_assign: Vec<(syn::Type, syn::Expr)>,
112    /// Tuples of `(rhs type, output type, add expression)`.
113    div: Vec<(syn::Type, syn::Type, syn::Expr)>,
114    /// Tuples of `(rhs type, add-assign expression)`.
115    div_assign: Vec<(syn::Type, syn::Expr)>,
116    /// Tuples of `(rhs type, output type, add expression)`.
117    mul: Vec<(syn::Type, syn::Type, syn::Expr)>,
118    /// Tuples of `(rhs type, add-assign expression)`.
119    mul_assign: Vec<(syn::Type, syn::Expr)>,
120    /// Tuples of `(rhs type, output type, add expression)`.
121    rem: Vec<(syn::Type, syn::Type, syn::Expr)>,
122    /// Tuples of `(rhs type, add-assign expression)`.
123    rem_assign: Vec<(syn::Type, syn::Expr)>,
124    /// Tuples of `(rhs type, output type, add expression)`.
125    shl: Vec<(syn::Type, syn::Type, syn::Expr)>,
126    /// Tuples of `(rhs type, add-assign expression)`.
127    shl_assign: Vec<(syn::Type, syn::Expr)>,
128    /// Tuples of `(rhs type, output type, add expression)`.
129    shr: Vec<(syn::Type, syn::Type, syn::Expr)>,
130    /// Tuples of `(rhs type, add-assign expression)`.
131    shr_assign: Vec<(syn::Type, syn::Expr)>,
132    /// Tuples of `(other type, comparison expression)`.
133    partial_eq: Vec<(syn::Type, syn::Expr)>,
134    /// Tuples of `(rhs type, output type, sub expression)`.
135    sub: Vec<(syn::Type, syn::Type, syn::Expr)>,
136    /// Tuples of `(rhs type, sub-assign expression)`.
137    sub_assign: Vec<(syn::Type, syn::Expr)>,
138}
139
140/// Defines `newtype` attribute kind.
141///
142/// ```ignore
143/// #[newtype(Amount)]
144/// struct Apples(u64);
145/// ```
146#[derive(Debug, PartialEq)]
147enum NewtypeKind {
148    Amount,
149    Id,
150}
151
152impl core::fmt::Display for NewtypeKind {
153    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154        match self {
155            Self::Amount => f.write_str("Amount"),
156            Self::Id => f.write_str("Id"),
157        }
158    }
159}
160
161impl TryFrom<&syn::Ident> for NewtypeKind {
162    type Error = syn::Error;
163
164    fn try_from(value: &syn::Ident) -> Result<Self, Self::Error> {
165        match value {
166            ident if ident == "Amount" => Ok(Self::Amount),
167            ident if ident == "Id" => Ok(Self::Id),
168            _ => Err(syn::Error::new_spanned(value, "expected 'Amount' or 'Id'")),
169        }
170    }
171}
172
173/// Defines `Newtype` derive attribute type.
174///
175/// ```ignore
176/// #[derive(Newtype)]
177/// #[newtype(from(Oranges, with =  "|oranges| Apples(oranges.0 as u64 * 2)"))]
178/// struct Apples(u64);
179/// ```
180#[derive(Debug, PartialEq)]
181enum DeriveType {
182    From,
183    TryFrom,
184    Into,
185    TryInto,
186    Add,
187    AddAssign,
188    BitAnd,
189    BitAndAssign,
190    BitOr,
191    BitOrAssign,
192    BitXor,
193    BitXorAssign,
194    Div,
195    DivAssign,
196    Mul,
197    MulAssign,
198    Rem,
199    RemAssign,
200    Shl,
201    ShlAssign,
202    Shr,
203    ShrAssign,
204    PartialEq,
205    Sub,
206    SubAssign,
207}
208
209impl core::fmt::Display for DeriveType {
210    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211        match self {
212            Self::From => f.write_str("from"),
213            Self::TryFrom => f.write_str("try_from"),
214            Self::Into => f.write_str("into"),
215            Self::TryInto => f.write_str("try_into"),
216            Self::Add => f.write_str("add"),
217            Self::AddAssign => f.write_str("add_assign"),
218            Self::BitAnd => f.write_str("bitand"),
219            Self::BitAndAssign => f.write_str("bitand_assign"),
220            Self::BitOr => f.write_str("bitor"),
221            Self::BitOrAssign => f.write_str("bitor_assign"),
222            Self::BitXor => f.write_str("bitxor"),
223            Self::BitXorAssign => f.write_str("bitxor_assign"),
224            Self::Div => f.write_str("div"),
225            Self::DivAssign => f.write_str("div_assign"),
226            Self::Mul => f.write_str("mul"),
227            Self::MulAssign => f.write_str("mul_assign"),
228            Self::Rem => f.write_str("rem"),
229            Self::RemAssign => f.write_str("rem_assign"),
230            Self::Shl => f.write_str("shl"),
231            Self::ShlAssign => f.write_str("shl_assign"),
232            Self::Shr => f.write_str("shr"),
233            Self::ShrAssign => f.write_str("shr_assign"),
234            Self::PartialEq => f.write_str("partial_eq"),
235            Self::Sub => f.write_str("sub"),
236            Self::SubAssign => f.write_str("sub_assign"),
237        }
238    }
239}
240
241impl TryFrom<Option<&syn::Ident>> for DeriveType {
242    type Error = syn::Error;
243
244    fn try_from(value: Option<&syn::Ident>) -> Result<Self, Self::Error> {
245        match value {
246            Some(ident) if ident == "from" => Ok(Self::From),
247            Some(ident) if ident == "try_from" => Ok(Self::TryFrom),
248            Some(ident) if ident == "into" => Ok(Self::Into),
249            Some(ident) if ident == "try_into" => Ok(Self::TryInto),
250            Some(ident) if ident == "add" => Ok(Self::Add),
251            Some(ident) if ident == "add_assign" => Ok(Self::AddAssign),
252            Some(ident) if ident == "bitand" => Ok(Self::BitAnd),
253            Some(ident) if ident == "bitand_assign" => Ok(Self::BitAndAssign),
254            Some(ident) if ident == "bitor" => Ok(Self::BitOr),
255            Some(ident) if ident == "bitor_assign" => Ok(Self::BitOrAssign),
256            Some(ident) if ident == "bitxor" => Ok(Self::BitXor),
257            Some(ident) if ident == "bitxor_assign" => Ok(Self::BitXorAssign),
258            Some(ident) if ident == "div" => Ok(Self::Div),
259            Some(ident) if ident == "div_assign" => Ok(Self::DivAssign),
260            Some(ident) if ident == "mul" => Ok(Self::Mul),
261            Some(ident) if ident == "mul_assign" => Ok(Self::MulAssign),
262            Some(ident) if ident == "rem" => Ok(Self::Rem),
263            Some(ident) if ident == "rem_assign" => Ok(Self::RemAssign),
264            Some(ident) if ident == "shl" => Ok(Self::Shl),
265            Some(ident) if ident == "shl_assign" => Ok(Self::ShlAssign),
266            Some(ident) if ident == "shr" => Ok(Self::Shr),
267            Some(ident) if ident == "shr_assign" => Ok(Self::ShrAssign),
268            Some(ident) if ident == "partial_eq" => Ok(Self::PartialEq),
269            Some(ident) if ident == "sub" => Ok(Self::Sub),
270            Some(ident) if ident == "sub_assign" => Ok(Self::SubAssign),
271            _ => Err(syn::Error::new_spanned(
272                value,
273                "expected `(try_)from`, `(try_)into`, `add(_assign)`, `bitand(_assign)`, \
274                `bitor(_assign)`, `bitxor(_assign)`, `div(_assign)`, `mul(_assign)`, \
275                `rem(_assign)`, `shl(_assign)`, `shr(_assign)`, `partial_eq`, or `sub(_assign)`",
276            )),
277        }
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    #[test]
284    fn newtype_kind_display_roundtrip() {
285        use super::NewtypeKind;
286        assert_eq!(format!("{}", NewtypeKind::Amount), "Amount");
287        assert_eq!(format!("{}", NewtypeKind::Id), "Id");
288    }
289
290    #[test]
291    fn derive_type_display_roundtrip() {
292        use super::DeriveType;
293        assert_eq!(format!("{}", DeriveType::From), "from");
294        assert_eq!(format!("{}", DeriveType::TryFrom), "try_from");
295        assert_eq!(format!("{}", DeriveType::Into), "into");
296        assert_eq!(format!("{}", DeriveType::TryInto), "try_into");
297        assert_eq!(format!("{}", DeriveType::Add), "add");
298        assert_eq!(format!("{}", DeriveType::AddAssign), "add_assign");
299        assert_eq!(format!("{}", DeriveType::BitAnd), "bitand");
300        assert_eq!(format!("{}", DeriveType::BitAndAssign), "bitand_assign");
301        assert_eq!(format!("{}", DeriveType::BitOr), "bitor");
302        assert_eq!(format!("{}", DeriveType::BitOrAssign), "bitor_assign");
303        assert_eq!(format!("{}", DeriveType::BitXor), "bitxor");
304        assert_eq!(format!("{}", DeriveType::BitXorAssign), "bitxor_assign");
305        assert_eq!(format!("{}", DeriveType::Div), "div");
306        assert_eq!(format!("{}", DeriveType::DivAssign), "div_assign");
307        assert_eq!(format!("{}", DeriveType::Mul), "mul");
308        assert_eq!(format!("{}", DeriveType::MulAssign), "mul_assign");
309        assert_eq!(format!("{}", DeriveType::Rem), "rem");
310        assert_eq!(format!("{}", DeriveType::RemAssign), "rem_assign");
311        assert_eq!(format!("{}", DeriveType::Shl), "shl");
312        assert_eq!(format!("{}", DeriveType::ShlAssign), "shl_assign");
313        assert_eq!(format!("{}", DeriveType::Shr), "shr");
314        assert_eq!(format!("{}", DeriveType::ShrAssign), "shr_assign");
315        assert_eq!(format!("{}", DeriveType::PartialEq), "partial_eq");
316        assert_eq!(format!("{}", DeriveType::Sub), "sub");
317        assert_eq!(format!("{}", DeriveType::SubAssign), "sub_assign");
318    }
319}