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 },
}
}
}