argley_macro 1.3.0

Macro for the `argley` crate
Documentation
use std::cmp::Ordering;
use std::iter::Sum;

use crate::{parse_eq, OPT_SKIP};
use proc_macro2::{Ident, Literal};
use syn::spanned::Spanned;
use syn::{Attribute, ExprPath};

#[derive(Default)]
pub struct FieldOpts {
    pub skip: bool,
    pub short: bool,
    pub variadic: Option<Ident>,
    pub position: Option<u16>,
    pub rename: Option<Literal>,
    pub formatter: Option<ExprPath>,
}

impl FieldOpts {
    pub fn is_default_field_name(&self) -> bool {
        self.position.is_none() && self.rename.is_none() && self.variadic.is_none()
    }

    pub fn name_prefix(&self) -> String {
        String::from(if self.short { "-" } else { "--" })
    }

    pub fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(match (&self.variadic, &other.variadic) {
            (Some(_), None) => Ordering::Greater,
            (None, Some(_)) => Ordering::Less,
            _ => match (&self.position, &other.position) {
                (Some(_), None) => Ordering::Greater,
                (None, Some(_)) => Ordering::Less,
                (Some(a), Some(b)) => a.cmp(b),
                _ => return None,
            },
        })
    }
}

impl TryFrom<Attribute> for FieldOpts {
    type Error = syn::Error;

    fn try_from(attr: Attribute) -> Result<Self, Self::Error> {
        let mut opts = Self::default();

        attr.parse_nested_meta(|meta| {
            let ident = match meta.path.get_ident() {
                Some(ident) => ident,
                None => {
                    return Err(syn::Error::new(meta.path.span(), "Expected `Ident`"));
                }
            };

            match ident.to_string().as_str() {
                v if v == OPT_SKIP => {
                    opts.skip = true;
                }
                "short" => {
                    opts.short = true;
                }
                "variadic" => {
                    opts.variadic = Some(ident.clone());
                }
                "position" => {
                    let literal = parse_eq::<Literal>(meta.input)?;
                    if let Ok(pos) = literal.to_string().parse() {
                        opts.position = Some(pos);
                    } else {
                        return Err(syn::Error::new(literal.span(), "Position must be a u16"));
                    }
                }
                "formatter" => {
                    opts.formatter = Some(parse_eq(meta.input)?);
                }
                "rename" => {
                    opts.rename = Some(parse_eq(meta.input)?);
                }
                _ => return Err(syn::Error::new(ident.span(), "Unknown option")),
            };

            Ok(())
        })?;

        Ok(opts)
    }
}

impl Sum for FieldOpts {
    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
        iter.reduce(move |mut acc, opts| {
            if opts.skip {
                acc.skip = true;
            }
            if opts.short {
                acc.short = true;
            }
            if opts.variadic.is_some() {
                acc.variadic = opts.variadic;
            }
            if opts.position.is_some() {
                acc.position = opts.position;
            }
            if opts.rename.is_some() {
                acc.rename = opts.rename;
            }

            acc
        })
        .unwrap_or_default()
    }
}