use super::*;
use syn::{
parse::{Parse, ParseStream},
Token,
};
pub(crate) mod kw {
syn::custom_keyword!(target);
syn::custom_keyword!(freq);
syn::custom_keyword!(max_rate);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Target {
kw: kw::target,
colon: Token![:],
expr: syn::Expr,
}
impl Parse for Target {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self { kw: input.parse()?, colon: input.parse()?, expr: input.parse()? })
}
}
impl ToTokens for Target {
fn to_tokens(&self, tokens: &mut TokenStream) {
let kw = &self.kw;
let colon = &self.colon;
let expr = &self.expr;
tokens.extend(quote! {
#kw #colon #expr
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum FormatMarker {
Questionmark(Token![?]),
Percentage(Token![%]),
None,
}
impl Parse for FormatMarker {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![?]) {
input.parse().map(Self::Questionmark)
} else if lookahead.peek(Token![%]) {
input.parse().map(Self::Percentage)
} else {
Ok(Self::None)
}
}
}
impl ToTokens for FormatMarker {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match self {
Self::Percentage(p) => p.to_token_stream(),
Self::Questionmark(q) => q.to_token_stream(),
Self::None => TokenStream::new(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ValueWithAliasIdent {
pub alias: Ident,
pub eq: Token![=],
pub marker: FormatMarker,
pub expr: syn::Expr,
}
impl Parse for ValueWithAliasIdent {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self {
alias: input.parse()?,
eq: input.parse()?,
marker: input.parse()?,
expr: input.parse()?,
})
}
}
impl ToTokens for ValueWithAliasIdent {
fn to_tokens(&self, tokens: &mut TokenStream) {
let alias = &self.alias;
let eq = &self.eq;
let marker = &self.marker;
let expr = &self.expr;
tokens.extend(quote! {
#alias #eq #marker #expr
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ValueWithFormatMarker {
pub marker: FormatMarker,
pub ident: Ident,
pub dot: Option<Token![.]>,
pub inner: Punctuated<syn::Member, Token![.]>,
}
impl Parse for ValueWithFormatMarker {
fn parse(input: ParseStream) -> Result<Self> {
let marker = input.parse::<FormatMarker>()?;
let ident = input.parse::<syn::Ident>()?;
let mut inner = Punctuated::<syn::Member, Token![.]>::new();
let lookahead = input.lookahead1();
let dot = if lookahead.peek(Token![.]) {
let dot = Some(input.parse::<Token![.]>()?);
loop {
let member = input.parse::<syn::Member>()?;
inner.push_value(member);
let lookahead = input.lookahead1();
if !lookahead.peek(Token![.]) {
break
}
let token = input.parse::<Token![.]>()?;
inner.push_punct(token);
}
dot
} else {
None
};
Ok(Self { marker, ident, dot, inner })
}
}
impl ToTokens for ValueWithFormatMarker {
fn to_tokens(&self, tokens: &mut TokenStream) {
let marker = &self.marker;
let ident = &self.ident;
let dot = &self.dot;
let inner = &self.inner;
tokens.extend(quote! {
#marker #ident #dot #inner
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Value {
Alias(ValueWithAliasIdent),
Value(ValueWithFormatMarker),
}
impl Value {
pub fn as_ident(&self) -> &Ident {
match self {
Self::Alias(alias) => &alias.alias,
Self::Value(value) => &value.ident,
}
}
}
impl Parse for Value {
fn parse(input: ParseStream) -> Result<Self> {
if input.fork().parse::<ValueWithAliasIdent>().is_ok() {
input.parse().map(Self::Alias)
} else if input.fork().parse::<ValueWithFormatMarker>().is_ok() {
input.parse().map(Self::Value)
} else {
Err(syn::Error::new(Span::call_site(), "Neither value nor aliased value."))
}
}
}
impl ToTokens for Value {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match self {
Self::Alias(alias) => quote! { #alias },
Self::Value(value) => quote! { #value },
})
}
}
#[derive(Debug, Clone)]
pub(crate) struct FmtGroup {
pub format_str: syn::LitStr,
pub maybe_comma: Option<Token![,]>,
pub rest: TokenStream,
}
impl Parse for FmtGroup {
fn parse(input: ParseStream) -> Result<Self> {
let format_str = input
.parse()
.map_err(|e| syn::Error::new(e.span(), "Expected format specifier"))?;
let (maybe_comma, rest) = if input.peek(Token![,]) {
let comma = input.parse::<Token![,]>()?;
let rest = input.parse()?;
(Some(comma), rest)
} else {
(None, TokenStream::new())
};
if !input.is_empty() {
return Err(syn::Error::new(input.span(), "Unexpected data, expected closing `)`."))
}
Ok(Self { format_str, maybe_comma, rest })
}
}
impl ToTokens for FmtGroup {
fn to_tokens(&self, tokens: &mut TokenStream) {
let format_str = &self.format_str;
let maybe_comma = &self.maybe_comma;
let rest = &self.rest;
tokens.extend(quote! { #format_str #maybe_comma #rest });
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Freq {
kw: kw::freq,
colon: Token![:],
pub expr: syn::Expr,
}
impl Parse for Freq {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self { kw: input.parse()?, colon: input.parse()?, expr: input.parse()? })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct MaxRate {
kw: kw::max_rate,
colon: Token![:],
pub expr: syn::Expr,
}
impl Parse for MaxRate {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self { kw: input.parse()?, colon: input.parse()?, expr: input.parse()? })
}
}
pub(crate) struct ArgsIfFrequent {
pub freq: Freq,
pub max_rate: MaxRate,
pub rest: TokenStream,
}
impl Parse for ArgsIfFrequent {
fn parse(input: ParseStream) -> Result<Self> {
let freq = input.parse()?;
let _: Token![,] = input.parse()?;
let max_rate = input.parse()?;
let _: Token![,] = input.parse()?;
let rest = input.parse()?;
Ok(Self { freq, max_rate, rest })
}
}
#[derive(Debug, Clone)]
pub(crate) struct Args {
pub target: Option<Target>,
pub comma: Option<Token![,]>,
pub values: Punctuated<Value, Token![,]>,
pub fmt: Option<FmtGroup>,
}
impl Parse for Args {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
let (target, comma) = if lookahead.peek(kw::target) {
let target = input.parse()?;
let comma = input.parse::<Token![,]>()?;
(Some(target), Some(comma))
} else {
(None, None)
};
let mut values = Punctuated::new();
loop {
if input.fork().parse::<Value>().is_ok() {
values.push_value(input.parse::<Value>()?);
} else {
break
}
if input.peek(Token![,]) {
values.push_punct(input.parse::<Token![,]>()?);
} else {
break
}
}
let fmt = if values.empty_or_trailing() && !input.is_empty() {
let fmt = input.parse::<FmtGroup>()?;
Some(fmt)
} else {
None
};
Ok(Self { target, comma, values, fmt })
}
}
impl ToTokens for Args {
fn to_tokens(&self, tokens: &mut TokenStream) {
let target = &self.target;
let comma = &self.comma;
let values = &self.values;
let fmt = &self.fmt;
tokens.extend(quote! {
#target #comma #values #fmt
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Level {
Error,
Warn,
Info,
Debug,
Trace,
}
impl ToTokens for Level {
fn to_tokens(&self, tokens: &mut TokenStream) {
let span = Span::call_site();
let variant = match self {
Self::Error => Ident::new("ERROR", span),
Self::Warn => Ident::new("WARN", span),
Self::Info => Ident::new("INFO", span),
Self::Debug => Ident::new("DEBUG", span),
Self::Trace => Ident::new("TRACE", span),
};
let krate = support_crate();
tokens.extend(quote! {
#krate :: Level :: #variant
})
}
}