use syn::{Data, DataStruct, DeriveInput, Field, Fields};
const NEWTYPE_MUST_HAVE_ONLY_ONE_FIELD: &str =
"Newtype struct must only have one field.\n\
See https://doc.rust-lang.org/book/ch19-04-advanced-types.html#advanced-types \
for more information.";
const MACRO_MUST_BE_USED_ON_NEWTYPE_STRUCT: &str =
"This macro must be used on a newtype struct.\n\
See https://doc.rust-lang.org/book/ch19-04-advanced-types.html#advanced-types \
for more information.";
pub trait DeriveInputNewtypeExt {
fn inner_type(&self) -> &Field;
fn inner_type_mut(&mut self) -> &mut Field;
fn is_newtype(&self) -> bool;
}
impl DeriveInputNewtypeExt for DeriveInput {
fn inner_type(&self) -> &Field {
if let Data::Struct(DataStruct {
fields: Fields::Unnamed(fields_unnamed),
..
}) = &self.data
{
if fields_unnamed.unnamed.len() == 1 {
fields_unnamed
.unnamed
.first()
.expect("Expected field to exist.")
} else {
panic!(NEWTYPE_MUST_HAVE_ONLY_ONE_FIELD)
}
} else {
panic!(MACRO_MUST_BE_USED_ON_NEWTYPE_STRUCT)
}
}
fn inner_type_mut(&mut self) -> &mut Field {
if let Data::Struct(DataStruct {
fields: Fields::Unnamed(fields_unnamed),
..
}) = &mut self.data
{
if fields_unnamed.unnamed.len() == 1 {
fields_unnamed
.unnamed
.iter_mut()
.next()
.expect("Expected field to exist.")
} else {
panic!(NEWTYPE_MUST_HAVE_ONLY_ONE_FIELD)
}
} else {
panic!(MACRO_MUST_BE_USED_ON_NEWTYPE_STRUCT)
}
}
fn is_newtype(&self) -> bool {
if let Data::Struct(DataStruct {
fields: Fields::Unnamed(fields_unnamed),
..
}) = &self.data
{
fields_unnamed.unnamed.len() == 1
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use syn::{parse_quote, DeriveInput, Type};
use super::DeriveInputNewtypeExt;
#[test]
fn inner_type_returns_field() {
let ast: DeriveInput = parse_quote! {
struct Newtype(u32);
};
let inner_field = ast.inner_type();
let expected_type: Type = Type::Path(parse_quote!(u32));
assert_eq!(expected_type, inner_field.ty);
}
#[test]
#[should_panic(expected = "This macro must be used on a newtype struct.\n\
See https://doc.rust-lang.org/book/ch19-04-advanced-types.html#advanced-types \
for more information.")]
fn inner_type_panics_when_struct_fields_not_unnamed() {
let ast: DeriveInput = parse_quote! {
struct Unit;
};
ast.inner_type();
}
#[test]
#[should_panic(expected = "Newtype struct must only have one field.\n\
See https://doc.rust-lang.org/book/ch19-04-advanced-types.html#advanced-types \
for more information.")]
fn inner_type_panics_when_struct_has_multiple_fields() {
let ast: DeriveInput = parse_quote! {
struct Newtype(u32, u32);
};
ast.inner_type();
}
#[test]
fn inner_type_mut_returns_field() {
let mut ast: DeriveInput = parse_quote! {
struct Newtype(u32);
};
let inner_field = ast.inner_type_mut();
let expected_type: Type = Type::Path(parse_quote!(u32));
assert_eq!(expected_type, inner_field.ty);
}
#[test]
#[should_panic(expected = "This macro must be used on a newtype struct.\n\
See https://doc.rust-lang.org/book/ch19-04-advanced-types.html#advanced-types \
for more information.")]
fn inner_type_mut_panics_when_struct_fields_not_unnamed() {
let mut ast: DeriveInput = parse_quote! {
struct Unit;
};
ast.inner_type_mut();
}
#[test]
#[should_panic(expected = "Newtype struct must only have one field.\n\
See https://doc.rust-lang.org/book/ch19-04-advanced-types.html#advanced-types \
for more information.")]
fn inner_type_mut_panics_when_struct_has_multiple_fields() {
let mut ast: DeriveInput = parse_quote! {
struct Newtype(u32, u32);
};
ast.inner_type_mut();
}
#[test]
fn is_newtype_returns_true_when_fields_unnamed_and_exactly_one() {
let ast: DeriveInput = parse_quote! {
struct Tuple(u32);
};
assert!(ast.is_newtype());
}
#[test]
fn is_newtype_returns_false_when_fields_unnamed_and_zero() {
let ast: DeriveInput = parse_quote! {
struct Tuple();
};
assert!(!ast.is_newtype());
}
#[test]
fn is_newtype_returns_false_when_fields_unnamed_and_more_than_one() {
let ast: DeriveInput = parse_quote! {
struct Tuple(u32, u32);
};
assert!(!ast.is_newtype());
}
#[test]
fn is_newtype_returns_false_when_fields_not_tuple() {
let ast: DeriveInput = parse_quote! {
struct Unit;
};
assert!(!ast.is_newtype());
}
}