use super::runnable::Runnable; use super::util::{AddResult, AddableResource}; use super::Setup; use super::SymbolRunner; use crate::async_utils::try_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) -> AddResult; } 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<(($($name::Artifact,)*), bool), Box> { let x: Result<_, Box> = try_join!($(self.add_force($name, false),)*); let ($($name,)*) = x?; Ok((($($name.0,)*), false $(|| $name.1)*)) } } ); } for_each_tuple!(add_generic); // This is for self-referential T // FIXME: Wait for specialization #[async_trait(?Send)] impl< SR: 'static + SymbolRunner, LOG: 'static + Logger, 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) -> AddResult> { Ok(match r { Some(r) => { let (result, did_run) = self.add_force(r, false).await?; (Some(result), did_run) } None => (None, false), }) } } #[async_trait(?Send)] impl< LOG: 'static + Logger, 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) -> AddResult { self.add_force(r, false).await } } #[async_trait(?Send)] pub trait SetupCore { async fn add>( &self, setup: &S, parent_logger: &LOG, resource: RR, force_run: bool, ) -> AddResult; } #[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, parent_logger: &LOG, resource: RR, force_run: bool, ) -> AddResult { let resource = resource.as_ref(); let logger = StoringLogger::new(); logger.write(4, format!("Adding {:?} ... ", resource)); let result = { logger.trace(format!(" (force_run is {})", force_run)); let (location, location_prereqs) = L::locate(resource); logger.trace(format!("Adding location prereqs for {:?}", resource)); let (_, location_prereqs_did_run) = setup.add_generic(location_prereqs).await?; logger.trace(format!( "Location prereqs for {:?} did_run: {}", resource, location_prereqs_did_run )); logger.trace(format!("Adding implementation prereqs for {:?}", resource)); let (prereqs, prereqs_did_run) = setup.add_generic(B::prerequisites(resource)).await?; 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 = implementation .run( &self.symbol_runner, &logger, force_run || location_prereqs_did_run || prereqs_did_run, ) .await?; Ok((location, did_run)) }; logger.write(4, "done."); let max_level = if result.is_err() { 5 } else { 3 }; if parent_logger.put(logger.release().into_iter().filter(|e| e.0 <= max_level)) == 0 { parent_logger.write(3, "."); } result } } #[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.debug(format!("Directly running {:?} ...", symbol)); let result = self.symbol_runner.run_symbol(symbol, &logger, force).await; logger.debug("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 } }