use proc_macro2::TokenStream as TokenStream2;
use quote::{ToTokens, TokenStreamExt as _};
use syn::{parse::discouraged::Speculative as _, Token};
use crate::delegate_arm::DelegateArm;
#[allow(
clippy::module_name_repetitions,
reason = "this follows syn conventions"
)]
pub struct ExprDelegateMatch {
pub outer_attrs: Vec<syn::Attribute>,
pub match_token: Token![match],
pub expr: Box<syn::Expr>,
pub brace_token: syn::token::Brace,
pub inner_attrs: Vec<syn::Attribute>,
pub arms: Vec<Arm>,
}
impl ToTokens for ExprDelegateMatch {
fn to_tokens(&self, tokens: &mut TokenStream2) {
tokens.append_all(&self.outer_attrs);
self.match_token.to_tokens(tokens);
self.expr.to_tokens(tokens);
self.brace_token.surround(tokens, |tokens| {
tokens.append_all(&self.inner_attrs);
for arm in &self.arms {
arm.to_tokens(tokens);
}
});
}
}
impl syn::parse::Parse for ExprDelegateMatch {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let outer_attrs = input.call(syn::Attribute::parse_outer)?;
let match_token: Token![match] = input.parse()?;
let expr = syn::Expr::parse_without_eager_brace(input)?;
let content;
let brace_token = syn::braced!(content in input);
let inner_attrs = content.call(syn::Attribute::parse_inner)?;
let arms = Arm::parse_all(&content)?;
Ok(Self {
outer_attrs,
match_token,
expr: Box::new(expr),
brace_token,
inner_attrs,
arms,
})
}
}
#[derive(Clone)]
pub enum Arm {
Delegate(DelegateArm),
Regular(syn::Arm),
}
impl Arm {
fn parse_all(input: syn::parse::ParseStream<'_>) -> syn::Result<Vec<Self>> {
let mut arms = Vec::new();
while !input.is_empty() {
arms.push(input.parse()?);
}
Ok(arms)
}
}
impl syn::parse::Parse for Arm {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let fork = input.fork();
let mut delegate_err = None;
let result = fork.parse::<syn::Arm>().map_or_else(
|e| {
delegate_err = Some(e);
input.parse::<DelegateArm>().map(Arm::Delegate)
},
|regular| {
input.advance_to(&fork);
Ok(Self::Regular(regular))
},
);
result.map_or_else(
move |mut regular_err| {
input.advance_to(&fork);
if let Some(delegate_err) = delegate_err {
regular_err.combine(delegate_err);
}
Err(regular_err)
},
Ok,
)
}
}
impl ToTokens for Arm {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
Self::Delegate(d) => d.to_tokens(tokens),
Self::Regular(r) => r.to_tokens(tokens),
}
}
}