mutable_derive 0.3.3

Implementation for derive for Mutable and Softeq
Documentation
use proc_macro::{TokenStream, Span};
use proc_macro_error::abort;
use quote::ToTokens;
use syn::{Item, Meta, NestedMeta};

pub(crate) fn try_(item: TokenStream) -> Result<TokenStream, ()> {
    let item = syn::parse::<Item>(item).map_err(|_| abort!(Span::call_site(), "Could not parse item"))?;

    let struct_ = match item {
        Item::Struct(struct_) => struct_,
        _ => abort!(item, "Observable may only be derived on structures"),
    };

    let (uid_types, uid_names): (Vec<String>, Vec<String>) = struct_.fields.iter()
        .filter(|field| if let Some(meta) = field.attrs.iter().find(|a| a.path.is_ident("softeq")) {
            if let Ok(Meta::List(list)) = meta.parse_meta() {
                list.nested.iter().any(|m| {
                    if let NestedMeta::Meta(Meta::Path(p)) = m {
                        p.is_ident("uid")
                    } else {
                        false
                    }
                })
            } else {
                false
            }
        } else {
            false
        })
        .map(|field| (field.ty.to_token_stream().to_string(), field.ident.as_ref().unwrap().to_string()))
        .unzip();

    if uid_types.is_empty() {
        abort!(struct_, "SoftEq requires at least one unique identifier");
    }

    Ok(format!(r#"
            impl SoftEq for {}{{
                type Uid = ({});
                fn uid(&self) -> Self::Uid{{
                    ({})
                }}
            }}
            "#,
               struct_.ident,
               uid_types.join(", "),
               uid_names.iter().map(|n| format!("self.{n}.clone()")).collect::<Vec<String>>().join(", ")
    ).parse().unwrap())
}