mod local_type_assertions;
mod local_type_impls;
mod base_trait;
mod step_fn;
mod story_trait;
mod step_args;
mod step_types;
mod story_consts;
mod story_context;
mod dummy_environment;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use crate::{item_story::ItemStory, story_attr_syntax::StoryAttr, Asyncness};
pub(crate) fn generate(attr: &StoryAttr, item: &ItemStory) -> TokenStream {
let mod_ident = format_ident!("mod_{}", item.ident);
let ident = &item.ident;
let async_ident = format_ident!("Async{}", item.ident);
let context_ident = format_ident!("{}Context", item.ident);
let base_trait = base_trait::generate(item, Asyncness::Sync);
let async_base_trait = base_trait::generate(item, Asyncness::Async);
let story_trait = story_trait::generate(item, Asyncness::Sync);
let async_story_trait = story_trait::generate(item, Asyncness::Async);
let step_args = step_args::generate(item);
let step_types = step_types::generate(item);
let story_consts = story_consts::generate(item);
let story_context = story_context::generate(attr, item);
let context_ext = story_context::generate_ext(item);
let local_type_impls = local_type_impls::generate(item);
let local_type_assertions = local_type_assertions::generate(item);
let dummy_environment = dummy_environment::generate(item, Asyncness::Sync);
let async_dummy_environment = dummy_environment::generate(item, Asyncness::Async);
let local_type_trait = format_ident!("{}LocalType", ident);
quote! {
#[allow(non_snake_case, unused_imports)]
mod #mod_ident {
use super::*;
#base_trait
#async_base_trait
#story_trait
#async_story_trait
#step_args
#step_types
#story_consts
#story_context
#context_ext
#local_type_impls
#local_type_assertions
#dummy_environment
#async_dummy_environment
}
#[allow(unused_imports)]
use narrative::prelude::*;
#[allow(unused_imports)]
pub use #mod_ident::#ident;
#[allow(unused_imports)]
pub use #mod_ident::#async_ident;
#[allow(unused_imports)]
pub use #mod_ident::StoryContext as #context_ident;
#[allow(unused_imports)]
pub use #mod_ident::#local_type_trait;
pub use #mod_ident::ContextExt as _;
pub use #mod_ident::AsyncContextExt as _;
pub use #mod_ident::BaseTrait as _;
}
}
#[derive(Default)]
struct MatchArms {
arms: Vec<TokenStream>,
cast_as: Option<TokenStream>,
match_target: Option<TokenStream>,
fallback: Option<TokenStream>,
}
impl MatchArms {
pub fn cast_as(mut self, cast_as: TokenStream) -> Self {
self.cast_as = Some(cast_as);
self
}
pub fn match_target(mut self, match_target: TokenStream) -> Self {
self.match_target = Some(match_target);
self
}
}
impl FromIterator<TokenStream> for MatchArms {
fn from_iter<T: IntoIterator<Item = TokenStream>>(iter: T) -> Self {
Self {
arms: iter.into_iter().collect(),
..Default::default()
}
}
}
impl<'a> FromIterator<&'a TokenStream> for MatchArms {
fn from_iter<T: IntoIterator<Item = &'a TokenStream>>(iter: T) -> Self {
Self {
arms: iter.into_iter().cloned().collect(),
..Default::default()
}
}
}
impl ToTokens for MatchArms {
fn to_tokens(&self, tokens: &mut TokenStream) {
let extend = if self.arms.is_empty() {
if let Some(cast_as) = &self.cast_as {
quote! {
#[allow(unreachable_code)]
{
unreachable!() as #cast_as
}
}
} else {
quote! {
unreachable!()
}
}
} else {
let arms = &self.arms;
let match_target = self.match_target.clone().unwrap_or_else(|| quote!(self));
let fallback = self.fallback.as_ref().map(|fallback| {
quote! {
_ => #fallback,
}
});
quote! {
match #match_target {
#(#arms)*
#fallback
}
}
};
tokens.extend(::core::iter::once(extend));
}
}