moxy-derive 0.0.4

derive macros for moxy crate
Documentation
mod compact;
mod debug;
mod default;
#[cfg(feature = "json")]
mod json;
mod keyvalue;
mod map;

use quote::{format_ident, quote};

use crate::core::{Attrs, Field, FieldName};

pub use compact::CompactStyleSyntax;
pub use debug::DebugStyleSyntax;
pub use default::DefaultStyleSyntax;
#[cfg(feature = "json")]
pub use json::JsonStyleSyntax;
pub use keyvalue::KeyValueStyleSyntax;
pub use map::MapStyleSyntax;

use super::{DisplayOptions, DisplayOutput, Segment, SegmentKind};

pub enum StyleSyntax {
    Default(DefaultStyleSyntax),
    Debug(DebugStyleSyntax),
    Compact(CompactStyleSyntax),
    KeyValue(KeyValueStyleSyntax),
    Map(MapStyleSyntax),
    #[cfg(feature = "json")]
    Json(JsonStyleSyntax),
}

impl StyleSyntax {
    pub fn parse(attrs: &Attrs) -> syn::Result<Self> {
        let display = attrs.get("display")?;
        let display_attr = display.iter().find_map(|arg| arg.as_attr());

        let style = if let Some(attr) = display_attr {
            let styles: Vec<_> = attr
                .args()
                .iter()
                .filter_map(|arg| {
                    let n = arg.path().get_ident()?.to_string();
                    let is_style = matches!(n.as_str(), "debug" | "compact" | "keyvalue" | "map")
                        || cfg!(feature = "json") && n == "json";
                    is_style.then_some((n, arg.path().clone()))
                })
                .collect();

            if styles.len() > 1 {
                return Err(syn::Error::new_spanned(
                    &styles[1].1,
                    "conflicting display styles; only one style may be specified",
                ));
            }

            styles.into_iter().next().map(|(n, _)| n)
        } else {
            None
        };

        let pretty = display_attr.map(|a| a.exists("pretty")).unwrap_or(false);

        Ok(match style.as_deref() {
            Some("debug") => Self::Debug(DebugStyleSyntax { pretty }),
            Some("compact") => Self::Compact(CompactStyleSyntax),
            Some("keyvalue") => Self::KeyValue(KeyValueStyleSyntax { pretty }),
            Some("map") => Self::Map(MapStyleSyntax { pretty }),
            #[cfg(feature = "json")]
            Some("json") => Self::Json(JsonStyleSyntax { pretty }),
            _ => Self::Default(DefaultStyleSyntax { pretty }),
        })
    }
}

#[allow(unused)]
impl StyleSyntax {
    pub fn is_default(&self) -> bool {
        matches!(self, Self::Default(_))
    }

    pub fn is_debug(&self) -> bool {
        matches!(self, Self::Debug(_))
    }

    pub fn is_compact(&self) -> bool {
        matches!(self, Self::Compact(_))
    }

    pub fn is_keyvalue(&self) -> bool {
        matches!(self, Self::KeyValue(_))
    }

    pub fn is_map(&self) -> bool {
        matches!(self, Self::Map(_))
    }

    #[cfg(feature = "json")]
    pub fn is_json(&self) -> bool {
        matches!(self, Self::Json(_))
    }

    #[allow(unused)]
    pub fn as_default(&self) -> Option<&DefaultStyleSyntax> {
        match self {
            Self::Default(v) => Some(v),
            _ => None,
        }
    }

    #[allow(unused)]
    pub fn as_debug(&self) -> Option<&DebugStyleSyntax> {
        match self {
            Self::Debug(v) => Some(v),
            _ => None,
        }
    }

    #[allow(unused)]
    pub fn as_compact(&self) -> Option<&CompactStyleSyntax> {
        match self {
            Self::Compact(v) => Some(v),
            _ => None,
        }
    }

    #[allow(unused)]
    pub fn as_keyvalue(&self) -> Option<&KeyValueStyleSyntax> {
        match self {
            Self::KeyValue(v) => Some(v),
            _ => None,
        }
    }

    #[allow(unused)]
    pub fn as_map(&self) -> Option<&MapStyleSyntax> {
        match self {
            Self::Map(v) => Some(v),
            _ => None,
        }
    }

    #[cfg(feature = "json")]
    pub fn as_json(&self) -> Option<&JsonStyleSyntax> {
        match self {
            Self::Json(v) => Some(v),
            _ => None,
        }
    }
}

impl StyleSyntax {
    pub fn is_pretty(&self) -> bool {
        match self {
            Self::Default(v) => v.pretty,
            Self::Debug(v) => v.pretty,
            Self::Compact(_) => false,
            Self::KeyValue(v) => v.pretty,
            Self::Map(v) => v.pretty,
            #[cfg(feature = "json")]
            Self::Json(v) => v.pretty,
        }
    }

    pub fn with_pretty(self, pretty: bool) -> Self {
        match self {
            Self::Default(_) => Self::Default(DefaultStyleSyntax { pretty }),
            Self::Debug(_) => Self::Debug(DebugStyleSyntax { pretty }),
            Self::Compact(_) => Self::Compact(CompactStyleSyntax),
            Self::KeyValue(_) => Self::KeyValue(KeyValueStyleSyntax { pretty }),
            Self::Map(_) => Self::Map(MapStyleSyntax { pretty }),
            #[cfg(feature = "json")]
            Self::Json(_) => Self::Json(JsonStyleSyntax { pretty }),
        }
    }

    pub fn render(&self, opts: &DisplayOptions) -> syn::Result<DisplayOutput> {
        match self {
            Self::Default(v) => v.render(opts),
            Self::Debug(v) => v.render(opts),
            Self::Compact(v) => v.render(opts),
            Self::KeyValue(v) => v.render(opts),
            Self::Map(v) => v.render(opts),
            #[cfg(feature = "json")]
            Self::Json(v) => v.render(opts),
        }
    }
}

pub(super) fn access(f: &Field, use_self: bool) -> proc_macro2::TokenStream {
    let fname = f.name();

    if use_self {
        quote! { self.#fname }
    } else {
        match fname {
            FieldName::Index(idx) => {
                let binding = format_ident!("__v{}", idx.index);
                quote! { #binding }
            }
            FieldName::Ident(_) => quote! { #fname },
        }
    }
}