packable-derive 0.10.0

Derive macro for the `packable` crate.
Documentation
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use syn::{
    parse::{Parse, ParseStream},
    parse_quote, Attribute, Error, Expr, Result,
};

use crate::parse::{parse_kv, parse_kv_after_comma, skip_stream};

pub(crate) struct UnpackErrorInfo {
    pub(crate) unpack_error: syn::Type,
    pub(crate) with: Expr,
}

struct Type(syn::Type);

impl Parse for Type {
    fn parse(input: ParseStream) -> Result<Self> {
        syn::Type::parse(input).map(Self).map_err(|err| {
            Error::new(
                err.span(),
                "The `unpack_error` attribute requires a type for its value.",
            )
        })
    }
}

impl UnpackErrorInfo {
    pub(crate) fn new<'a>(
        filtered_attrs: impl Iterator<Item = &'a Attribute>,
        default_unpack_error: impl FnOnce() -> syn::Type,
    ) -> Result<Self> {
        for attr in filtered_attrs {
            let opt_info =
                attr.parse_args_with(|stream: ParseStream| match parse_kv::<Type>("unpack_error", stream)? {
                    Some(Type(unpack_error)) => {
                        let with = match parse_kv_after_comma("with", stream)? {
                            Some(with) => with,
                            None => parse_quote!(core::convert::identity),
                        };

                        Ok(Some(Self { unpack_error, with }))
                    }
                    None => {
                        skip_stream(stream)?;
                        Ok(None)
                    }
                })?;

            if let Some(info) = opt_info {
                return Ok(info);
            }
        }

        Ok(Self {
            unpack_error: default_unpack_error(),
            with: parse_quote!(core::convert::identity),
        })
    }
}