Skip to content

Commit

Permalink
Change default error, improve unit tests, and fix impl of skip attr
Browse files Browse the repository at this point in the history
  • Loading branch information
loiclec committed Jan 10, 2023
1 parent 22e8e8b commit fbeb8cc
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 76 deletions.
110 changes: 66 additions & 44 deletions derive/src/parse_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,18 +390,24 @@ impl NamedFieldsInfo {
// `true` iff the field has the needs_predicate attribute
let mut needs_predicate = vec![];

for field in fields.named.iter() {
let mut fields_extra = fields
.named
.into_iter()
.map(|field| {
let attrs = read_deserr_field_attributes(&field.attrs)?;
Ok((field, attrs))
})
.collect::<Result<Vec<_>, syn::Error>>()?;

// We put all the non-skipped fields at the beginning, so that when we iterate
// over the non-skipped key names, we can access their corresponding field names
// using the same index.
fields_extra.sort_by_key(|x| x.1.skipped);

for (field, attrs) in fields_extra.iter() {
let field_name = field.ident.clone().unwrap();
let field_ty = &field.ty;

let attrs = read_deserr_field_attributes(&field.attrs)?;
let renamed = attrs.rename.as_ref().map(|i| i.value());
let key_name = key_name_for_ident(
field_name.to_string(),
data_attrs.rename_all.as_ref(),
renamed.as_deref(),
);

let field_default = if let Some(default) = &attrs.default {
match default {
// #[deserr(default)] => use the Default trait
Expand All @@ -413,35 +419,46 @@ impl NamedFieldsInfo {
quote! { ::std::option::Option::Some(#expr) }
}
}
} else if attrs.skipped {
quote! { ::std::option::Option::Some(::std::default::Default::default()) }
} else {
// no `default` attribute => use the DeserializeFromValue::default() method
quote! { ::deserr::DeserializeFromValue::<#err_ty>::default() }
};

let missing_field_error = match attrs.missing_field_error {
Some(error_function) => {
let field_ty = match attrs.from {
Some(ref from) => from.from_ty.clone(),
None => field_ty.clone(),
};

let field_map = match &attrs.map {
Some(func) => {
quote! {
let deserr_e__ = #error_function ( #key_name, deserr_location__ ) ;
deserr_error__ = ::std::option::Option::Some(<#err_ty as ::deserr::MergeWithError<_>>::merge(
deserr_error__,
deserr_e__,
deserr_location__
)?);
#func
}
}
None => {
quote! {
deserr_error__ = ::std::option::Option::Some(<#err_ty as ::deserr::DeserializeError>::error::<V>(
deserr_error__,
::deserr::ErrorKind::MissingField {
field: #key_name,
},
deserr_location__
)?);
}
quote! { ::std::convert::identity }
}
};

field_names.push(field_name);
field_tys.push(field_ty.clone());
field_defaults.push(field_default);
field_maps.push(field_map);
needs_predicate.push(attrs.needs_predicate);
}

for (field, attrs) in fields_extra.into_iter().filter(|x| !x.1.skipped) {
let field_ty = &field.ty;
let field_name = field.ident.clone().unwrap();

let renamed = attrs.rename.as_ref().map(|i| i.value());
let key_name = key_name_for_ident(
field_name.to_string(),
data_attrs.rename_all.as_ref(),
renamed.as_deref(),
);
let error = match attrs.error {
Some(error) => error,
None => data_attrs
Expand Down Expand Up @@ -472,30 +489,35 @@ impl NamedFieldsInfo {
.as_ref()
.map(|from| from.function.error_ty.clone());

let field_map = match attrs.map {
Some(func) => {
let missing_field_error = match &attrs.missing_field_error {
Some(error_function) => {
quote! {
#func
let deserr_e__ = #error_function ( #key_name, deserr_location__ ) ;
deserr_error__ = ::std::option::Option::Some(<#err_ty as ::deserr::MergeWithError<_>>::merge(
deserr_error__,
deserr_e__,
deserr_location__
)?);
}
}
None => {
quote! { ::std::convert::identity }
quote! {
deserr_error__ = ::std::option::Option::Some(<#err_ty as ::deserr::DeserializeError>::error::<V>(
deserr_error__,
::deserr::ErrorKind::MissingField {
field: #key_name,
},
deserr_location__
)?);
}
}
};

field_names.push(field_name);
field_tys.push(field_ty.clone());
field_defaults.push(field_default);
field_maps.push(field_map);
needs_predicate.push(attrs.needs_predicate);

if !attrs.skipped {
key_names.push(key_name.clone());
missing_field_errors.push(missing_field_error);
field_errs.push(error);
field_from_fns.push(field_from_fn);
field_from_errors.push(field_from_error);
}
key_names.push(key_name.clone());
field_errs.push(error);
field_from_fns.push(field_from_fn);
field_from_errors.push(field_from_error);
missing_field_errors.push(missing_field_error);
}

// Create the token stream representing the code to handle an unknown field key.
Expand All @@ -510,7 +532,7 @@ impl NamedFieldsInfo {
quote! {
deserr_error__ = ::std::option::Option::Some(<#err_ty as ::deserr::DeserializeError>::error::<V>(
deserr_error__,
deserr::ErrorKind::UnknownKey {
::deserr::ErrorKind::UnknownKey {
key: deserr_key__,
accepted: &[#(#key_names),*],
},
Expand Down
33 changes: 24 additions & 9 deletions src/default_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ use std::num::ParseIntError;
use crate::*;

#[derive(Debug, PartialEq, Eq)]
pub enum DefaultError {
pub struct DefaultError {
pub location: ValuePointer,
pub content: DefaultErrorContent,
}

#[derive(Debug, PartialEq, Eq)]
pub enum DefaultErrorContent {
Unexpected(String),
MissingField(String),
IncorrectValueKind {
Expand Down Expand Up @@ -35,37 +41,46 @@ impl DeserializeError for DefaultError {
fn error<V: IntoValue>(
_self_: Option<Self>,
error: ErrorKind<V>,
_location: ValuePointerRef,
location: ValuePointerRef,
) -> Result<Self, Self> {
Err(match error {
let content = match error {
ErrorKind::IncorrectValueKind {
actual: _,
accepted,
} => Self::IncorrectValueKind {
} => DefaultErrorContent::IncorrectValueKind {
accepted: accepted.to_vec(),
},
ErrorKind::MissingField { field } => Self::MissingField(field.to_string()),
ErrorKind::UnknownKey { key, accepted } => Self::UnknownKey {
ErrorKind::MissingField { field } => {
DefaultErrorContent::MissingField(field.to_string())
}
ErrorKind::UnknownKey { key, accepted } => DefaultErrorContent::UnknownKey {
key: key.to_string(),
accepted: accepted
.iter()
.map(|accepted| accepted.to_string())
.collect(),
},
ErrorKind::UnknownValue { value, accepted } => Self::UnknownValue {
ErrorKind::UnknownValue { value, accepted } => DefaultErrorContent::UnknownValue {
value: value.to_string(),
accepted: accepted
.iter()
.map(|accepted| accepted.to_string())
.collect(),
},
ErrorKind::Unexpected { msg } => Self::Unexpected(msg),
ErrorKind::Unexpected { msg } => DefaultErrorContent::Unexpected(msg),
};
Err(Self {
location: location.to_owned(),
content,
})
}
}

impl From<ParseIntError> for DefaultError {
fn from(value: ParseIntError) -> Self {
Self::Unexpected(value.to_string())
Self {
location: ValuePointerRef::Origin.to_owned(),
content: DefaultErrorContent::Unexpected(value.to_string()),
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod serde_json;
mod value;

pub use default_error::DefaultError;
pub use default_error::DefaultErrorContent;
extern crate self as deserr;

/**
Expand Down
Loading

0 comments on commit fbeb8cc

Please sign in to comment.