From 5bc3aa055a8850a1076b305103c9e9a922944c94 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Mon, 14 Jul 2025 19:35:27 +0300 Subject: [PATCH] fix: clippy errors --- src/from_file.rs | 176 ++++++++++++++++++++++++++--------------------- src/lib.rs | 4 +- 2 files changed, 101 insertions(+), 79 deletions(-) diff --git a/src/from_file.rs b/src/from_file.rs index 8357cd5..c79641a 100644 --- a/src/from_file.rs +++ b/src/from_file.rs @@ -1,8 +1,8 @@ -use proc_macro::TokenStream; +use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{ - Attribute, Data, DeriveInput, Error, Expr, Fields, GenericParam, Generics, Meta, Result, - WherePredicate, parse_quote, parse2, + Attribute, Data, DeriveInput, Error, Expr, Fields, FieldsNamed, GenericParam, Generics, Meta, + Result, WhereClause, WherePredicate, parse_quote, parse2, }; const WITH_MERGE: bool = cfg!(feature = "merge"); @@ -10,47 +10,72 @@ const WITH_MERGE: bool = cfg!(feature = "merge"); pub fn impl_from_file(input: &DeriveInput) -> Result { let name = &input.ident; let vis = &input.vis; - let generics = add_trait_bouts(input.generics.clone()); + let generics = add_trait_bouds(input.generics.clone()); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let file_ident = format_ident!("{name}File"); - let fields = match &input.data { - Data::Struct(ds) => match &ds.fields { - Fields::Named(fields) => &fields.named, - _ => { - return Err(Error::new_spanned( - &input.ident, - "FromFile can only be derived for structs with named fields", - )); - } - }, - _ => { - return Err(Error::new_spanned( - &input.ident, - "FromFile can only be derived for structs", - )); - } - }; + let fields = extract_named_fields(input)?; + let (field_assignments, file_fields, default_bounds) = process_fields(fields)?; + let where_clause = build_where_clause(where_clause.cloned(), default_bounds)?; + + let derive_clause = build_derive_clause(); + + Ok(quote! { + #derive_clause + #vis struct #file_ident { + #(#file_fields),* + } + + impl #impl_generics #name #ty_generics #where_clause { + pub fn from_file(file: Option<#file_ident #ty_generics>) -> Self { + let file = file.unwrap_or_default(); + Self { + #(#field_assignments),* + } + } + } + + impl #impl_generics From> for #name #ty_generics #where_clause { + fn from(value: Option<#file_ident #ty_generics>) -> Self { + Self::from_file(value) + } + } + }) +} + +fn extract_named_fields(input: &DeriveInput) -> Result<&FieldsNamed> { + match &input.data { + Data::Struct(ds) => match &ds.fields { + Fields::Named(fields) => Ok(fields), + _ => Err(Error::new_spanned( + &input.ident, + "FromFile can only be derived for structs with named fields", + )), + }, + _ => Err(Error::new_spanned( + &input.ident, + "FromFile can only be derived for structs", + )), + } +} + +fn process_fields( + fields: &FieldsNamed, +) -> Result<(Vec, Vec, Vec)> { let mut field_assignments = Vec::new(); let mut file_fields = Vec::new(); let mut default_bounds = Vec::new(); - for field in fields { + for field in &fields.named { let ident = field .ident .as_ref() .ok_or_else(|| Error::new_spanned(field, "Expected named fields"))?; let ty = &field.ty; - let mut default_expr = None; - for attr in &field.attrs { - if let Some(expr) = parse_default_attr(attr)? { - default_expr = Some(expr); - break; - } - } + let default_expr = parse_default_attrs(&field.attrs)?; let field_attrs = if WITH_MERGE { quote! { @@ -76,57 +101,43 @@ pub fn impl_from_file(input: &DeriveInput) -> Result { } } - let where_clause = if default_bounds.is_empty() { - where_clause.cloned() - } else { - let mut where_clause = where_clause.cloned(); - if let Some(wc) = &mut where_clause { - for bound in default_bounds { - let predicate = parse2::(bound.clone()) - .map_err(|_| Error::new_spanned(&bound, "Failed to parse where predicate"))?; - wc.predicates.push(predicate); - } - } else { - where_clause = Some(parse_quote!(where #(#default_bounds),*)); - } - where_clause - }; - - // Conditionally include Merge derive based on feature - let derive_clause = if WITH_MERGE { - quote! { - #[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize, merge::Merge)] - } - } else { - quote! { - #[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)] - } - }; - - Ok(quote! { - #derive_clause - #vis struct #file_ident { - #(#file_fields),* - } - - impl #impl_generics #name #ty_generics #where_clause { - pub fn from_file(file: Option<#file_ident #ty_generics>) -> Self { - let file = file.unwrap_or_default(); - Self { - #(#field_assignments),* - } - } - } - - impl #impl_generics From> for #name #ty_generics #where_clause { - fn from(value: Option<#file_ident #ty_generics>) -> Self { - Self::from_file(value) - } - } - }.into()) + Ok((field_assignments, file_fields, default_bounds)) } -fn add_trait_bouts(mut generics: Generics) -> Generics { +fn build_where_clause( + where_clause: Option, + default_bounds: Vec, +) -> Result> { + if default_bounds.is_empty() { + return Ok(where_clause); + } + + let mut where_clause = where_clause; + if let Some(wc) = &mut where_clause { + for bound in default_bounds { + let predicate = parse2::(bound.clone()) + .map_err(|_| Error::new_spanned(&bound, "Failed to parse where predicate"))?; + wc.predicates.push(predicate); + } + } else { + where_clause = Some(parse_quote!(where #(#default_bounds),*)); + } + Ok(where_clause) +} + +fn build_derive_clause() -> TokenStream { + if WITH_MERGE { + return quote! { + #[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize, merge::Merge)] + }; + } + + quote! { + #[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)] + } +} + +fn add_trait_bouds(mut generics: Generics) -> Generics { for param in &mut generics.params { if let GenericParam::Type(type_param) = param { type_param.bounds.push(parse_quote!(Default)); @@ -135,6 +146,15 @@ fn add_trait_bouts(mut generics: Generics) -> Generics { generics } +fn parse_default_attrs(attrs: &[Attribute]) -> Result> { + for attr in attrs { + if let Some(expr) = parse_default_attr(attr)? { + return Ok(Some(expr)); + } + } + Ok(None) +} + fn parse_default_attr(attr: &Attribute) -> Result> { if !attr.path().is_ident("default") { return Ok(None); diff --git a/src/lib.rs b/src/lib.rs index 2b6c4bb..72d7ae4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,5 +9,7 @@ use syn::{DeriveInput, parse_macro_input}; #[proc_macro_derive(FromFile, attributes(from_file))] pub fn derive_from_file(input: TokenStream) -> TokenStream { let inp = parse_macro_input!(input as DeriveInput); - impl_from_file(&inp).unwrap_or_else(|e| e.to_compile_error().into()) + impl_from_file(&inp) + .unwrap_or_else(|e| e.to_compile_error()) + .into() }