use alloc::{string::ToString, vec::Vec};
use syn::{
Ident, Path,
parse::{Parse, ParseStream},
token::Paren,
};
use crate::{diagnostic::Errors, field::FieldRef, format::Format};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Param {
name: Option<Ident>,
kind: ParamKind,
}
impl Param {
#[inline]
#[must_use]
pub const fn lone(kind: ParamKind) -> Self {
Self { name: None, kind }
}
#[inline]
#[must_use]
pub const fn identified(name: Ident, kind: ParamKind) -> Self {
Self { name: Some(name), kind }
}
#[inline]
#[must_use]
pub fn parts(self) -> (Option<Ident>, ParamKind) {
let Self { name, kind } = self;
(name, kind)
}
#[inline]
pub fn classify<'a>(iter: impl IntoIterator<Item = &'a syn::Attribute>) -> syn::Result<(Vec<Self>, Vec<&'a syn::Attribute>)> {
let attr_iter = iter.into_iter();
let (attr_len, ..) = attr_iter.size_hint();
let mut params = Vec::with_capacity(attr_len);
let mut rest = Vec::new();
let mut errors = Errors::new();
for attr in attr_iter {
match attr.path().get_ident() {
Some(ident) if ident == "error" => match attr.parse_args_with(Self::parse) {
Ok(param) => params.push(param),
Err(error) => errors.push(error),
},
_ => rest.push(attr),
}
}
errors.finish((params, rest))
}
}
impl Parse for Param {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(syn::LitStr) {
return Ok(Self::lone(ParamKind::Format(input.parse()?)));
}
if !lookahead.peek(Ident) {
return Err(lookahead.error());
}
let ident: Ident = input.parse()?;
let kind = match ident.to_string().as_str() {
"inline" => {
let options = if input.peek(Paren) {
let content;
syn::parenthesized!(content in input);
content.parse()?
} else {
Inline::default()
};
ParamKind::Inline(options)
}
"import" => {
let content;
syn::parenthesized!(content in input);
ParamKind::Import(content.parse()?)
}
"source" => {
let content;
syn::parenthesized!(content in input);
ParamKind::Source(content.parse()?)
}
"transparent" => {
let content;
syn::parenthesized!(content in input);
ParamKind::Transparent(Transparent(content.parse()?))
}
"from" => ParamKind::From,
"display" => {
let content;
syn::parenthesized!(content in input);
ParamKind::Display(content.parse()?)
}
_ => {
return Err(syn::Error::new_spanned(
ident,
"expected `inline`, `import`, `source`, `transparent`, `from`, or `display`",
));
}
};
Ok(Self::identified(ident, kind))
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Config {
inline: Option<Inline>,
import: Option<Import>,
}
impl Config {
#[inline]
#[must_use]
pub const fn new(inline: Option<Inline>, import: Option<Import>) -> Self {
Self { inline, import }
}
#[inline]
#[must_use]
pub fn parts(self) -> (Option<Inline>, Option<Import>) {
let Self { inline, import } = self;
(inline, import)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Declaration {
format: Option<Format>,
display: Option<Path>,
source: Option<FieldRef>,
transparent: Option<Transparent>,
from: bool,
}
impl Declaration {
#[inline]
#[must_use]
pub const fn new(
format: Option<Format>,
display: Option<Path>,
source: Option<FieldRef>,
transparent: Option<Transparent>,
from: bool,
) -> Self {
Self {
format,
display,
source,
transparent,
from,
}
}
#[inline]
#[must_use]
pub fn parts(self) -> (Option<Format>, Option<Path>, Option<FieldRef>, Option<Transparent>, bool) {
let Self {
format,
display,
source,
transparent,
from,
} = self;
(format, display, source, transparent, from)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ParamKind {
Inline(Inline),
Import(Import),
Format(Format),
Source(FieldRef),
Transparent(Transparent),
From,
Display(Path),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Inline {
#[default]
Neutral,
Never,
Always,
}
impl Parse for Inline {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
let ident: Ident = input.parse()?;
match ident.to_string().as_str() {
"neutral" => Ok(Self::Neutral),
"never" => Ok(Self::Never),
"always" => Ok(Self::Always),
_ => Err(syn::Error::new_spanned(ident, "expected `neutral`, `never` or `always`")),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Import(pub Path);
impl Parse for Import {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self(input.parse()?))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Transparent(pub FieldRef);
impl Parse for Transparent {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse().map(Self)
}
}