Skip to content

Commit

Permalink
Create a TryIntoError type
Browse files Browse the repository at this point in the history
Instead of returning a string as the error, this returns a dedicated
error type. This has the big benefit that in case of an error it's
possible to get back the original value by using `error.input`.
Previously the original value would be dropped in case of an error.

Fixes #173
Fixes #130
  • Loading branch information
JelteF committed Nov 14, 2022
1 parent 77e0cc8 commit f5712b6
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 90 deletions.
6 changes: 2 additions & 4 deletions impl/src/try_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
})
.collect::<Vec<_>>()
.join(", ");
let message =
format!("Only {} can be converted to {}", variant_names, output_type);

let generics_impl;
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
Expand All @@ -108,13 +106,13 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
(#(#reference_with_lifetime #original_types),*)
#where_clause
{
type Error = &'static str;
type Error = ::derive_more::TryIntoError<#reference_with_lifetime #input_type>;

#[inline]
fn try_from(value: #reference_with_lifetime #input_type #ty_generics) -> ::core::result::Result<Self, Self::Error> {
match value {
#(#matchers)|* => ::core::result::Result::Ok(#vars),
_ => ::core::result::Result::Err(#message),
_ => ::core::result::Result::Err(::derive_more::TryIntoError::new(value, #variant_names, #output_type)),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@

#[doc(inline)]
pub use derive_more_impl::*;

mod errors;
pub use crate::errors::TryIntoError;
176 changes: 90 additions & 86 deletions tests/try_into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use derive_more::TryInto;
// has been redefined.
type Result = ();

#[derive(Clone, Copy, TryInto)]
#[derive(Clone, Copy, Eq, PartialEq, Debug, TryInto)]
#[try_into(owned, ref, ref_mut)]
enum MixedInts {
SmallInt(i32),
Expand Down Expand Up @@ -36,161 +36,165 @@ enum MixedInts {
#[test]
fn test_try_into() {
let mut i = MixedInts::SmallInt(42);
assert_eq!(Ok(42i32), i.try_into());
assert_eq!(Ok(&42i32), (&i).try_into());
assert_eq!(Ok(&mut 42i32), (&mut i).try_into());
assert_eq!(42i32, i.try_into().unwrap());
assert_eq!(&42i32, <_ as TryInto<&i32>>::try_into(&i).unwrap());
assert_eq!(&mut 42i32, <_ as TryInto<&mut i32>>::try_into(&mut i).unwrap());
assert_eq!(
i64::try_from(i),
Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
i64::try_from(i).unwrap_err().to_string(),
"Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64"
);
assert_eq!(
<(i32, i32)>::try_from(i),
Err("Only TwoSmallInts can be converted to (i32, i32)")
i64::try_from(i).unwrap_err().input,
MixedInts::SmallInt(42)
);
assert_eq!(
<(i64, i64)>::try_from(i),
Err("Only NamedBigInts can be converted to (i64, i64)")
<(i32, i32)>::try_from(i).unwrap_err().to_string(),
"Only TwoSmallInts can be converted to (i32, i32)"
);
assert_eq!(
u32::try_from(i),
Err("Only Unsigned, NamedUnsigned can be converted to u32")
<(i64, i64)>::try_from(i).unwrap_err().to_string(),
"Only NamedBigInts can be converted to (i64, i64)"
);
assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
assert_eq!(
u32::try_from(i).unwrap_err().to_string(),
"Only Unsigned, NamedUnsigned can be converted to u32"
);
assert_eq!(<()>::try_from(i).unwrap_err().to_string(), "Only Unit can be converted to ()");

let mut i = MixedInts::NamedBigInt { int: 42 };
assert_eq!(
i32::try_from(i),
Err("Only SmallInt can be converted to i32")
i32::try_from(i).unwrap_err().to_string(),
"Only SmallInt can be converted to i32"
);
assert_eq!(Ok(42i64), i.try_into());
assert_eq!(Ok(&42i64), (&i).try_into());
assert_eq!(Ok(&mut 42i64), (&mut i).try_into());
assert_eq!(42i64, i.try_into().unwrap());
assert_eq!(&42i64, <_ as TryInto<&i64>>::try_into(&i).unwrap());
assert_eq!(&mut 42i64, <_ as TryInto<&mut i64>>::try_into(&mut i).unwrap());
assert_eq!(
<(i32, i32)>::try_from(i),
Err("Only TwoSmallInts can be converted to (i32, i32)")
<(i32, i32)>::try_from(i).unwrap_err().to_string(),
"Only TwoSmallInts can be converted to (i32, i32)"
);
assert_eq!(
<(i64, i64)>::try_from(i),
Err("Only NamedBigInts can be converted to (i64, i64)")
<(i64, i64)>::try_from(i).unwrap_err().to_string(),
"Only NamedBigInts can be converted to (i64, i64)"
);
assert_eq!(
u32::try_from(i),
Err("Only Unsigned, NamedUnsigned can be converted to u32")
u32::try_from(i).unwrap_err().to_string(),
"Only Unsigned, NamedUnsigned can be converted to u32"
);
assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
assert_eq!(<()>::try_from(i).unwrap_err().to_string(), "Only Unit can be converted to ()");

let mut i = MixedInts::TwoSmallInts(42, 64);
assert_eq!(
i32::try_from(i),
Err("Only SmallInt can be converted to i32")
i32::try_from(i).unwrap_err().to_string(),
"Only SmallInt can be converted to i32"
);
assert_eq!(
i64::try_from(i),
Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
i64::try_from(i).unwrap_err().to_string(),
"Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64"
);
assert_eq!(Ok((42i32, 64i32)), i.try_into());
assert_eq!(Ok((&42i32, &64i32)), (&i).try_into());
assert_eq!(Ok((&mut 42i32, &mut 64i32)), (&mut i).try_into());
assert_eq!((42i32, 64i32), i.try_into().unwrap());
assert_eq!((&42i32, &64i32), (&i).try_into().unwrap());
assert_eq!((&mut 42i32, &mut 64i32), (&mut i).try_into().unwrap());
assert_eq!(
<(i64, i64)>::try_from(i),
Err("Only NamedBigInts can be converted to (i64, i64)")
<(i64, i64)>::try_from(i).unwrap_err().to_string(),
"Only NamedBigInts can be converted to (i64, i64)"
);
assert_eq!(
u32::try_from(i),
Err("Only Unsigned, NamedUnsigned can be converted to u32")
u32::try_from(i).unwrap_err().to_string(),
"Only Unsigned, NamedUnsigned can be converted to u32"
);
assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
assert_eq!(<()>::try_from(i).unwrap_err().to_string(), "Only Unit can be converted to ()");

let mut i = MixedInts::NamedBigInts { x: 42, y: 64 };
assert_eq!(
i32::try_from(i),
Err("Only SmallInt can be converted to i32")
i32::try_from(i).unwrap_err().to_string(),
"Only SmallInt can be converted to i32"
);
assert_eq!(
i64::try_from(i),
Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
i64::try_from(i).unwrap_err().to_string(),
"Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64"
);
assert_eq!(
<(i32, i32)>::try_from(i),
Err("Only TwoSmallInts can be converted to (i32, i32)")
<(i32, i32)>::try_from(i).unwrap_err().to_string(),
"Only TwoSmallInts can be converted to (i32, i32)"
);
assert_eq!(Ok((42i64, 64i64)), i.try_into());
assert_eq!(Ok((&42i64, &64i64)), (&i).try_into());
assert_eq!(Ok((&mut 42i64, &mut 64i64)), (&mut i).try_into());
assert_eq!((42i64, 64i64), i.try_into().unwrap());
assert_eq!((&42i64, &64i64), (&i).try_into().unwrap());
assert_eq!((&mut 42i64, &mut 64i64), (&mut i).try_into().unwrap());
assert_eq!(
u32::try_from(i),
Err("Only Unsigned, NamedUnsigned can be converted to u32")
u32::try_from(i).unwrap_err().to_string(),
"Only Unsigned, NamedUnsigned can be converted to u32"
);
assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
assert_eq!(<()>::try_from(i).unwrap_err().to_string(), "Only Unit can be converted to ()");

let mut i = MixedInts::Unsigned(42);
assert_eq!(
i32::try_from(i),
Err("Only SmallInt can be converted to i32")
i32::try_from(i).unwrap_err().to_string(),
"Only SmallInt can be converted to i32"
);
assert_eq!(
i64::try_from(i),
Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
i64::try_from(i).unwrap_err().to_string(),
"Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64"
);
assert_eq!(
<(i32, i32)>::try_from(i),
Err("Only TwoSmallInts can be converted to (i32, i32)")
<(i32, i32)>::try_from(i).unwrap_err().to_string(),
"Only TwoSmallInts can be converted to (i32, i32)"
);
assert_eq!(
<(i64, i64)>::try_from(i),
Err("Only NamedBigInts can be converted to (i64, i64)")
<(i64, i64)>::try_from(i).unwrap_err().to_string(),
"Only NamedBigInts can be converted to (i64, i64)"
);
assert_eq!(Ok(42u32), i.try_into());
assert_eq!(Ok(&42u32), (&i).try_into());
assert_eq!(Ok(&mut 42u32), (&mut i).try_into());
assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
assert_eq!(42u32, i.try_into().unwrap());
assert_eq!(&42u32, <_ as TryInto<&u32>>::try_into(&i).unwrap());
assert_eq!(&mut 42u32, <_ as TryInto<&mut u32>>::try_into(&mut i).unwrap());
assert_eq!(<()>::try_from(i).unwrap_err().to_string(), "Only Unit can be converted to ()");

let mut i = MixedInts::NamedUnsigned { x: 42 };
assert_eq!(
i32::try_from(i),
Err("Only SmallInt can be converted to i32")
i32::try_from(i).unwrap_err().to_string(),
"Only SmallInt can be converted to i32"
);
assert_eq!(
i64::try_from(i),
Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
i64::try_from(i).unwrap_err().to_string(),
"Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64"
);
assert_eq!(
i64::try_from(i),
Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
i64::try_from(i).unwrap_err().to_string(),
"Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64"
);
assert_eq!(
<(i32, i32)>::try_from(i),
Err("Only TwoSmallInts can be converted to (i32, i32)")
<(i32, i32)>::try_from(i).unwrap_err().to_string(),
"Only TwoSmallInts can be converted to (i32, i32)"
);
assert_eq!(
<(i64, i64)>::try_from(i),
Err("Only NamedBigInts can be converted to (i64, i64)")
<(i64, i64)>::try_from(i).unwrap_err().to_string(),
"Only NamedBigInts can be converted to (i64, i64)"
);
assert_eq!(Ok(42u32), i.try_into());
assert_eq!(Ok(&42u32), (&i).try_into());
assert_eq!(Ok(&mut 42u32), (&mut i).try_into());
assert_eq!(<()>::try_from(i), Err("Only Unit can be converted to ()"));
assert_eq!(42u32, i.try_into().unwrap());
assert_eq!(&42u32, <_ as TryInto<&u32>>::try_into(&i).unwrap());
assert_eq!(&mut 42u32, <_ as TryInto<&mut u32>>::try_into(&mut i).unwrap());
assert_eq!(<()>::try_from(i).unwrap_err().to_string(), "Only Unit can be converted to ()");

let i = MixedInts::Unit;
assert_eq!(
i32::try_from(i),
Err("Only SmallInt can be converted to i32")
i32::try_from(i).unwrap_err().to_string(),
"Only SmallInt can be converted to i32"
);
assert_eq!(
i64::try_from(i),
Err("Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64")
i64::try_from(i).unwrap_err().to_string(),
"Only NamedBigInt, UnsignedWithIgnoredField, NamedUnsignedWithIgnnoredField can be converted to i64"
);
assert_eq!(
<(i32, i32)>::try_from(i),
Err("Only TwoSmallInts can be converted to (i32, i32)")
<(i32, i32)>::try_from(i).unwrap_err().to_string(),
"Only TwoSmallInts can be converted to (i32, i32)"
);
assert_eq!(
<(i64, i64)>::try_from(i),
Err("Only NamedBigInts can be converted to (i64, i64)")
<(i64, i64)>::try_from(i).unwrap_err().to_string(),
"Only NamedBigInts can be converted to (i64, i64)"
);
assert_eq!(
u32::try_from(i),
Err("Only Unsigned, NamedUnsigned can be converted to u32")
u32::try_from(i).unwrap_err().to_string(),
"Only Unsigned, NamedUnsigned can be converted to u32"
);
assert_eq!(Ok(()), i.try_into());
assert_eq!((), i.try_into().unwrap());
}

0 comments on commit f5712b6

Please sign in to comment.