serde_valid_derive 2.0.2

JSON Schema based validation tool using serde.
Documentation
use std::collections::HashMap;

use crate::types::{CommaSeparatedMetas, Field, NamedField};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};

pub type RenameMap = HashMap<String, TokenStream>;

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).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) {
                    renames.insert(
                        field.ident.to_token_stream().to_string(),
                        quote!(std::borrow::Cow::from(#rename)),
                    );
                }
            }
        }
    }
    Ok(renames)
}

fn find_rename_from_serde_attributes(attribute: &syn::Attribute) -> Option<TokenStream> {
    if let syn::Meta::List(serde_list) = &attribute.meta {
        if let Ok(serde_nested_meta) =
            serde_list.parse_args_with(CommaSeparatedMetas::parse_terminated)
        {
            for serde_meta in serde_nested_meta {
                if let Some(rename) = find_rename_from_serde_rename_attributes(&serde_meta) {
                    return Some(rename);
                }
            }
        }
    }
    None
}

fn find_rename_from_serde_rename_attributes(serde_meta: &syn::Meta) -> Option<TokenStream> {
    match serde_meta {
        syn::Meta::NameValue(rename_name_value) => {
            if let syn::Expr::Lit(syn::ExprLit {
                lit: syn::Lit::Str(lit_str),
                ..
            }) = &rename_name_value.value
            {
                Some(lit_str.to_token_stream())
            } else {
                None
            }
        }
        syn::Meta::List(rename_list) => {
            if let Ok(nested) = rename_list.parse_args_with(CommaSeparatedMetas::parse_terminated) {
                for rename_meta in nested {
                    if !rename_meta.path().is_ident("deserialize") {
                        continue;
                    }
                    if let syn::Meta::NameValue(deserialize_name_value) = rename_meta {
                        if let syn::Expr::Lit(syn::ExprLit {
                            lit: syn::Lit::Str(lit_str),
                            ..
                        }) = &deserialize_name_value.value
                        {
                            return Some(lit_str.to_token_stream());
                        }
                    }
                }
            }

            None
        }
        _ => None,
    }
}