use super::runnable::Runnable; use super::setup::Setup; use super::util::{AddableResource, InternalAddResult}; use super::SymbolRunner; use crate::async_utils::join; use crate::loggers::{Logger, StoringLogger}; use crate::resources::{FromArtifact, FromResource}; use crate::symbols::Symbol; use crate::to_artifact::ToArtifact; use crate::{ImplementationBuilder, ResourceLocator}; use async_trait::async_trait; use std::error::Error; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; #[async_trait(?Send)] pub trait AddGeneric { async fn add_generic(&self, x: X) -> InternalAddResult; } macro_rules! add_generic { ( $($name:ident)* ) => ( #[async_trait(?Send)] #[allow(non_snake_case)] impl AddGeneric<($($name,)*)> for Setup where $( RegularSetupCore: SetupCore<$name, Self>, As: FromArtifact<$name>, Rs: FromResource<$name>, $name::Artifact: Clone ),* { #[allow(unused)] async fn add_generic(&self, ($($name,)*): ($($name,)*)) -> Result<(StoringLogger, ($($name::Artifact,)*), bool), (StoringLogger, Box)> { let ($($name,)*) = join!($(self.add($name, false),)*); let logger = StoringLogger::default(); let mut did_run_any = false; $( let (log, artifact, did_run) = $name?; logger.put(log.release()); did_run_any = did_run_any || did_run; let $name = artifact; )* Ok((logger, ($($name,)*), did_run_any)) } } ); } for_each_tuple!(add_generic); // This is for self-referential T // FIXME: Wait for specialization #[async_trait(?Send)] impl< SR: 'static + SymbolRunner, T: AddableResource, Rs: 'static + Hash + Eq + FromResource, As: 'static + FromArtifact + Clone, L: 'static + ResourceLocator>, B: 'static + ImplementationBuilder, > AddGeneric> for Setup where >::Implementation: Runnable + Debug, Self: AddGeneric, T::Artifact: Clone, { async fn add_generic(&self, r: Option) -> InternalAddResult> { Ok(match r { Some(r) => { let (logger, result, did_run) = self.add(r, false).await?; (logger, Some(result), did_run) } None => (StoringLogger::default(), None, false), }) } } #[async_trait(?Send)] impl< T: AddableResource, Rs: 'static + Hash + Eq + FromResource, As: 'static + FromArtifact + Clone, SR: 'static, L: 'static, B: 'static, > AddGeneric for Setup where T::Artifact: Clone, RegularSetupCore: 'static + SetupCore, { async fn add_generic(&self, r: T) -> InternalAddResult { self.add(r, false).await } } #[async_trait(?Send)] pub trait SetupCore { async fn add>( &self, setup: &S, resource: RR, force_run: bool, ) -> InternalAddResult; } #[derive(Debug)] pub struct RegularSetupCore { symbol_runner: SR, phantom: PhantomData<(L, B)>, } impl RegularSetupCore { pub fn new(symbol_runner: SR) -> Self { Self { symbol_runner, phantom: PhantomData::default(), } } } #[async_trait(?Send)] impl SetupCore for RegularSetupCore where B: ImplementationBuilder, >::Implementation: Runnable + Debug, L: ResourceLocator, S: AddGeneric + AddGeneric<>::Prerequisites>, { async fn add>( &self, setup: &S, resource: RR, force_run: bool, ) -> InternalAddResult { let resource = resource.as_ref(); let logger = StoringLogger::new(); logger.write(4, format!("Adding {:?} ... ", resource)); logger.write(4, format!("(force_run is {})", force_run)); let (location, location_prereqs) = L::locate(resource); logger.trace(format!("Adding location prereqs for {:?}", resource)); let result = setup.add_generic(location_prereqs).await; if let Err((log, e)) = result { logger.put(log.release()); return Err((logger, e)); } let (location_prereq_logger, _, location_prereqs_did_run) = result.unwrap(); logger.put(location_prereq_logger.release()); logger.trace(format!( "Location prereqs for {:?} did_run: {}", resource, location_prereqs_did_run )); logger.trace(format!("Adding implementation prereqs for {:?}", resource)); let result = setup.add_generic(B::prerequisites(resource)).await; if let Err((log, e)) = result { logger.put(log.release()); return Err((logger, e)); } let (impl_prereq_logger, prereqs, prereqs_did_run) = result.unwrap(); logger.put(impl_prereq_logger.release()); logger.trace(format!( "Implementation prereqs for {:?} did_run: {}", resource, prereqs_did_run )); logger.trace(format!("Running implementation for {:?}", resource)); let implementation = B::create(resource, &location, prereqs); let did_run_result = implementation .run( &self.symbol_runner, &logger, force_run || location_prereqs_did_run || prereqs_did_run, ) .await; match did_run_result { Ok(did_run) => { logger.write(4, "done."); Ok((logger, location, did_run)) } Err(e) => Err((logger, e)), } } } #[async_trait(?Send)] impl SymbolRunner for RegularSetupCore { async fn run_symbol( &self, symbol: &S, parent_logger: &LOG, force: bool, ) -> Result> { let logger = StoringLogger::new(); logger.write(4, format!("Directly running {:?} ...", symbol)); let result = self.symbol_runner.run_symbol(symbol, &logger, force).await; logger.write(4, "done."); let max_level = if result.is_err() { 5 } else { 3 }; parent_logger.put(logger.release().into_iter().filter(|e| e.0 <= max_level)); result } }