optional-default 0.1.0

Helper macro to allow specifying default values for only some of a struct's fields
Documentation
use std::collections::HashSet;

use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Error, ExprArray, Ident};

pub fn check_required(input: CheckInput) -> TokenStream {
    let required_set = array_to_set(input.required);
    let present_set = array_to_set(input.present);

    let missing = required_set.difference(&present_set).collect::<Vec<_>>();

    if !missing.is_empty() {
        let sp = Span::mixed_site();
        let missing_fields = missing
            .into_iter()
            .map(|ident| ident.to_string())
            .collect::<Vec<String>>()
            .join(", ");
        Error::new(sp, &format!("Missing required fields: {}", missing_fields)).to_compile_error()
    } else {
        quote!()
    }
}

fn array_to_set(array: ExprArray) -> HashSet<Ident> {
    array
        .elems
        .into_iter()
        .map(|expr| match expr {
            syn::Expr::Path(path) => path
                .path
                .require_ident()
                .expect("Field initializers cannot be paths")
                .clone(),
            _ => panic!("Expected path"),
        })
        .collect()
}

pub struct CheckInput {
    required: ExprArray,
    present: ExprArray,
}

impl syn::parse::Parse for CheckInput {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let required = input.parse()?;
        input.parse::<syn::Token![,]>()?;
        let present = input.parse()?;
        Ok(CheckInput { required, present })
    }
}