A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
5.8 KiB

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<X: ToArtifact> {
async fn add_generic(&self, x: X) -> AddResult<X>;
}
macro_rules! add_generic {
( $($name:ident)* ) => (
#[async_trait(?Send)]
#[allow(non_snake_case)]
impl<SR: 'static, _L: 'static, _B: 'static, LOG: 'static + Logger, Rs: 'static + Hash + Eq, As: 'static + Clone, $($name: AddableResource,)*>
AddGeneric<($($name,)*)> for Setup<SR, LOG, _L, _B, Rs, As>
where
$(
RegularSetupCore<SR, _L, _B>: 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<dyn Error>>
{
let x: Result<_, Box<dyn Error>> = 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<T>,
As: 'static + FromArtifact<T> + Clone,
L: 'static + ResourceLocator<T, Prerequisites = Option<T>>,
B: 'static + ImplementationBuilder<T>,
> AddGeneric<Option<T>> for Setup<SR, LOG, L, B, Rs, As>
where
<B as ImplementationBuilder<T>>::Implementation: Runnable + Debug,
Self: AddGeneric<B::Prerequisites>,
T::Artifact: Clone,
{
async fn add_generic(&self, r: Option<T>) -> AddResult<Option<T>> {
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<T>,
As: 'static + FromArtifact<T> + Clone,
SR: 'static,
L: 'static,
B: 'static,
> AddGeneric<T> for Setup<SR, LOG, L, B, Rs, As>
where
T::Artifact: Clone,
RegularSetupCore<SR, L, B>: 'static + SetupCore<T, Self>,
{
async fn add_generic(&self, r: T) -> AddResult<T> {
self.add_force(r, false).await
}
}
#[async_trait(?Send)]
pub trait SetupCore<R: AddableResource, S> {
async fn add<LOG: Logger, RR: AsRef<R>>(
&self,
setup: &S,
parent_logger: &LOG,
resource: RR,
force_run: bool,
) -> AddResult<R>;
}
#[derive(Debug)]
pub struct RegularSetupCore<SR, L, B> {
symbol_runner: SR,
phantom: PhantomData<(L, B)>,
}
impl<SR, L, B> RegularSetupCore<SR, L, B> {
pub fn new(symbol_runner: SR) -> Self {
Self {
symbol_runner,
phantom: PhantomData::default(),
}
}
}
#[async_trait(?Send)]
impl<SR: SymbolRunner, L, B, R: AddableResource, S> SetupCore<R, S> for RegularSetupCore<SR, L, B>
where
B: ImplementationBuilder<R>,
<B as ImplementationBuilder<R>>::Implementation: Runnable + Debug,
L: ResourceLocator<R>,
S: AddGeneric<B::Prerequisites> + AddGeneric<<L as ResourceLocator<R>>::Prerequisites>,
{
async fn add<LOG: Logger, RR: AsRef<R>>(
&self,
setup: &S,
parent_logger: &LOG,
resource: RR,
force_run: bool,
) -> AddResult<R> {
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<SR: SymbolRunner, L, B> SymbolRunner for RegularSetupCore<SR, L, B> {
async fn run_symbol<S: Symbol + Debug, LOG: Logger>(
&self,
symbol: &S,
parent_logger: &LOG,
force: bool,
) -> Result<bool, Box<dyn Error>> {
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
}
}