Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ keywords = ["json_schema", "serde", "validation"]
[workspace.dependencies]
itertools = "^0.14.0"
paste = "^1.0"
proc-macro-error2 = { version = "^2.0", default-features = false }
proc-macro2 = "^1.0"
quote = "^1.0"
regex = "^1.12"
Expand Down
1 change: 0 additions & 1 deletion crates/serde_valid_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ proc-macro = true
[dependencies]
itertools.workspace = true
paste.workspace = true
proc-macro-error2 = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
strsim = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub fn extract_custom_message_format(
syn::Meta::NameValue(name_value) => &name_value.path,
};
let custom_message_name = SingleIdentPath::new(custom_message_path)
.map_err(|error| vec![error])?
.ident()
.to_string();

Expand Down Expand Up @@ -71,7 +72,9 @@ fn extract_custom_message_format_from_meta_list(
#[cfg(feature = "fluent")]
message_type @ (MetaListCustomMessage::I18n | MetaListCustomMessage::Fluent) => {
let path = &meta_list.path;
let path_ident = SingleIdentPath::new(path).ident();
let path_ident = SingleIdentPath::new(path)
.map_err(|error| vec![error])?
.ident();
let message_fn_define = meta_list
.parse_args_with(CommaSeparatedNestedMetas::parse_terminated)
.map_err(|error| {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ pub fn extract_generic_custom_validator_from_meta_list(
rename_map: &RenameMap,
) -> Result<Validator, crate::Errors> {
let path = &meta_list.path;
let path_ident = SingleIdentPath::new(path).ident();
let path_ident = SingleIdentPath::new(path)
.map_err(|error| vec![error])?
.ident();
let field_name = field.name();
let field_key = field.key();
let nested = meta_list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ fn inner_extract_field_validator(
syn::Meta::NameValue(name_value) => &name_value.path,
};

let validation_name = SingleIdentPath::new(validation_path).ident().to_string();
let validation_name = SingleIdentPath::new(validation_path)
.map_err(|error| vec![error])?
.ident()
.to_string();

let validator = match (
MetaPathFieldValidation::from_str(&validation_name),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ fn inner_extract_struct_validator(
syn::Meta::NameValue(name_value) => &name_value.path,
};

let validation_name = SingleIdentPath::new(validation_path).ident().to_string();
let validation_name = SingleIdentPath::new(validation_path)
.map_err(|error| vec![error])?
.ident()
.to_string();
let validator = match (
MetaPathStructValidation::from_str(&validation_name),
MetaListStructValidation::from_str(&validation_name),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ fn inner_extract_variant_validator(
syn::Meta::NameValue(name_value) => &name_value.path,
};

let validation_name = SingleIdentPath::new(validation_path).ident().to_string();
let validation_name = SingleIdentPath::new(validation_path)
.map_err(|error| vec![error])?
.ident()
.to_string();
let validator = match (
MetaPathStructValidation::from_str(&validation_name),
MetaListStructValidation::from_str(&validation_name),
Expand Down
2 changes: 1 addition & 1 deletion crates/serde_valid_derive/src/derive/enum_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn expand_enum_variant_named_fields_validation(

let variant_ident = &variant.ident;
let mut fields_idents = CommaSeparatedTokenStreams::new();
let rename_map = collect_serde_rename_map(named_fields);
let rename_map = collect_serde_rename_map(named_fields)?;

let enum_validates = match collect_variant_custom_from_variant(&input.attrs) {
Ok(validations) => {
Expand Down
4 changes: 2 additions & 2 deletions crates/serde_valid_derive/src/derive/named_struct_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub fn expand_named_struct_derive(
) -> Result<TokenStream, crate::Errors> {
let ident = &input.ident;
let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
let rename_map = collect_serde_rename_map(fields);
let rename_map = collect_serde_rename_map(fields)?;

let mut warnings = vec![];
let mut errors = vec![];
Expand Down Expand Up @@ -110,7 +110,7 @@ fn collect_named_field_validators<'a>(
) -> Result<FieldValidators<'a, NamedField<'a>>, crate::Errors> {
let mut errors = vec![];

let named_field = NamedField::new(field);
let named_field = NamedField::new(field).map_err(|error| vec![error])?;
let validators = named_field
.attrs()
.iter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ fn collect_unnamed_field_validators(
) -> Result<FieldValidators<'_, UnnamedField<'_>>, crate::Errors> {
let mut errors = vec![];

let unnamed_field = UnnamedField::new(index, field);
let unnamed_field = UnnamedField::new(index, field).map_err(|error| vec![error])?;

let validators = unnamed_field
.attrs()
Expand Down
21 changes: 21 additions & 0 deletions crates/serde_valid_derive/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ impl Error {
Self::new(input.span(), "#[derive(Validate)] does not support Union.")
}

pub fn path_must_be_single_ident(path: &syn::Path) -> Self {
let path_str = path
.segments
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<_>>()
.join("::");
Self::new(
path.span(),
format!("Path(='{path_str}') must be single ident path."),
)
}

pub fn named_fields_struct_required(field: &syn::Field) -> Self {
Self::new(field.span(), "struct must be named fields struct.")
}

pub fn unnamed_fields_struct_required(field: &syn::Field) -> Self {
Self::new(field.span(), "struct must be unnamed fields struct.")
}

pub fn validate_meta_name_value_not_supported(name_value: &syn::MetaNameValue) -> Self {
Self::new(name_value.span(), "#[validate = ???] not supported.")
}
Expand Down
2 changes: 0 additions & 2 deletions crates/serde_valid_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ use derive::expand_derive;
use error::to_compile_errors;
use error::{Error, Errors};
use proc_macro::TokenStream;
use proc_macro_error2::proc_macro_error;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Validate, attributes(rule, validate, serde_valid))]
#[proc_macro_error]
pub fn derive_validate(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as DeriveInput);

Expand Down
6 changes: 3 additions & 3 deletions crates/serde_valid_derive/src/serde/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use quote::{quote, ToTokens};

pub type RenameMap = HashMap<String, TokenStream>;

pub fn collect_serde_rename_map(fields: &syn::FieldsNamed) -> RenameMap {
pub fn collect_serde_rename_map(fields: &syn::FieldsNamed) -> Result<RenameMap, crate::Errors> {
let mut renames = RenameMap::new();
for field in fields.named.iter() {
let named_field = NamedField::new(field);
let named_field = NamedField::new(field).map_err(|error| vec![error])?;
for attribute in named_field.attrs() {
if attribute.path().is_ident("serde") {
if let Some(rename) = find_rename_from_serde_attributes(attribute) {
Expand All @@ -21,7 +21,7 @@ pub fn collect_serde_rename_map(fields: &syn::FieldsNamed) -> RenameMap {
}
}
}
renames
Ok(renames)
}

fn find_rename_from_serde_attributes(attribute: &syn::Attribute) -> Option<TokenStream> {
Expand Down
16 changes: 7 additions & 9 deletions crates/serde_valid_derive/src/types/field/named.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use super::Field;
use proc_macro_error2::abort;
use quote::quote;
use std::borrow::Cow;
use syn::spanned::Spanned;

#[derive(Debug, Clone)]
pub struct NamedField<'a> {
Expand All @@ -11,14 +9,14 @@ pub struct NamedField<'a> {
}

impl<'a> NamedField<'a> {
pub fn new(field: &'a syn::Field) -> Self {
if field.ident.is_none() {
abort!(field.span(), "struct must be named fields struct.")
}
Self {
name: field.ident.as_ref().unwrap().to_string(),
pub fn new(field: &'a syn::Field) -> Result<Self, crate::Error> {
let Some(ident) = field.ident.as_ref() else {
return Err(crate::Error::named_fields_struct_required(field));
};
Ok(Self {
name: ident.to_string(),
field: Cow::Borrowed(field),
}
})
}
}

Expand Down
9 changes: 4 additions & 5 deletions crates/serde_valid_derive/src/types/field/unnamed.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::Field;
use proc_macro_error2::abort;
use quote::quote;
use std::borrow::Cow;
use std::convert::AsRef;
Expand All @@ -14,16 +13,16 @@ pub struct UnnamedField<'a> {
}

impl<'a> UnnamedField<'a> {
pub fn new(index: usize, field: &'a syn::Field) -> Self {
pub fn new(index: usize, field: &'a syn::Field) -> Result<Self, crate::Error> {
if field.ident.is_some() {
abort!(field.span(), "struct must be unnamed fields struct.")
return Err(crate::Error::unnamed_fields_struct_required(field));
}
Self {
Ok(Self {
name: index.to_string(),
index,
ident: syn::Ident::new(&format!("__{}", index), field.span()),
field: Cow::Borrowed(field),
}
})
}
}

Expand Down
26 changes: 3 additions & 23 deletions crates/serde_valid_derive/src/types/single_ident_path.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
use proc_macro_error2::abort;
use syn::spanned::Spanned;

pub struct SingleIdentPath<'a>(&'a syn::Path);

impl<'a> SingleIdentPath<'a> {
pub fn new(path: &'a syn::Path) -> Self {
pub fn new(path: &'a syn::Path) -> Result<Self, crate::Error> {
if path.get_ident().is_none() {
abort!(
path.span(),
"Path(='{}') must be single ident path.",
path_to_string(path)
)
return Err(crate::Error::path_must_be_single_ident(path));
}
Self(path)
Ok(Self(path))
}

pub fn ident(&self) -> &'a syn::Ident {
self.0.get_ident().unwrap()
}
}

fn path_to_string(path: &syn::Path) -> String {
path.segments
.pairs()
.map(|pair| match pair {
syn::punctuated::Pair::Punctuated(seg, ..) => {
format!("{}::", seg.ident)
}
syn::punctuated::Pair::End(seg) => seg.ident.to_string(),
})
.collect::<Vec<String>>()
.join("")
}
Loading