use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use crate::{item_story::ItemStory, Asyncness};
pub(crate) fn generate(input: &ItemStory, asyncness: Asyncness) -> TokenStream {
let ident = match asyncness {
Asyncness::Sync => input.ident.clone(),
Asyncness::Async => format_ident!("Async{}", input.ident),
};
let ext_ident = match asyncness {
Asyncness::Sync => syn::Ident::new("StoryExt", Span::call_site()),
Asyncness::Async => syn::Ident::new("AsyncStoryExt", Span::call_site()),
};
let async_trait = match asyncness {
Asyncness::Sync => quote! {},
Asyncness::Async => quote!(#[narrative::async_trait]),
};
let sig = match asyncness {
Asyncness::Sync => quote! {fn run_all(&mut self)},
Asyncness::Async => quote! {async fn run_all_async(&mut self)},
};
let step_run = match asyncness {
Asyncness::Sync => quote! {step.run(self)},
Asyncness::Async => quote! {step.run_async(self).await},
};
let use_run = match asyncness {
Asyncness::Sync => quote! {use narrative::step::Run;},
Asyncness::Async => quote! {use narrative::step::RunAsync;},
};
quote! {
#async_trait
pub trait #ext_ident: Sized {
type Error: std::error::Error;
#sig -> Result<(), Self::Error>;
}
#async_trait
impl <T: #ident> #ext_ident for T {
type Error = T::Error;
#[inline]
#sig -> Result<(), Self::Error> {
use narrative::step::Step;
#use_run
for step in narrative::story::StoryContext::steps(&StoryContext::default()) {
#step_run?;
}
Ok(())
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_sync() {
let story_syntax = syn::parse_quote! {
trait UserStory {}
};
let actual = generate(&story_syntax, Asyncness::Sync);
let expected = quote! {
pub trait StoryExt: Sized {
type Error: std::error::Error;
fn run_all(&mut self) -> Result<(), Self::Error>;
}
impl <T: UserStory> StoryExt for T {
type Error = T::Error;
#[inline]
fn run_all(&mut self) -> Result<(), Self::Error> {
use narrative::step::Step;
use narrative::step::Run;
for step in narrative::story::StoryContext::steps(&StoryContext::default()) {
step.run(self)?;
}
Ok(())
}
}
};
assert_eq!(actual.to_string(), expected.to_string());
}
#[test]
fn test_async() {
let story_syntax = syn::parse_quote! {
trait UserStory {}
};
let actual = generate(&story_syntax, Asyncness::Async);
let expected = quote! {
#[narrative::async_trait]
pub trait AsyncStoryExt: Sized {
type Error: std::error::Error;
async fn run_all_async(&mut self) -> Result<(), Self::Error>;
}
#[narrative::async_trait]
impl <T: AsyncUserStory> AsyncStoryExt for T {
type Error = T::Error;
#[inline]
async fn run_all_async(&mut self) -> Result<(), Self::Error> {
use narrative::step::Step;
use narrative::step::RunAsync;
for step in narrative::story::StoryContext::steps(&StoryContext::default()) {
step.run_async(self).await?;
}
Ok(())
}
}
};
assert_eq!(actual.to_string(), expected.to_string());
}
}