#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]
extern crate alloc;
use alloc::vec::Vec;
use proc_macro::TokenStream;
mod expand;
mod parse;
#[proc_macro_attribute]
pub fn newtype(attr: TokenStream, item: TokenStream) -> TokenStream {
let kind = match parse::parse_newtype_kind(attr.into()) {
Ok(kind) => kind,
Err(err) => return err.to_compile_error().into(),
};
let input = item.clone();
let newtype = match parse::parse_newtype(input) {
Ok(newtype) => newtype,
Err(err) => return err.to_compile_error().into(),
};
expand::expand_newtype(newtype, kind, item)
}
#[proc_macro_derive(Newtype, attributes(newtype))]
pub fn newtype_derive(input: TokenStream) -> TokenStream {
let newtype_derives = match parse::parse_newtype_derives(input.into()) {
Ok(newtype_derives) => newtype_derives,
Err(err) => return err.to_compile_error().into(),
};
expand::expand_newtype_derives(newtype_derives)
}
struct Newtype {
newtype: syn::Ident,
inner_ty: syn::Type,
generics: syn::Generics,
}
impl Newtype {
fn new(newtype: syn::Ident, inner_ty: syn::Type, generics: syn::Generics) -> Self {
Self {
newtype,
inner_ty,
generics,
}
}
}
#[derive(Default)]
struct NewtypeDerives {
from: Vec<(syn::Type, syn::Expr)>,
try_from: Vec<(syn::Type, syn::Type, syn::Expr)>,
into: Vec<(syn::Type, syn::Expr)>,
try_into: Vec<(syn::Type, syn::Type, syn::Expr)>,
add: Vec<(syn::Type, syn::Type, syn::Expr)>,
add_assign: Vec<(syn::Type, syn::Expr)>,
bitand: Vec<(syn::Type, syn::Type, syn::Expr)>,
bitand_assign: Vec<(syn::Type, syn::Expr)>,
bitor: Vec<(syn::Type, syn::Type, syn::Expr)>,
bitor_assign: Vec<(syn::Type, syn::Expr)>,
bitxor: Vec<(syn::Type, syn::Type, syn::Expr)>,
bitxor_assign: Vec<(syn::Type, syn::Expr)>,
div: Vec<(syn::Type, syn::Type, syn::Expr)>,
div_assign: Vec<(syn::Type, syn::Expr)>,
mul: Vec<(syn::Type, syn::Type, syn::Expr)>,
mul_assign: Vec<(syn::Type, syn::Expr)>,
rem: Vec<(syn::Type, syn::Type, syn::Expr)>,
rem_assign: Vec<(syn::Type, syn::Expr)>,
shl: Vec<(syn::Type, syn::Type, syn::Expr)>,
shl_assign: Vec<(syn::Type, syn::Expr)>,
shr: Vec<(syn::Type, syn::Type, syn::Expr)>,
shr_assign: Vec<(syn::Type, syn::Expr)>,
partial_eq: Vec<(syn::Type, syn::Expr)>,
sub: Vec<(syn::Type, syn::Type, syn::Expr)>,
sub_assign: Vec<(syn::Type, syn::Expr)>,
}
#[derive(Debug, PartialEq)]
enum NewtypeKind {
Amount,
Id,
}
impl core::fmt::Display for NewtypeKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Amount => f.write_str("Amount"),
Self::Id => f.write_str("Id"),
}
}
}
impl TryFrom<&syn::Ident> for NewtypeKind {
type Error = syn::Error;
fn try_from(value: &syn::Ident) -> Result<Self, Self::Error> {
match value {
ident if ident == "Amount" => Ok(Self::Amount),
ident if ident == "Id" => Ok(Self::Id),
_ => Err(syn::Error::new_spanned(value, "expected 'Amount' or 'Id'")),
}
}
}
#[derive(Debug, PartialEq)]
enum DeriveType {
From,
TryFrom,
Into,
TryInto,
Add,
AddAssign,
BitAnd,
BitAndAssign,
BitOr,
BitOrAssign,
BitXor,
BitXorAssign,
Div,
DivAssign,
Mul,
MulAssign,
Rem,
RemAssign,
Shl,
ShlAssign,
Shr,
ShrAssign,
PartialEq,
Sub,
SubAssign,
}
impl core::fmt::Display for DeriveType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::From => f.write_str("from"),
Self::TryFrom => f.write_str("try_from"),
Self::Into => f.write_str("into"),
Self::TryInto => f.write_str("try_into"),
Self::Add => f.write_str("add"),
Self::AddAssign => f.write_str("add_assign"),
Self::BitAnd => f.write_str("bitand"),
Self::BitAndAssign => f.write_str("bitand_assign"),
Self::BitOr => f.write_str("bitor"),
Self::BitOrAssign => f.write_str("bitor_assign"),
Self::BitXor => f.write_str("bitxor"),
Self::BitXorAssign => f.write_str("bitxor_assign"),
Self::Div => f.write_str("div"),
Self::DivAssign => f.write_str("div_assign"),
Self::Mul => f.write_str("mul"),
Self::MulAssign => f.write_str("mul_assign"),
Self::Rem => f.write_str("rem"),
Self::RemAssign => f.write_str("rem_assign"),
Self::Shl => f.write_str("shl"),
Self::ShlAssign => f.write_str("shl_assign"),
Self::Shr => f.write_str("shr"),
Self::ShrAssign => f.write_str("shr_assign"),
Self::PartialEq => f.write_str("partial_eq"),
Self::Sub => f.write_str("sub"),
Self::SubAssign => f.write_str("sub_assign"),
}
}
}
impl TryFrom<Option<&syn::Ident>> for DeriveType {
type Error = syn::Error;
fn try_from(value: Option<&syn::Ident>) -> Result<Self, Self::Error> {
match value {
Some(ident) if ident == "from" => Ok(Self::From),
Some(ident) if ident == "try_from" => Ok(Self::TryFrom),
Some(ident) if ident == "into" => Ok(Self::Into),
Some(ident) if ident == "try_into" => Ok(Self::TryInto),
Some(ident) if ident == "add" => Ok(Self::Add),
Some(ident) if ident == "add_assign" => Ok(Self::AddAssign),
Some(ident) if ident == "bitand" => Ok(Self::BitAnd),
Some(ident) if ident == "bitand_assign" => Ok(Self::BitAndAssign),
Some(ident) if ident == "bitor" => Ok(Self::BitOr),
Some(ident) if ident == "bitor_assign" => Ok(Self::BitOrAssign),
Some(ident) if ident == "bitxor" => Ok(Self::BitXor),
Some(ident) if ident == "bitxor_assign" => Ok(Self::BitXorAssign),
Some(ident) if ident == "div" => Ok(Self::Div),
Some(ident) if ident == "div_assign" => Ok(Self::DivAssign),
Some(ident) if ident == "mul" => Ok(Self::Mul),
Some(ident) if ident == "mul_assign" => Ok(Self::MulAssign),
Some(ident) if ident == "rem" => Ok(Self::Rem),
Some(ident) if ident == "rem_assign" => Ok(Self::RemAssign),
Some(ident) if ident == "shl" => Ok(Self::Shl),
Some(ident) if ident == "shl_assign" => Ok(Self::ShlAssign),
Some(ident) if ident == "shr" => Ok(Self::Shr),
Some(ident) if ident == "shr_assign" => Ok(Self::ShrAssign),
Some(ident) if ident == "partial_eq" => Ok(Self::PartialEq),
Some(ident) if ident == "sub" => Ok(Self::Sub),
Some(ident) if ident == "sub_assign" => Ok(Self::SubAssign),
_ => Err(syn::Error::new_spanned(
value,
"expected `(try_)from`, `(try_)into`, `add(_assign)`, `bitand(_assign)`, \
`bitor(_assign)`, `bitxor(_assign)`, `div(_assign)`, `mul(_assign)`, \
`rem(_assign)`, `shl(_assign)`, `shr(_assign)`, `partial_eq`, or `sub(_assign)`",
)),
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn newtype_kind_display_roundtrip() {
use super::NewtypeKind;
assert_eq!(format!("{}", NewtypeKind::Amount), "Amount");
assert_eq!(format!("{}", NewtypeKind::Id), "Id");
}
#[test]
fn derive_type_display_roundtrip() {
use super::DeriveType;
assert_eq!(format!("{}", DeriveType::From), "from");
assert_eq!(format!("{}", DeriveType::TryFrom), "try_from");
assert_eq!(format!("{}", DeriveType::Into), "into");
assert_eq!(format!("{}", DeriveType::TryInto), "try_into");
assert_eq!(format!("{}", DeriveType::Add), "add");
assert_eq!(format!("{}", DeriveType::AddAssign), "add_assign");
assert_eq!(format!("{}", DeriveType::BitAnd), "bitand");
assert_eq!(format!("{}", DeriveType::BitAndAssign), "bitand_assign");
assert_eq!(format!("{}", DeriveType::BitOr), "bitor");
assert_eq!(format!("{}", DeriveType::BitOrAssign), "bitor_assign");
assert_eq!(format!("{}", DeriveType::BitXor), "bitxor");
assert_eq!(format!("{}", DeriveType::BitXorAssign), "bitxor_assign");
assert_eq!(format!("{}", DeriveType::Div), "div");
assert_eq!(format!("{}", DeriveType::DivAssign), "div_assign");
assert_eq!(format!("{}", DeriveType::Mul), "mul");
assert_eq!(format!("{}", DeriveType::MulAssign), "mul_assign");
assert_eq!(format!("{}", DeriveType::Rem), "rem");
assert_eq!(format!("{}", DeriveType::RemAssign), "rem_assign");
assert_eq!(format!("{}", DeriveType::Shl), "shl");
assert_eq!(format!("{}", DeriveType::ShlAssign), "shl_assign");
assert_eq!(format!("{}", DeriveType::Shr), "shr");
assert_eq!(format!("{}", DeriveType::ShrAssign), "shr_assign");
assert_eq!(format!("{}", DeriveType::PartialEq), "partial_eq");
assert_eq!(format!("{}", DeriveType::Sub), "sub");
assert_eq!(format!("{}", DeriveType::SubAssign), "sub_assign");
}
}