doku-derive 0.21.2

A framework for building documentation (internal macro)
Documentation
use darling::{Error, FromMeta, Result, ast::NestedMeta};
use std::{collections::BTreeMap, iter::FromIterator};

const ERROR_EXPECTED_KV_LITERAL: &str = "Expected a key-value string literal such as: `#[doku(meta(\"key = value\"))]`";

#[derive(Clone, Debug, Default)]
pub struct DokuMetas {
    pub metas: BTreeMap<String, String>,
}

impl FromMeta for DokuMetas {
    fn from_list(items: &[NestedMeta]) -> Result<Self> {
        let metas = items
            .iter()
            .map(|item| match item {
                NestedMeta::Lit(syn::Lit::Str(lit)) => Ok((lit, lit.value())),

                _ => {
                    Err(Error::custom(ERROR_EXPECTED_KV_LITERAL)
                        .with_span(item))
                }
            })
            .map(|item| {
                item.and_then(|(lit, lit_value)| {
                    let mut kv = lit_value.splitn(2, '=');

                    let key = kv.next().ok_or_else(|| {
                        Error::custom(ERROR_EXPECTED_KV_LITERAL).with_span(lit)
                    })?;

                    let value = kv.next().ok_or_else(|| {
                        Error::custom(ERROR_EXPECTED_KV_LITERAL).with_span(lit)
                    })?;

                    Ok((key.trim().to_owned(), value.trim().to_owned()))
                })
            })
            .collect::<Result<_>>()?;

        Ok(Self { metas })
    }
}

impl FromIterator<DokuMetas> for DokuMetas {
    fn from_iter<T>(iter: T) -> Self
    where
        T: IntoIterator<Item = DokuMetas>,
    {
        Self {
            metas: iter.into_iter().flat_map(|metas| metas.metas).collect(),
        }
    }
}