quick-impl 0.2.1

Quickly implement usual methods and traits for enums and structs.
Documentation
use std::collections::BTreeMap;

use proc_macro2::{Span, TokenStream};
use quote::quote_spanned;
use syn::{Ident, Lit, LitStr};

use crate::{
    order::OrderConfigList,
    utils::{runtime_format, set_lit_str_value},
};

#[must_use]
pub struct Config(BTreeMap<String, (Span, Lit)>);

impl Config {
    pub fn new(order_config_list: &OrderConfigList, main: Option<&str>) -> syn::Result<Self> {
        let mut map = BTreeMap::new();
        match order_config_list {
            OrderConfigList::None => Ok(Self(map)),
            OrderConfigList::Single(lit) => {
                if let Some(main) = main {
                    map.insert(main.to_string(), (lit.span(), lit.clone()));
                } else {
                    return Err(syn::Error::new_spanned(
                        lit,
                        "no default configuration is available",
                    ));
                };
                Ok(Self(map))
            }
            OrderConfigList::Multiple { brace: _, configs } => {
                for config in configs {
                    let old = map.insert(
                        config.ident.to_string(),
                        (config.ident.span(), config.literal.clone()),
                    );

                    if old.is_some() {
                        return Err(syn::Error::new_spanned(
                            &config.ident,
                            format!("duplicate config parameter `{}`", &config.ident),
                        ));
                    }
                }

                Ok(Self(map))
            }
        }
    }

    pub fn finish(self) -> syn::Result<()> {
        if let Some((ident, (span, _))) = self.0.into_iter().next() {
            Err(syn::Error::new(
                span,
                format!("unknown config parameter `{}`", ident),
            ))
        } else {
            Ok(())
        }
    }
}

impl Config {
    pub fn get_lit(&mut self, config_ident: &str) -> Option<Lit> {
        self.0.remove(config_ident).map(|(_, lit)| lit)
    }

    pub fn get_lit_str(&mut self, config_ident: &'static str) -> syn::Result<Option<LitStr>> {
        match self.get_lit(config_ident) {
            Some(Lit::Str(lit_str)) => Ok(Some(lit_str)),
            None => Ok(None),
            Some(lit) => Err(syn::Error::new_spanned(lit, "expected string literal")),
        }
    }

    pub fn get_lit_str_tokens(
        &mut self,
        config_ident: &'static str,
    ) -> syn::Result<Option<TokenStream>> {
        let lit_str = self.get_lit_str(config_ident)?;
        Ok(lit_str.map(|lit_str| {
            let value = lit_str.value();
            let span = lit_str.span();
            quote_spanned! {span=> #value}
        }))
    }

    pub fn get_lit_str_ident(&mut self, config_ident: &'static str) -> syn::Result<Option<Ident>> {
        let lit_str = self.get_lit_str(config_ident)?;
        Ok(lit_str.map(|lit_str| {
            let value = lit_str.value();
            let span = lit_str.span();
            Ident::new(&value, span)
        }))
    }

    pub fn get_formatted_lit_str(
        &mut self,
        config_ident: &'static str,
        default_lit_str: LitStr,
        replacements: impl IntoIterator<Item = impl AsRef<str>>,
    ) -> syn::Result<LitStr> {
        let mut lit_str = self.get_lit_str(config_ident)?.unwrap_or(default_lit_str);
        let value = runtime_format(lit_str.value(), replacements);
        set_lit_str_value(&mut lit_str, value);
        Ok(lit_str)
    }

    pub fn get_formatted_lit_str_ident(
        &mut self,
        config_ident: &'static str,
        default_lit_str: LitStr,
        replacements: impl IntoIterator<Item = impl AsRef<str>>,
    ) -> syn::Result<Ident> {
        let lit_str = self.get_formatted_lit_str(config_ident, default_lit_str, replacements)?;
        let value = lit_str.value();
        let span = lit_str.span();
        Ok(Ident::new(&value, span))
    }
}