Experiment with futures
This commit is contained in:
parent
907fbf95db
commit
2d3e3688fa
44 changed files with 2081 additions and 1242 deletions
|
|
@ -6,8 +6,11 @@ edition = "2018"
|
||||||
build = "src/build.rs"
|
build = "src/build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
users = "0.7.0"
|
users = "0.10.0"
|
||||||
regex = "1.0.1"
|
regex = "1.0.1"
|
||||||
|
futures = "0.3"
|
||||||
|
async-trait = "0.1"
|
||||||
|
tokio = { version = "0.2", features = ["process", "io-util", "rt-core", "macros"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempdir = "0.3"
|
tempdir = "0.3"
|
||||||
|
|
|
||||||
87
src/async_utils.rs
Normal file
87
src/async_utils.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
task::{Context, Poll, Waker},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use async_trait::async_trait;
|
||||||
|
|
||||||
|
pub fn run<F: Future>(future: F) -> F::Output {
|
||||||
|
tokio::runtime::Runtime::new().unwrap().block_on(future)
|
||||||
|
}
|
||||||
|
pub use tokio::try_join;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TimerFuture {
|
||||||
|
state: Arc<Mutex<State>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum State {
|
||||||
|
NotStarted(Duration),
|
||||||
|
Running(Waker),
|
||||||
|
Completed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for TimerFuture {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
if let State::Completed = *state {
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let State::NotStarted(duration) = *state {
|
||||||
|
let thread_state = self.state.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
thread::sleep(duration);
|
||||||
|
let mut state = thread_state.lock().unwrap();
|
||||||
|
let waker = if let State::Running(waker) = &*state {
|
||||||
|
Some(waker.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
*state = State::Completed;
|
||||||
|
if let Some(w) = waker {
|
||||||
|
w.wake()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
*state = State::Running(cx.waker().clone());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sleep(duration: Duration) -> impl Future<Output = ()> {
|
||||||
|
TimerFuture {
|
||||||
|
state: Arc::new(Mutex::new(State::NotStarted(duration))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::async_utils::{run, sleep};
|
||||||
|
use futures::future::FutureExt;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sleep() {
|
||||||
|
run(async {
|
||||||
|
let start = Instant::now();
|
||||||
|
let sleep = sleep(Duration::from_millis(100)).fuse();
|
||||||
|
let ok = async {}.fuse();
|
||||||
|
futures::pin_mut!(sleep, ok);
|
||||||
|
loop {
|
||||||
|
futures::select! {
|
||||||
|
_ = sleep => {},
|
||||||
|
_ = ok => assert!((Instant::now() - start).as_millis() < 100),
|
||||||
|
complete => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
200
src/builder.rs
200
src/builder.rs
|
|
@ -39,48 +39,49 @@ use crate::to_artifact::ToArtifact;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub trait SymbolBuilder<R> {
|
pub trait ImplementationBuilder<R> {
|
||||||
type Prerequisites: ToArtifact;
|
type Prerequisites: ToArtifact;
|
||||||
fn prerequisites(resource: &R) -> Self::Prerequisites;
|
fn prerequisites(resource: &R) -> Self::Prerequisites;
|
||||||
|
|
||||||
type Symbol;
|
type Implementation;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &R,
|
resource: &R,
|
||||||
target: &R::Artifact,
|
target: &R::Artifact,
|
||||||
inputs: <Self::Prerequisites as ToArtifact>::Artifact,
|
inputs: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol
|
) -> Self::Implementation
|
||||||
where
|
where
|
||||||
R: Resource;
|
R: Resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DefaultBuilder;
|
pub struct DefaultBuilder;
|
||||||
|
|
||||||
impl<D> SymbolBuilder<Key<D>> for DefaultBuilder {
|
impl<D> ImplementationBuilder<Key<D>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &Key<D>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &Key<D>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = KeySymbol<StdCommandRunner, PathBuf>;
|
type Implementation = KeySymbol<StdCommandRunner, PathBuf>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &Key<D>,
|
_resource: &Key<D>,
|
||||||
target: &<Key<D> as Resource>::Artifact,
|
target: &<Key<D> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
KeySymbol::new(StdCommandRunner, target.clone().into())
|
KeySymbol::new(StdCommandRunner, target.clone().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<Csr<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<Csr<D>> for DefaultBuilder {
|
||||||
type Prerequisites = Key<D>;
|
type Prerequisites = Key<D>;
|
||||||
fn prerequisites(resource: &Csr<D>) -> Self::Prerequisites {
|
fn prerequisites(resource: &Csr<D>) -> Self::Prerequisites {
|
||||||
Key(resource.0.clone())
|
Key(resource.0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = CsrSymbol<StdCommandRunner, D, PathBuf, PathBuf>;
|
type Implementation = CsrSymbol<StdCommandRunner, D, PathBuf, PathBuf>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &Csr<D>,
|
resource: &Csr<D>,
|
||||||
target: &<Csr<D> as Resource>::Artifact,
|
target: &<Csr<D> as Resource>::Artifact,
|
||||||
key: <Self::Prerequisites as ToArtifact>::Artifact,
|
key: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
CsrSymbol::new(
|
CsrSymbol::new(
|
||||||
StdCommandRunner,
|
StdCommandRunner,
|
||||||
resource.0.clone(),
|
resource.0.clone(),
|
||||||
|
|
@ -90,7 +91,7 @@ impl<D: Clone> SymbolBuilder<Csr<D>> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<Cert<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<Cert<D>> for DefaultBuilder {
|
||||||
type Prerequisites = (
|
type Prerequisites = (
|
||||||
Csr<D>,
|
Csr<D>,
|
||||||
AcmeRootCert,
|
AcmeRootCert,
|
||||||
|
|
@ -110,7 +111,7 @@ impl<D: Clone> SymbolBuilder<Cert<D>> for DefaultBuilder {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = CertSymbol<
|
type Implementation = CertSymbol<
|
||||||
SetuidCommandRunner<'static, String, StdCommandRunner>,
|
SetuidCommandRunner<'static, String, StdCommandRunner>,
|
||||||
SetuidCommandRunner<'static, String, StdCommandRunner>,
|
SetuidCommandRunner<'static, String, StdCommandRunner>,
|
||||||
D,
|
D,
|
||||||
|
|
@ -120,7 +121,7 @@ impl<D: Clone> SymbolBuilder<Cert<D>> for DefaultBuilder {
|
||||||
resource: &Cert<D>,
|
resource: &Cert<D>,
|
||||||
target: &<Cert<D> as Resource>::Artifact,
|
target: &<Cert<D> as Resource>::Artifact,
|
||||||
(csr, root_cert, account_key, challenges_dir, user_name, _): <Self::Prerequisites as ToArtifact>::Artifact,
|
(csr, root_cert, account_key, challenges_dir, user_name, _): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
CertSymbol::new(
|
CertSymbol::new(
|
||||||
resource.0.clone(),
|
resource.0.clone(),
|
||||||
SetuidCommandRunner::new(user_name.0, &StdCommandRunner),
|
SetuidCommandRunner::new(user_name.0, &StdCommandRunner),
|
||||||
|
|
@ -133,73 +134,73 @@ impl<D: Clone> SymbolBuilder<Cert<D>> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<CertChain<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<CertChain<D>> for DefaultBuilder {
|
||||||
type Prerequisites = (Cert<D>, AcmeRootCert);
|
type Prerequisites = (Cert<D>, AcmeRootCert);
|
||||||
fn prerequisites(resource: &CertChain<D>) -> Self::Prerequisites {
|
fn prerequisites(resource: &CertChain<D>) -> Self::Prerequisites {
|
||||||
(Cert(resource.0.clone()), AcmeRootCert)
|
(Cert(resource.0.clone()), AcmeRootCert)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = ConcatSymbol<[PathBuf; 2], PathBuf, PathBuf>;
|
type Implementation = ConcatSymbol<[PathBuf; 2], PathBuf, PathBuf>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &CertChain<D>,
|
_resource: &CertChain<D>,
|
||||||
target: &<CertChain<D> as Resource>::Artifact,
|
target: &<CertChain<D> as Resource>::Artifact,
|
||||||
(cert, root_cert): <Self::Prerequisites as ToArtifact>::Artifact,
|
(cert, root_cert): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
ConcatSymbol::new([cert.into(), root_cert.into()], target.clone().into())
|
ConcatSymbol::new([cert.into(), root_cert.into()], target.clone().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<KeyAndCertBundle<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<KeyAndCertBundle<D>> for DefaultBuilder {
|
||||||
type Prerequisites = (CertChain<D>, Key<D>);
|
type Prerequisites = (CertChain<D>, Key<D>);
|
||||||
fn prerequisites(resource: &KeyAndCertBundle<D>) -> Self::Prerequisites {
|
fn prerequisites(resource: &KeyAndCertBundle<D>) -> Self::Prerequisites {
|
||||||
(CertChain(resource.0.clone()), Key(resource.0.clone()))
|
(CertChain(resource.0.clone()), Key(resource.0.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = ConcatSymbol<[PathBuf; 2], PathBuf, PathBuf>;
|
type Implementation = ConcatSymbol<[PathBuf; 2], PathBuf, PathBuf>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &KeyAndCertBundle<D>,
|
_resource: &KeyAndCertBundle<D>,
|
||||||
target: &<KeyAndCertBundle<D> as Resource>::Artifact,
|
target: &<KeyAndCertBundle<D> as Resource>::Artifact,
|
||||||
(cert_chain, key): <Self::Prerequisites as ToArtifact>::Artifact,
|
(cert_chain, key): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
ConcatSymbol::new([key.into(), cert_chain.into()], target.clone().into())
|
ConcatSymbol::new([key.into(), cert_chain.into()], target.clone().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: AsRef<Path> + Clone> SymbolBuilder<File<P>> for DefaultBuilder {
|
impl<P: AsRef<Path> + Clone> ImplementationBuilder<File<P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &File<P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &File<P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = FileSymbol<P, String>;
|
type Implementation = FileSymbol<P, String>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &File<P>,
|
resource: &File<P>,
|
||||||
_target: &<File<P> as Resource>::Artifact,
|
_target: &<File<P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
FileSymbol::new(resource.0.clone(), resource.1.clone())
|
FileSymbol::new(resource.0.clone(), resource.1.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, P: AsRef<Path> + Clone> SymbolBuilder<GitCheckout<'a, P>> for DefaultBuilder {
|
impl<'a, P: AsRef<Path> + Clone> ImplementationBuilder<GitCheckout<'a, P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &GitCheckout<'a, P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &GitCheckout<'a, P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = GitCheckoutSymbol<StdCommandRunner, StdCommandRunner, P, &'a str, &'a str>;
|
type Implementation = GitCheckoutSymbol<StdCommandRunner, StdCommandRunner, P, &'a str, &'a str>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &GitCheckout<'a, P>,
|
resource: &GitCheckout<'a, P>,
|
||||||
_target: &<GitCheckout<'a, P> as Resource>::Artifact,
|
_target: &<GitCheckout<'a, P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
GitCheckoutSymbol::new(resource.0.clone(), resource.1, resource.2, StdCommandRunner)
|
GitCheckoutSymbol::new(resource.0.clone(), resource.1, resource.2, StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolBuilder<DefaultServer> for DefaultBuilder {
|
impl ImplementationBuilder<DefaultServer> for DefaultBuilder {
|
||||||
type Prerequisites = AcmeChallengesNginxSnippet;
|
type Prerequisites = AcmeChallengesNginxSnippet;
|
||||||
fn prerequisites(_resource: &DefaultServer) -> Self::Prerequisites {
|
fn prerequisites(_resource: &DefaultServer) -> Self::Prerequisites {
|
||||||
AcmeChallengesNginxSnippet
|
AcmeChallengesNginxSnippet
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
||||||
);
|
);
|
||||||
|
|
@ -207,7 +208,7 @@ impl SymbolBuilder<DefaultServer> for DefaultBuilder {
|
||||||
_resource: &DefaultServer,
|
_resource: &DefaultServer,
|
||||||
target: &<DefaultServer as Resource>::Artifact,
|
target: &<DefaultServer as Resource>::Artifact,
|
||||||
challenges_snippet_path: <Self::Prerequisites as ToArtifact>::Artifact,
|
challenges_snippet_path: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
target.clone().into(),
|
target.clone().into(),
|
||||||
|
|
@ -218,7 +219,7 @@ impl SymbolBuilder<DefaultServer> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeCustom<D>> for DefaultBuilder {
|
impl<D: AsRef<str> + Clone + Display> ImplementationBuilder<ServeCustom<D>> for DefaultBuilder {
|
||||||
type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
|
type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
|
||||||
fn prerequisites(resource: &ServeCustom<D>) -> Self::Prerequisites {
|
fn prerequisites(resource: &ServeCustom<D>) -> Self::Prerequisites {
|
||||||
(
|
(
|
||||||
|
|
@ -228,7 +229,7 @@ impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeCustom<D>> for DefaultB
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
||||||
);
|
);
|
||||||
|
|
@ -236,7 +237,7 @@ impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeCustom<D>> for DefaultB
|
||||||
resource: &ServeCustom<D>,
|
resource: &ServeCustom<D>,
|
||||||
target: &<ServeCustom<D> as Resource>::Artifact,
|
target: &<ServeCustom<D> as Resource>::Artifact,
|
||||||
(cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
(cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
target.clone().into(),
|
target.clone().into(),
|
||||||
|
|
@ -247,7 +248,7 @@ impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeCustom<D>> for DefaultB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone + Display, P: AsRef<Path>> SymbolBuilder<ServePhp<D, P>> for DefaultBuilder {
|
impl<D: Clone + Display, P: AsRef<Path>> ImplementationBuilder<ServePhp<D, P>> for DefaultBuilder {
|
||||||
type Prerequisites = (
|
type Prerequisites = (
|
||||||
PhpFpmPool<D>,
|
PhpFpmPool<D>,
|
||||||
CertChain<D>,
|
CertChain<D>,
|
||||||
|
|
@ -263,7 +264,7 @@ impl<D: Clone + Display, P: AsRef<Path>> SymbolBuilder<ServePhp<D, P>> for Defau
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
||||||
);
|
);
|
||||||
|
|
@ -271,7 +272,7 @@ impl<D: Clone + Display, P: AsRef<Path>> SymbolBuilder<ServePhp<D, P>> for Defau
|
||||||
resource: &ServePhp<D, P>,
|
resource: &ServePhp<D, P>,
|
||||||
target: &<ServePhp<D, P> as Resource>::Artifact,
|
target: &<ServePhp<D, P> as Resource>::Artifact,
|
||||||
(pool, cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
(pool, cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
target.clone().into(),
|
target.clone().into(),
|
||||||
|
|
@ -288,7 +289,7 @@ impl<D: Clone + Display, P: AsRef<Path>> SymbolBuilder<ServePhp<D, P>> for Defau
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone + Display, P: Clone + AsRef<Path>> SymbolBuilder<ServeService<D, P>>
|
impl<D: Clone + Display, P: Clone + AsRef<Path>> ImplementationBuilder<ServeService<D, P>>
|
||||||
for DefaultBuilder
|
for DefaultBuilder
|
||||||
{
|
{
|
||||||
type Prerequisites = (
|
type Prerequisites = (
|
||||||
|
|
@ -312,7 +313,7 @@ impl<D: Clone + Display, P: Clone + AsRef<Path>> SymbolBuilder<ServeService<D, P
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
||||||
);
|
);
|
||||||
|
|
@ -320,7 +321,7 @@ impl<D: Clone + Display, P: Clone + AsRef<Path>> SymbolBuilder<ServeService<D, P
|
||||||
resource: &ServeService<D, P>,
|
resource: &ServeService<D, P>,
|
||||||
target: &<ServeService<D, P> as Resource>::Artifact,
|
target: &<ServeService<D, P> as Resource>::Artifact,
|
||||||
(socket, cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
(socket, cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
target.clone().into(),
|
target.clone().into(),
|
||||||
|
|
@ -337,7 +338,7 @@ impl<D: Clone + Display, P: Clone + AsRef<Path>> SymbolBuilder<ServeService<D, P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeRedir<D>> for DefaultBuilder {
|
impl<D: AsRef<str> + Clone + Display> ImplementationBuilder<ServeRedir<D>> for DefaultBuilder {
|
||||||
type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
|
type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
|
||||||
fn prerequisites(resource: &ServeRedir<D>) -> Self::Prerequisites {
|
fn prerequisites(resource: &ServeRedir<D>) -> Self::Prerequisites {
|
||||||
(
|
(
|
||||||
|
|
@ -347,7 +348,7 @@ impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeRedir<D>> for DefaultBu
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
||||||
);
|
);
|
||||||
|
|
@ -355,7 +356,7 @@ impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeRedir<D>> for DefaultBu
|
||||||
resource: &ServeRedir<D>,
|
resource: &ServeRedir<D>,
|
||||||
target: &<ServeRedir<D> as Resource>::Artifact,
|
target: &<ServeRedir<D> as Resource>::Artifact,
|
||||||
(cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
(cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
target.clone().into(),
|
target.clone().into(),
|
||||||
|
|
@ -372,7 +373,7 @@ impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeRedir<D>> for DefaultBu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: AsRef<str> + Clone + Display, P: AsRef<Path>> SymbolBuilder<ServeStatic<D, P>>
|
impl<D: AsRef<str> + Clone + Display, P: AsRef<Path>> ImplementationBuilder<ServeStatic<D, P>>
|
||||||
for DefaultBuilder
|
for DefaultBuilder
|
||||||
{
|
{
|
||||||
type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
|
type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
|
||||||
|
|
@ -384,7 +385,7 @@ impl<D: AsRef<str> + Clone + Display, P: AsRef<Path>> SymbolBuilder<ServeStatic<
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
|
||||||
);
|
);
|
||||||
|
|
@ -392,7 +393,7 @@ impl<D: AsRef<str> + Clone + Display, P: AsRef<Path>> SymbolBuilder<ServeStatic<
|
||||||
resource: &ServeStatic<D, P>,
|
resource: &ServeStatic<D, P>,
|
||||||
target: &<ServeStatic<D, P> as Resource>::Artifact,
|
target: &<ServeStatic<D, P> as Resource>::Artifact,
|
||||||
(cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
(cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
target.clone().into(),
|
target.clone().into(),
|
||||||
|
|
@ -409,11 +410,11 @@ impl<D: AsRef<str> + Clone + Display, P: AsRef<Path>> SymbolBuilder<ServeStatic<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<PhpFpmPool<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<PhpFpmPool<D>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &PhpFpmPool<D>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &PhpFpmPool<D>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, String>,
|
ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, String>,
|
||||||
);
|
);
|
||||||
|
|
@ -421,7 +422,7 @@ impl<D: Clone> SymbolBuilder<PhpFpmPool<D>> for DefaultBuilder {
|
||||||
resource: &PhpFpmPool<D>,
|
resource: &PhpFpmPool<D>,
|
||||||
(socket_path, conf_path, user_name, service_name): &<PhpFpmPool<D> as Resource>::Artifact,
|
(socket_path, conf_path, user_name, service_name): &<PhpFpmPool<D> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
conf_path.clone().into(),
|
conf_path.clone().into(),
|
||||||
|
|
@ -432,11 +433,11 @@ impl<D: Clone> SymbolBuilder<PhpFpmPool<D>> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D, P: AsRef<Path>> SymbolBuilder<SystemdSocketService<D, P>> for DefaultBuilder {
|
impl<D, P: AsRef<Path>> ImplementationBuilder<SystemdSocketService<D, P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &SystemdSocketService<D, P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &SystemdSocketService<D, P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
FileSymbol<PathBuf, String>,
|
FileSymbol<PathBuf, String>,
|
||||||
SystemdUserSessionSymbol<'static, String, StdCommandRunner>,
|
SystemdUserSessionSymbol<'static, String, StdCommandRunner>,
|
||||||
UserServiceSymbol<'static, PathBuf, String, StdCommandRunner>,
|
UserServiceSymbol<'static, PathBuf, String, StdCommandRunner>,
|
||||||
|
|
@ -445,7 +446,7 @@ impl<D, P: AsRef<Path>> SymbolBuilder<SystemdSocketService<D, P>> for DefaultBui
|
||||||
resource: &SystemdSocketService<D, P>,
|
resource: &SystemdSocketService<D, P>,
|
||||||
(socket_path, conf_path, user_name): &<SystemdSocketService<D, P> as Resource>::Artifact,
|
(socket_path, conf_path, user_name): &<SystemdSocketService<D, P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
conf_path.clone().into(),
|
conf_path.clone().into(),
|
||||||
|
|
@ -471,44 +472,44 @@ impl<D, P: AsRef<Path>> SymbolBuilder<SystemdSocketService<D, P>> for DefaultBui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Clone> SymbolBuilder<Dir<P>> for DefaultBuilder {
|
impl<P: Clone> ImplementationBuilder<Dir<P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &Dir<P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &Dir<P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = DirSymbol<P>;
|
type Implementation = DirSymbol<P>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &Dir<P>,
|
resource: &Dir<P>,
|
||||||
_target: &<Dir<P> as Resource>::Artifact,
|
_target: &<Dir<P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
DirSymbol::new(resource.0.clone())
|
DirSymbol::new(resource.0.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Clone + AsRef<Path>> SymbolBuilder<NpmInstall<P>> for DefaultBuilder {
|
impl<P: Clone + AsRef<Path>> ImplementationBuilder<NpmInstall<P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &NpmInstall<P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &NpmInstall<P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = NpmInstallSymbol<'static, P, StdCommandRunner>;
|
type Implementation = NpmInstallSymbol<'static, P, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &NpmInstall<P>,
|
resource: &NpmInstall<P>,
|
||||||
_target: &<NpmInstall<P> as Resource>::Artifact,
|
_target: &<NpmInstall<P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
NpmInstallSymbol::new(resource.0.clone(), &StdCommandRunner)
|
NpmInstallSymbol::new(resource.0.clone(), &StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Clone + AsRef<Path>> SymbolBuilder<StoredDirectory<P>> for DefaultBuilder {
|
impl<P: Clone + AsRef<Path>> ImplementationBuilder<StoredDirectory<P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &StoredDirectory<P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &StoredDirectory<P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = SavedDirectorySymbol<StdCommandRunner, StdCommandRunner, P, SimpleStorage>;
|
type Implementation = SavedDirectorySymbol<StdCommandRunner, StdCommandRunner, P, SimpleStorage>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &StoredDirectory<P>,
|
resource: &StoredDirectory<P>,
|
||||||
target: &<StoredDirectory<P> as Resource>::Artifact,
|
target: &<StoredDirectory<P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
SavedDirectorySymbol::new(
|
SavedDirectorySymbol::new(
|
||||||
resource.1.clone(),
|
resource.1.clone(),
|
||||||
SimpleStorage::new(target.clone().into()),
|
SimpleStorage::new(target.clone().into()),
|
||||||
|
|
@ -518,16 +519,16 @@ impl<P: Clone + AsRef<Path>> SymbolBuilder<StoredDirectory<P>> for DefaultBuilde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Clone + AsRef<Path>> SymbolBuilder<LoadedDirectory<P>> for DefaultBuilder {
|
impl<P: Clone + AsRef<Path>> ImplementationBuilder<LoadedDirectory<P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &LoadedDirectory<P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &LoadedDirectory<P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = SavedDirectorySymbol<StdCommandRunner, StdCommandRunner, P, SimpleStorage>;
|
type Implementation = SavedDirectorySymbol<StdCommandRunner, StdCommandRunner, P, SimpleStorage>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &LoadedDirectory<P>,
|
resource: &LoadedDirectory<P>,
|
||||||
target: &<LoadedDirectory<P> as Resource>::Artifact,
|
target: &<LoadedDirectory<P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
SavedDirectorySymbol::new(
|
SavedDirectorySymbol::new(
|
||||||
resource.1.clone(),
|
resource.1.clone(),
|
||||||
SimpleStorage::new(target.clone().into()),
|
SimpleStorage::new(target.clone().into()),
|
||||||
|
|
@ -537,69 +538,69 @@ impl<P: Clone + AsRef<Path>> SymbolBuilder<LoadedDirectory<P>> for DefaultBuilde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<UserForDomain<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<UserForDomain<D>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &UserForDomain<D>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &UserForDomain<D>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = UserSymbol<String, StdCommandRunner>;
|
type Implementation = UserSymbol<String, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &UserForDomain<D>,
|
_resource: &UserForDomain<D>,
|
||||||
(user_name, _home_path): &<UserForDomain<D> as Resource>::Artifact,
|
(user_name, _home_path): &<UserForDomain<D> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
UserSymbol::new(user_name.0.clone(), StdCommandRunner)
|
UserSymbol::new(user_name.0.clone(), StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolBuilder<User> for DefaultBuilder {
|
impl ImplementationBuilder<User> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &User) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &User) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = UserSymbol<String, StdCommandRunner>;
|
type Implementation = UserSymbol<String, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &User,
|
resource: &User,
|
||||||
(): &<User as Resource>::Artifact,
|
(): &<User as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
UserSymbol::new(resource.0.clone(), StdCommandRunner)
|
UserSymbol::new(resource.0.clone(), StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: AsRef<Path> + Clone> SymbolBuilder<Owner<P>> for DefaultBuilder {
|
impl<P: AsRef<Path> + Clone> ImplementationBuilder<Owner<P>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &Owner<P>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &Owner<P>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = OwnerSymbol<StdCommandRunner, StdCommandRunner, P, String>;
|
type Implementation = OwnerSymbol<StdCommandRunner, StdCommandRunner, P, String>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &Owner<P>,
|
resource: &Owner<P>,
|
||||||
(): &<Owner<P> as Resource>::Artifact,
|
(): &<Owner<P> as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
OwnerSymbol::new(resource.1.clone(), resource.0.clone(), StdCommandRunner)
|
OwnerSymbol::new(resource.1.clone(), resource.0.clone(), StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolBuilder<AcmeUser> for DefaultBuilder {
|
impl ImplementationBuilder<AcmeUser> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &AcmeUser) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &AcmeUser) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = UserSymbol<String, StdCommandRunner>;
|
type Implementation = UserSymbol<String, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &AcmeUser,
|
_resource: &AcmeUser,
|
||||||
user_name: &<AcmeUser as Resource>::Artifact,
|
user_name: &<AcmeUser as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
UserSymbol::new(user_name.0.clone(), StdCommandRunner)
|
UserSymbol::new(user_name.0.clone(), StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolBuilder<AcmeChallengesDir> for DefaultBuilder {
|
impl ImplementationBuilder<AcmeChallengesDir> for DefaultBuilder {
|
||||||
type Prerequisites = AcmeUser;
|
type Prerequisites = AcmeUser;
|
||||||
fn prerequisites(_resource: &AcmeChallengesDir) -> Self::Prerequisites {
|
fn prerequisites(_resource: &AcmeChallengesDir) -> Self::Prerequisites {
|
||||||
AcmeUser
|
AcmeUser
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
DirSymbol<PathBuf>,
|
DirSymbol<PathBuf>,
|
||||||
OwnerSymbol<StdCommandRunner, StdCommandRunner, PathBuf, String>,
|
OwnerSymbol<StdCommandRunner, StdCommandRunner, PathBuf, String>,
|
||||||
);
|
);
|
||||||
|
|
@ -607,7 +608,7 @@ impl SymbolBuilder<AcmeChallengesDir> for DefaultBuilder {
|
||||||
_resource: &AcmeChallengesDir,
|
_resource: &AcmeChallengesDir,
|
||||||
target: &<AcmeChallengesDir as Resource>::Artifact,
|
target: &<AcmeChallengesDir as Resource>::Artifact,
|
||||||
user_name: <Self::Prerequisites as ToArtifact>::Artifact,
|
user_name: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
DirSymbol::new(target.clone().into()),
|
DirSymbol::new(target.clone().into()),
|
||||||
OwnerSymbol::new(target.clone().into(), user_name.0, StdCommandRunner),
|
OwnerSymbol::new(target.clone().into(), user_name.0, StdCommandRunner),
|
||||||
|
|
@ -615,18 +616,18 @@ impl SymbolBuilder<AcmeChallengesDir> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolBuilder<AcmeChallengesNginxSnippet> for DefaultBuilder {
|
impl ImplementationBuilder<AcmeChallengesNginxSnippet> for DefaultBuilder {
|
||||||
type Prerequisites = AcmeChallengesDir;
|
type Prerequisites = AcmeChallengesDir;
|
||||||
fn prerequisites(_resource: &AcmeChallengesNginxSnippet) -> Self::Prerequisites {
|
fn prerequisites(_resource: &AcmeChallengesNginxSnippet) -> Self::Prerequisites {
|
||||||
AcmeChallengesDir
|
AcmeChallengesDir
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = FileSymbol<PathBuf, String>;
|
type Implementation = FileSymbol<PathBuf, String>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &AcmeChallengesNginxSnippet,
|
_resource: &AcmeChallengesNginxSnippet,
|
||||||
target: &<AcmeChallengesNginxSnippet as Resource>::Artifact,
|
target: &<AcmeChallengesNginxSnippet as Resource>::Artifact,
|
||||||
challenges_dir: <Self::Prerequisites as ToArtifact>::Artifact,
|
challenges_dir: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
FileSymbol::new(
|
FileSymbol::new(
|
||||||
target.clone().into(),
|
target.clone().into(),
|
||||||
nginx::acme_challenges_snippet(challenges_dir),
|
nginx::acme_challenges_snippet(challenges_dir),
|
||||||
|
|
@ -634,13 +635,13 @@ impl SymbolBuilder<AcmeChallengesNginxSnippet> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolBuilder<AcmeAccountKey> for DefaultBuilder {
|
impl ImplementationBuilder<AcmeAccountKey> for DefaultBuilder {
|
||||||
type Prerequisites = AcmeUser;
|
type Prerequisites = AcmeUser;
|
||||||
fn prerequisites(_resource: &AcmeAccountKey) -> Self::Prerequisites {
|
fn prerequisites(_resource: &AcmeAccountKey) -> Self::Prerequisites {
|
||||||
AcmeUser
|
AcmeUser
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
KeySymbol<StdCommandRunner, PathBuf>,
|
KeySymbol<StdCommandRunner, PathBuf>,
|
||||||
OwnerSymbol<StdCommandRunner, StdCommandRunner, PathBuf, String>,
|
OwnerSymbol<StdCommandRunner, StdCommandRunner, PathBuf, String>,
|
||||||
);
|
);
|
||||||
|
|
@ -648,7 +649,7 @@ impl SymbolBuilder<AcmeAccountKey> for DefaultBuilder {
|
||||||
_resource: &AcmeAccountKey,
|
_resource: &AcmeAccountKey,
|
||||||
target: &<AcmeAccountKey as Resource>::Artifact,
|
target: &<AcmeAccountKey as Resource>::Artifact,
|
||||||
user_name: <Self::Prerequisites as ToArtifact>::Artifact,
|
user_name: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
(
|
(
|
||||||
KeySymbol::new(StdCommandRunner, target.clone().into()),
|
KeySymbol::new(StdCommandRunner, target.clone().into()),
|
||||||
OwnerSymbol::new(target.clone().into(), user_name.0, StdCommandRunner),
|
OwnerSymbol::new(target.clone().into(), user_name.0, StdCommandRunner),
|
||||||
|
|
@ -656,41 +657,41 @@ impl SymbolBuilder<AcmeAccountKey> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolBuilder<AcmeRootCert> for DefaultBuilder {
|
impl ImplementationBuilder<AcmeRootCert> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &AcmeRootCert) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &AcmeRootCert) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = FileSymbol<PathBuf, &'static str>;
|
type Implementation = FileSymbol<PathBuf, &'static str>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &AcmeRootCert,
|
_resource: &AcmeRootCert,
|
||||||
target: &<AcmeRootCert as Resource>::Artifact,
|
target: &<AcmeRootCert as Resource>::Artifact,
|
||||||
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
(): <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
FileSymbol::new(target.clone().into(), LETS_ENCRYPT_X3_CROSS_SIGNED)
|
FileSymbol::new(target.clone().into(), LETS_ENCRYPT_X3_CROSS_SIGNED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> SymbolBuilder<MariaDbUser<D>> for DefaultBuilder {
|
impl<D> ImplementationBuilder<MariaDbUser<D>> for DefaultBuilder {
|
||||||
type Prerequisites = ();
|
type Prerequisites = ();
|
||||||
fn prerequisites(_resource: &MariaDbUser<D>) -> Self::Prerequisites {}
|
fn prerequisites(_resource: &MariaDbUser<D>) -> Self::Prerequisites {}
|
||||||
|
|
||||||
type Symbol = MariaDbUserSymbol<'static, String, StdCommandRunner>;
|
type Implementation = MariaDbUserSymbol<'static, String, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
_resource: &MariaDbUser<D>,
|
_resource: &MariaDbUser<D>,
|
||||||
user_name: &<MariaDbUser<D> as Resource>::Artifact,
|
user_name: &<MariaDbUser<D> as Resource>::Artifact,
|
||||||
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
MariaDbUserSymbol::new(user_name.0.clone(), &StdCommandRunner)
|
MariaDbUserSymbol::new(user_name.0.clone(), &StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<MariaDbDatabase<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<MariaDbDatabase<D>> for DefaultBuilder {
|
||||||
type Prerequisites = MariaDbUser<D>;
|
type Prerequisites = MariaDbUser<D>;
|
||||||
fn prerequisites(resource: &MariaDbDatabase<D>) -> Self::Prerequisites {
|
fn prerequisites(resource: &MariaDbDatabase<D>) -> Self::Prerequisites {
|
||||||
MariaDbUser(resource.0.clone())
|
MariaDbUser(resource.0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = (
|
type Implementation = (
|
||||||
MariaDbDatabaseSymbol<'static, String, SimpleStorage, StdCommandRunner>,
|
MariaDbDatabaseSymbol<'static, String, SimpleStorage, StdCommandRunner>,
|
||||||
MariaDbDumpSymbol<'static, String, StdCommandRunner, SimpleStorage>,
|
MariaDbDumpSymbol<'static, String, StdCommandRunner, SimpleStorage>,
|
||||||
);
|
);
|
||||||
|
|
@ -698,7 +699,7 @@ impl<D: Clone> SymbolBuilder<MariaDbDatabase<D>> for DefaultBuilder {
|
||||||
_resource: &MariaDbDatabase<D>,
|
_resource: &MariaDbDatabase<D>,
|
||||||
(db_name, _, data_path): &<MariaDbDatabase<D> as Resource>::Artifact,
|
(db_name, _, data_path): &<MariaDbDatabase<D> as Resource>::Artifact,
|
||||||
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
let db_dump = SimpleStorage::new(data_path.clone().into());
|
let db_dump = SimpleStorage::new(data_path.clone().into());
|
||||||
(
|
(
|
||||||
MariaDbDatabaseSymbol::new(db_name.0.clone(), db_dump.clone(), &StdCommandRunner),
|
MariaDbDatabaseSymbol::new(db_name.0.clone(), db_dump.clone(), &StdCommandRunner),
|
||||||
|
|
@ -707,34 +708,35 @@ impl<D: Clone> SymbolBuilder<MariaDbDatabase<D>> for DefaultBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Clone + AsRef<Path>> SymbolBuilder<WordpressPlugin<P>> for DefaultBuilder {
|
impl<P: Clone + AsRef<Path>> ImplementationBuilder<WordpressPlugin<P>> for DefaultBuilder {
|
||||||
type Prerequisites = Dir<PathBuf>;
|
type Prerequisites = Dir<PathBuf>;
|
||||||
fn prerequisites(resource: &WordpressPlugin<P>) -> Self::Prerequisites {
|
fn prerequisites(resource: &WordpressPlugin<P>) -> Self::Prerequisites {
|
||||||
Dir(resource.0.as_ref().join("wp-content/plugins"))
|
Dir(resource.0.as_ref().join("wp-content/plugins"))
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = WordpressPluginSymbol<'static, P, &'static str, StdCommandRunner>;
|
type Implementation = WordpressPluginSymbol<'static, P, &'static str, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &WordpressPlugin<P>,
|
resource: &WordpressPlugin<P>,
|
||||||
(): &<WordpressPlugin<P> as Resource>::Artifact,
|
(): &<WordpressPlugin<P> as Resource>::Artifact,
|
||||||
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
WordpressPluginSymbol::new(resource.0.clone(), resource.1, &StdCommandRunner)
|
WordpressPluginSymbol::new(resource.0.clone(), resource.1, &StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Clone + AsRef<Path>> SymbolBuilder<WordpressTranslation<P>> for DefaultBuilder {
|
impl<P: Clone + AsRef<Path>> ImplementationBuilder<WordpressTranslation<P>> for DefaultBuilder {
|
||||||
type Prerequisites = Dir<PathBuf>;
|
type Prerequisites = Dir<PathBuf>;
|
||||||
fn prerequisites(resource: &WordpressTranslation<P>) -> Self::Prerequisites {
|
fn prerequisites(resource: &WordpressTranslation<P>) -> Self::Prerequisites {
|
||||||
Dir(resource.0.as_ref().join("wp-content/languages"))
|
Dir(resource.0.as_ref().join("wp-content/languages"))
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = WordpressTranslationSymbol<'static, &'static str, PathBuf, StdCommandRunner>;
|
type Implementation =
|
||||||
|
WordpressTranslationSymbol<'static, &'static str, PathBuf, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &WordpressTranslation<P>,
|
resource: &WordpressTranslation<P>,
|
||||||
(): &<WordpressTranslation<P> as Resource>::Artifact,
|
(): &<WordpressTranslation<P> as Resource>::Artifact,
|
||||||
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
_: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
WordpressTranslationSymbol::new(
|
WordpressTranslationSymbol::new(
|
||||||
resource.0.as_ref().join("wp-content/languages"),
|
resource.0.as_ref().join("wp-content/languages"),
|
||||||
resource.1,
|
resource.1,
|
||||||
|
|
@ -744,18 +746,18 @@ impl<P: Clone + AsRef<Path>> SymbolBuilder<WordpressTranslation<P>> for DefaultB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone> SymbolBuilder<Cron<D>> for DefaultBuilder {
|
impl<D: Clone> ImplementationBuilder<Cron<D>> for DefaultBuilder {
|
||||||
type Prerequisites = UserForDomain<D>;
|
type Prerequisites = UserForDomain<D>;
|
||||||
fn prerequisites(resource: &Cron<D>) -> Self::Prerequisites {
|
fn prerequisites(resource: &Cron<D>) -> Self::Prerequisites {
|
||||||
UserForDomain(resource.0.clone())
|
UserForDomain(resource.0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
type Symbol = CronSymbol<'static, String, String, StdCommandRunner>;
|
type Implementation = CronSymbol<'static, String, String, StdCommandRunner>;
|
||||||
fn create(
|
fn create(
|
||||||
resource: &Cron<D>,
|
resource: &Cron<D>,
|
||||||
(): &<Cron<D> as Resource>::Artifact,
|
(): &<Cron<D> as Resource>::Artifact,
|
||||||
user_name: <Self::Prerequisites as ToArtifact>::Artifact,
|
user_name: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
) -> Self::Symbol {
|
) -> Self::Implementation {
|
||||||
CronSymbol::new((user_name.0).0, resource.1.clone(), &StdCommandRunner)
|
CronSymbol::new((user_name.0).0, resource.1.clone(), &StdCommandRunner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::io::{Result as IoResult, Write};
|
use std::io::Result as IoResult;
|
||||||
use std::process::Command;
|
|
||||||
use std::process::Output;
|
use std::process::Output;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! args {
|
macro_rules! args {
|
||||||
|
|
@ -13,46 +15,48 @@ macro_rules! args {
|
||||||
($($x:expr,)*) => (args![$($x),*]) // handle trailing commas
|
($($x:expr,)*) => (args![$($x),*]) // handle trailing commas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_success(output: Output) -> Result<Output, Box<dyn Error>> {
|
||||||
|
if output.status.success() {
|
||||||
|
Ok(output)
|
||||||
|
} else {
|
||||||
|
Err(String::from_utf8(output.stderr)?.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_success(res: Result<Output, impl Error + 'static>) -> Result<Output, Box<dyn Error>> {
|
||||||
|
check_success(res?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_output(output: Output) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
Ok(check_success(output)?.stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
pub trait CommandRunner {
|
pub trait CommandRunner {
|
||||||
fn run_with_args_and_stdin(
|
async fn run(&self, program: &str, args: &[&OsStr], stdin: &str) -> IoResult<Output>;
|
||||||
&self,
|
|
||||||
program: &str,
|
async fn run_with_args(&self, program: &str, args: &[&OsStr]) -> IoResult<Output> {
|
||||||
args: &[&OsStr],
|
self.run(program, args, "").await
|
||||||
stdin: &str,
|
|
||||||
) -> IoResult<Output>;
|
|
||||||
fn run_with_args(&self, program: &str, args: &[&OsStr]) -> IoResult<Output> {
|
|
||||||
self.run_with_args_and_stdin(program, args, "")
|
|
||||||
}
|
}
|
||||||
fn get_output(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
async fn get_output(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let output = self.run_with_args(program, args)?;
|
let output = self.run_with_args(program, args).await?;
|
||||||
if !output.status.success() {
|
get_output(output)
|
||||||
return Err(String::from_utf8(output.stderr)?.into());
|
|
||||||
}
|
}
|
||||||
Ok(output.stdout)
|
async fn run_successfully(&self, program: &str, args: &[&OsStr]) -> Result<(), Box<dyn Error>> {
|
||||||
|
is_success(self.run(program, args, "").await)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn get_stderr(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
async fn get_stderr(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let output = self.run_with_args(program, args)?;
|
Ok(is_success(self.run_with_args(program, args).await)?.stderr)
|
||||||
if !output.status.success() {
|
|
||||||
return Err(String::from_utf8(output.stderr)?.into());
|
|
||||||
}
|
|
||||||
Ok(output.stderr)
|
|
||||||
}
|
|
||||||
fn run_successfully(&self, program: &str, args: &[&OsStr]) -> Result<(), Box<dyn Error>> {
|
|
||||||
self.get_output(program, args).map(|_| ())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StdCommandRunner;
|
pub struct StdCommandRunner;
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl CommandRunner for StdCommandRunner {
|
impl CommandRunner for StdCommandRunner {
|
||||||
fn run_with_args_and_stdin(
|
async fn run(&self, program: &str, args: &[&OsStr], input: &str) -> IoResult<Output> {
|
||||||
&self,
|
|
||||||
program: &str,
|
|
||||||
args: &[&OsStr],
|
|
||||||
input: &str,
|
|
||||||
) -> IoResult<Output> {
|
|
||||||
// FIXME: logger
|
|
||||||
//println!("{} {:?}", program, args);
|
//println!("{} {:?}", program, args);
|
||||||
let mut child = Command::new(program)
|
let mut child = Command::new(program)
|
||||||
.args(args)
|
.args(args)
|
||||||
|
|
@ -64,9 +68,10 @@ impl CommandRunner for StdCommandRunner {
|
||||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||||
stdin
|
stdin
|
||||||
.write_all(input.as_bytes())
|
.write_all(input.as_bytes())
|
||||||
|
.await
|
||||||
.expect("Failed to write to stdin");
|
.expect("Failed to write to stdin");
|
||||||
let res = child.wait_with_output();
|
let res = child.wait_with_output().await;
|
||||||
println!("{:?}", res);
|
//println!("{:?}", res);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +92,6 @@ impl<'a, U: AsRef<str>, C: CommandRunner> SetuidCommandRunner<'a, U, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::os::unix::process::CommandExt;
|
|
||||||
use users::get_user_by_name;
|
use users::get_user_by_name;
|
||||||
|
|
||||||
struct TempSetEnv<'a> {
|
struct TempSetEnv<'a> {
|
||||||
|
|
@ -115,13 +119,9 @@ impl Drop for TempSetEnv<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<U: AsRef<str>, C: CommandRunner> CommandRunner for SetuidCommandRunner<'_, U, C> {
|
impl<U: AsRef<str>, C: CommandRunner> CommandRunner for SetuidCommandRunner<'_, U, C> {
|
||||||
fn run_with_args_and_stdin(
|
async fn run(&self, program: &str, args: &[&OsStr], input: &str) -> IoResult<Output> {
|
||||||
&self,
|
|
||||||
program: &str,
|
|
||||||
args: &[&OsStr],
|
|
||||||
input: &str,
|
|
||||||
) -> IoResult<Output> {
|
|
||||||
let uid = get_user_by_name(self.user_name.as_ref())
|
let uid = get_user_by_name(self.user_name.as_ref())
|
||||||
.expect("User does not exist")
|
.expect("User does not exist")
|
||||||
.uid();
|
.uid();
|
||||||
|
|
@ -140,9 +140,10 @@ impl<U: AsRef<str>, C: CommandRunner> CommandRunner for SetuidCommandRunner<'_,
|
||||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||||
stdin
|
stdin
|
||||||
.write_all(input.as_bytes())
|
.write_all(input.as_bytes())
|
||||||
|
.await
|
||||||
.expect("Failed to write to stdin");
|
.expect("Failed to write to stdin");
|
||||||
let res = child.wait_with_output();
|
let res = child.wait_with_output().await;
|
||||||
println!("{:?}", res);
|
//println!("{:?}", res);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,24 +171,43 @@ where
|
||||||
|
|
||||||
// Su doesn't set XDG_RUNTIME_DIR
|
// Su doesn't set XDG_RUNTIME_DIR
|
||||||
// https://github.com/systemd/systemd/blob/master/src/login/pam_systemd.c#L439
|
// https://github.com/systemd/systemd/blob/master/src/login/pam_systemd.c#L439
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<'a, C> CommandRunner for SuCommandRunner<'a, C>
|
impl<'a, C> CommandRunner for SuCommandRunner<'a, C>
|
||||||
where
|
where
|
||||||
C: 'a + CommandRunner,
|
C: 'a + CommandRunner,
|
||||||
{
|
{
|
||||||
fn run_with_args_and_stdin(
|
async fn run(&self, program: &str, args: &[&OsStr], input: &str) -> IoResult<Output> {
|
||||||
&self,
|
|
||||||
program: &str,
|
|
||||||
args: &[&OsStr],
|
|
||||||
input: &str,
|
|
||||||
) -> IoResult<Output> {
|
|
||||||
let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program];
|
let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program];
|
||||||
let mut new_args: Vec<&OsStr> = raw_new_args.iter().map(|s| s.as_ref()).collect();
|
let mut new_args: Vec<&OsStr> = raw_new_args.iter().map(|s| s.as_ref()).collect();
|
||||||
new_args.extend_from_slice(args);
|
new_args.extend_from_slice(args);
|
||||||
self
|
self.command_runner.run("su", &new_args, input).await
|
||||||
.command_runner
|
|
||||||
.run_with_args_and_stdin("su", &new_args, input)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {}
|
mod test {
|
||||||
|
use crate::args;
|
||||||
|
use crate::async_utils::run;
|
||||||
|
use crate::command_runner::{CommandRunner, StdCommandRunner};
|
||||||
|
use futures::future::FutureExt;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let c = StdCommandRunner;
|
||||||
|
run(async {
|
||||||
|
let args = args!["1"];
|
||||||
|
let start = Instant::now();
|
||||||
|
let res = c.run("sleep", args, "").fuse();
|
||||||
|
let ps = c.run("ps", args![], "").fuse();
|
||||||
|
futures::pin_mut!(res, ps);
|
||||||
|
loop {
|
||||||
|
futures::select! {
|
||||||
|
_ = res => {},
|
||||||
|
_ = ps => assert!((Instant::now() - start).as_millis() < 1000),
|
||||||
|
complete => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
19
src/lib.rs
19
src/lib.rs
|
|
@ -1,10 +1,10 @@
|
||||||
#![warn(
|
#![warn(
|
||||||
macro_use_extern_crate,
|
macro_use_extern_crate,
|
||||||
meta_variable_misuse,
|
meta_variable_misuse,
|
||||||
|
missing_debug_implementations,
|
||||||
non_ascii_idents,
|
non_ascii_idents,
|
||||||
trivial_numeric_casts,
|
trivial_numeric_casts,
|
||||||
unused,
|
unused,
|
||||||
unreachable_pub,
|
|
||||||
unsafe_code,
|
unsafe_code,
|
||||||
unstable_features,
|
unstable_features,
|
||||||
variant_size_differences,
|
variant_size_differences,
|
||||||
|
|
@ -15,26 +15,27 @@
|
||||||
clippy::pedantic
|
clippy::pedantic
|
||||||
)]
|
)]
|
||||||
#![allow(
|
#![allow(
|
||||||
|
unreachable_pub,
|
||||||
single_use_lifetimes,
|
single_use_lifetimes,
|
||||||
trivial_casts,
|
trivial_casts,
|
||||||
clippy::module_name_repetitions,
|
|
||||||
clippy::cargo_common_metadata,
|
clippy::cargo_common_metadata,
|
||||||
|
clippy::future_not_send,
|
||||||
|
clippy::missing_errors_doc,
|
||||||
|
clippy::module_name_repetitions,
|
||||||
rustdoc,
|
rustdoc,
|
||||||
missing_docs,
|
missing_docs,
|
||||||
missing_copy_implementations,
|
missing_copy_implementations
|
||||||
missing_debug_implementations
|
|
||||||
)]
|
)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod for_each_tuple;
|
mod for_each_tuple;
|
||||||
|
pub mod async_utils;
|
||||||
pub mod bin;
|
pub mod bin;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod command_runner;
|
pub mod command_runner;
|
||||||
pub mod loggers;
|
pub mod loggers;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
pub mod schema;
|
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod symbols;
|
pub mod symbols;
|
||||||
pub mod templates;
|
pub mod templates;
|
||||||
|
|
@ -46,6 +47,8 @@ mod setup;
|
||||||
pub mod static_files;
|
pub mod static_files;
|
||||||
mod to_artifact;
|
mod to_artifact;
|
||||||
|
|
||||||
pub use builder::{DefaultBuilder, SymbolBuilder};
|
pub use builder::{DefaultBuilder, ImplementationBuilder};
|
||||||
pub use locator::{DefaultLocator, DefaultPolicy, Policy, ResourceLocator};
|
pub use locator::{DefaultLocator, DefaultPolicy, Policy, ResourceLocator};
|
||||||
pub use setup::Setup;
|
pub use setup::{
|
||||||
|
DrySymbolRunner, InitializingSymbolRunner, ReportingSymbolRunner, Setup, SymbolRunner,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ pub trait Policy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DefaultPolicy;
|
pub struct DefaultPolicy;
|
||||||
|
|
||||||
impl Policy for DefaultPolicy {}
|
impl Policy for DefaultPolicy {}
|
||||||
|
|
@ -46,6 +47,7 @@ pub trait ResourceLocator<R> {
|
||||||
R: Resource;
|
R: Resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DefaultLocator<P = DefaultPolicy> {
|
pub struct DefaultLocator<P = DefaultPolicy> {
|
||||||
phantom: PhantomData<P>,
|
phantom: PhantomData<P>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
125
src/loggers.rs
125
src/loggers.rs
|
|
@ -1,36 +1,129 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::cmp::min;
|
||||||
use std::io::stderr;
|
use std::io::stderr;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
// The log crate defines
|
||||||
|
// 1 - Error, 2 - Warn, 3 - Info, 4 - Debug, 5 - Trace
|
||||||
|
pub type Level = usize;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Entry<S>(pub Level, pub S);
|
||||||
|
|
||||||
pub trait Logger {
|
pub trait Logger {
|
||||||
fn write(&mut self, msg: &str);
|
fn write<S: AsRef<str> + Into<String>>(&self, level: Level, msg: S)
|
||||||
fn debug(&mut self, msg: &str);
|
where
|
||||||
|
Self: Sized;
|
||||||
|
fn writeln<S: AsRef<str> + Into<String>>(&self, level: Level, msg: S)
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
fn info<S: AsRef<str> + Into<String>>(&self, msg: S)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.writeln(3, msg)
|
||||||
|
}
|
||||||
|
fn debug<S: AsRef<str> + Into<String>>(&self, msg: S)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.writeln(4, msg)
|
||||||
|
}
|
||||||
|
fn trace<S: AsRef<str> + Into<String>>(&self, msg: S)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.writeln(5, msg)
|
||||||
|
}
|
||||||
|
fn put<S: AsRef<str> + Into<String>>(&self, entries: impl IntoIterator<Item = Entry<S>>) -> usize
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let mut c = 0;
|
||||||
|
for item in entries {
|
||||||
|
self.writeln(item.0, item.1);
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StdErrLogger;
|
#[derive(Debug, Default)]
|
||||||
|
pub struct StdErrLogger {
|
||||||
|
line_started: RefCell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Logger for StdErrLogger {
|
impl Logger for StdErrLogger {
|
||||||
fn debug(&mut self, str: &str) {
|
fn write<S: AsRef<str> + Into<String>>(&self, _level: Level, msg: S) {
|
||||||
writeln!(&mut stderr(), "{}", str).unwrap();
|
*self.line_started.borrow_mut() = true;
|
||||||
|
write!(&mut stderr(), "{}", msg.as_ref()).unwrap();
|
||||||
|
}
|
||||||
|
fn writeln<S: AsRef<str> + Into<String>>(&self, _level: Level, msg: S) {
|
||||||
|
if self.line_started.replace(false) {
|
||||||
|
writeln!(&mut stderr()).unwrap();
|
||||||
|
}
|
||||||
|
writeln!(&mut stderr(), "{}", msg.as_ref()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for StdErrLogger {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if *self.line_started.borrow() == true {
|
||||||
|
writeln!(&mut stderr()).unwrap();
|
||||||
}
|
}
|
||||||
fn write(&mut self, str: &str) {
|
|
||||||
writeln!(&mut stderr(), "{}", str).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FilteringLogger<'a> {
|
#[derive(Debug)]
|
||||||
logger: &'a mut dyn Logger,
|
pub struct FilteringLogger<'a, L> {
|
||||||
|
logger: &'a L,
|
||||||
|
max_level: Level,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FilteringLogger<'a> {
|
impl<'a, L> FilteringLogger<'a, L> {
|
||||||
pub fn new(logger: &'a mut dyn Logger) -> Self {
|
pub fn new(logger: &'a L, max_level: Level) -> Self {
|
||||||
FilteringLogger { logger }
|
Self { logger, max_level }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Logger for FilteringLogger<'_> {
|
impl<'a, L: Logger> Logger for FilteringLogger<'a, L> {
|
||||||
fn debug(&mut self, _str: &str) {}
|
fn write<S: AsRef<str> + Into<String>>(&self, level: Level, str: S) {
|
||||||
fn write(&mut self, str: &str) {
|
if level <= self.max_level {
|
||||||
self.logger.write(str)
|
self.logger.write(level, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn writeln<S: AsRef<str> + Into<String>>(&self, level: Level, str: S) {
|
||||||
|
if level <= self.max_level {
|
||||||
|
self.logger.writeln(level, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct StoringLogger {
|
||||||
|
log: RefCell<Vec<Entry<String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StoringLogger {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> Vec<Entry<String>> {
|
||||||
|
self.log.into_inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Logger for StoringLogger {
|
||||||
|
fn write<S: AsRef<str> + Into<String>>(&self, level: Level, line: S) {
|
||||||
|
let mut log = self.log.borrow_mut();
|
||||||
|
let entry = log
|
||||||
|
.pop()
|
||||||
|
.map(|e| Entry(min(e.0, level), e.1 + line.as_ref()))
|
||||||
|
.unwrap_or_else(|| Entry(level, line.into()));
|
||||||
|
log.push(entry);
|
||||||
|
}
|
||||||
|
fn writeln<S: AsRef<str> + Into<String>>(&self, level: Level, line: S) {
|
||||||
|
self.log.borrow_mut().push(Entry(level, line.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -218,28 +218,51 @@ impl<D> Resource for Cron<D> {
|
||||||
type Artifact = ();
|
type Artifact = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BorrowResource<R> {
|
use std::rc::{Rc, Weak};
|
||||||
fn borrow_resource(&self) -> Option<&R>;
|
|
||||||
|
pub trait FromResource<R> {
|
||||||
|
fn from_resource(from: R) -> (Self, Weak<R>)
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromArtifact<R: Resource> {
|
||||||
|
fn from_artifact(from: R::Artifact) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
fn into_artifact(self) -> R::Artifact
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! default_resources {
|
macro_rules! default_resources {
|
||||||
( $($name:ident: $type:ty,)* ) => {
|
( $($name:ident: $type:ty,)* ) => {
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum DefaultResources<'a, D> {
|
pub enum DefaultResources<'a, D> {
|
||||||
$( $name($type) ),*
|
$( $name(Rc<$type>) ),*
|
||||||
}
|
}
|
||||||
|
|
||||||
$(impl<'a, D> From<$type> for DefaultResources<'a, D> {
|
$(impl<'a, D> FromResource<$type> for DefaultResources<'a, D> {
|
||||||
fn from(from: $type) -> Self {
|
fn from_resource(from: $type) -> (Self, Weak<$type>) {
|
||||||
Self::$name(from)
|
let inner = Rc::new(from);
|
||||||
|
(Self::$name(Rc::clone(&inner)), Rc::downgrade(&inner))
|
||||||
}
|
}
|
||||||
})*
|
})*
|
||||||
|
|
||||||
$(impl<'a, D> BorrowResource<$type> for DefaultResources<'a, D> {
|
#[derive(Clone, Debug)]
|
||||||
fn borrow_resource(&self) -> Option<&$type> {
|
pub enum DefaultArtifacts<'a, D> {
|
||||||
|
$( $name(<$type as Resource>::Artifact) ),*
|
||||||
|
}
|
||||||
|
|
||||||
|
$(impl<'a, D> FromArtifact<$type> for DefaultArtifacts<'a, D> {
|
||||||
|
fn from_artifact(from: <$type as Resource>::Artifact) -> Self {
|
||||||
|
Self::$name(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_artifact(self) -> <$type as Resource>::Artifact {
|
||||||
match self {
|
match self {
|
||||||
Self::$name(v) => Some(v),
|
Self::$name(inner) => inner,
|
||||||
_ => None
|
_ => panic!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})*
|
})*
|
||||||
|
|
|
||||||
274
src/schema.rs
274
src/schema.rs
|
|
@ -1,274 +0,0 @@
|
||||||
use crate::loggers::Logger;
|
|
||||||
use crate::symbols::Symbol;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
pub trait SymbolRunner {
|
|
||||||
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: SymbolRunner + ?Sized> SymbolRunner for Box<R> {
|
|
||||||
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
||||||
(**self).run_symbol(symbol, force)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum SymbolRunError {
|
|
||||||
Symbol(Box<dyn Error>),
|
|
||||||
ExecuteDidNotReach(()),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for SymbolRunError {
|
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
|
||||||
match self {
|
|
||||||
Self::Symbol(ref e) => Some(&**e),
|
|
||||||
Self::ExecuteDidNotReach(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for SymbolRunError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Symbol(ref e) => write!(f, "{}", e),
|
|
||||||
Self::ExecuteDidNotReach(_) => write!(f, "Target not reached after executing symbol"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InitializingSymbolRunner<L: Logger> {
|
|
||||||
logger: RefCell<L>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L: Logger> InitializingSymbolRunner<L> {
|
|
||||||
pub fn new(logger: L) -> Self {
|
|
||||||
Self {
|
|
||||||
logger: RefCell::new(logger),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exec_symbol<S: Symbol + Debug>(&self, symbol: &S) -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut logger = self.logger.borrow_mut();
|
|
||||||
logger.write(format!("Executing {:?}", symbol).as_str());
|
|
||||||
symbol.execute()?;
|
|
||||||
let target_reached = symbol.target_reached()?;
|
|
||||||
logger.debug(
|
|
||||||
format!(
|
|
||||||
"Symbol reports target_reached: {:?} (should be true)",
|
|
||||||
target_reached
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
);
|
|
||||||
if target_reached {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Box::new(SymbolRunError::ExecuteDidNotReach(())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
|
|
||||||
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
||||||
let mut logger = self.logger.borrow_mut();
|
|
||||||
let executed = if force {
|
|
||||||
logger.debug("Forcing symbol execution");
|
|
||||||
drop(logger);
|
|
||||||
self.exec_symbol(symbol)?;
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
let target_reached = symbol.target_reached()?;
|
|
||||||
if target_reached {
|
|
||||||
logger.write(format!("{:?} already reached", symbol).as_str());
|
|
||||||
} else {
|
|
||||||
logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str());
|
|
||||||
drop(logger);
|
|
||||||
self.exec_symbol(symbol)?;
|
|
||||||
}
|
|
||||||
!target_reached
|
|
||||||
};
|
|
||||||
Ok(executed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DrySymbolRunner<L: Logger> {
|
|
||||||
logger: RefCell<L>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L: Logger> DrySymbolRunner<L> {
|
|
||||||
pub fn new(logger: L) -> Self {
|
|
||||||
Self {
|
|
||||||
logger: RefCell::new(logger),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L: Logger> SymbolRunner for DrySymbolRunner<L> {
|
|
||||||
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
||||||
let mut logger = self.logger.borrow_mut();
|
|
||||||
let would_execute = if force {
|
|
||||||
logger.write(format!("Would force-execute {:?}", symbol).as_str());
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
let target_reached = symbol.target_reached()?;
|
|
||||||
logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str());
|
|
||||||
if !target_reached {
|
|
||||||
logger.write(format!("Would execute {:?}", symbol).as_str());
|
|
||||||
}
|
|
||||||
!target_reached
|
|
||||||
};
|
|
||||||
Ok(would_execute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ReportingSymbolRunner<'a, R, L>(&'a R, RefCell<L>);
|
|
||||||
|
|
||||||
impl<'a, R, L> ReportingSymbolRunner<'a, R, L> {
|
|
||||||
pub fn new(symbol_runner: &'a R, logger: L) -> Self {
|
|
||||||
ReportingSymbolRunner(symbol_runner, RefCell::new(logger))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R, L> SymbolRunner for ReportingSymbolRunner<'a, R, L>
|
|
||||||
where
|
|
||||||
R: SymbolRunner,
|
|
||||||
L: Logger,
|
|
||||||
{
|
|
||||||
fn run_symbol<S: Symbol + Debug>(&self, symbol: &S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
||||||
let mut logger = self.1.borrow_mut();
|
|
||||||
logger.debug(format!("Running symbol {:?}", symbol).as_str());
|
|
||||||
let res = self.0.run_symbol(symbol, force);
|
|
||||||
if let Err(ref e) = res {
|
|
||||||
logger.write(format!("Failed on {:?} with {}, aborting.", symbol, e).as_str())
|
|
||||||
} else {
|
|
||||||
logger.debug(format!("Successfully finished {:?}", symbol).as_str())
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use crate::loggers::Logger;
|
|
||||||
use crate::schema::InitializingSymbolRunner;
|
|
||||||
use crate::schema::SymbolRunner;
|
|
||||||
use crate::symbols::Symbol;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
enum DummySymbolError {
|
|
||||||
Error(()),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for DummySymbolError {}
|
|
||||||
|
|
||||||
impl fmt::Display for DummySymbolError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Dummy symbol error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DummySymbol<T, E> {
|
|
||||||
_target_reached: RefCell<T>,
|
|
||||||
_execute: RefCell<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
|
||||||
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
|
||||||
> Symbol for DummySymbol<T, E>
|
|
||||||
{
|
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
|
||||||
self._target_reached.borrow_mut().next().unwrap()
|
|
||||||
}
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
|
||||||
self._execute.borrow_mut().next().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
|
||||||
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
|
||||||
> DummySymbol<T, E>
|
|
||||||
{
|
|
||||||
fn new<
|
|
||||||
IE: IntoIterator<IntoIter = E, Item = Result<(), Box<dyn Error>>>,
|
|
||||||
IT: IntoIterator<IntoIter = T, Item = Result<bool, Box<dyn Error>>>,
|
|
||||||
>(
|
|
||||||
target_reached: IT,
|
|
||||||
execute: IE,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
_target_reached: RefCell::new(target_reached.into_iter()),
|
|
||||||
_execute: RefCell::new(execute.into_iter()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DummyLogger {
|
|
||||||
log: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DummyLogger {
|
|
||||||
fn new() -> DummyLogger {
|
|
||||||
DummyLogger { log: Vec::new() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Logger for DummyLogger {
|
|
||||||
fn write(&mut self, line: &str) {
|
|
||||||
self.log.push(line.into());
|
|
||||||
}
|
|
||||||
fn debug(&mut self, line: &str) {
|
|
||||||
self.log.push(line.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nothing_needed_to_be_done() {
|
|
||||||
let result = InitializingSymbolRunner::new(DummyLogger::new())
|
|
||||||
.run_symbol(&DummySymbol::new(vec![Ok(true)], vec![Ok(())]), false);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn everything_is_ok() {
|
|
||||||
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(
|
|
||||||
&DummySymbol::new(vec![Ok(true), Ok(false)], vec![Ok(())]),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn executing_did_not_change_state() {
|
|
||||||
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(
|
|
||||||
&DummySymbol::new(vec![Ok(false), Ok(false)], vec![Ok(())]),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
result.unwrap_err().to_string(),
|
|
||||||
"Target not reached after executing symbol"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn executing_did_not_work() {
|
|
||||||
let err = InitializingSymbolRunner::new(DummyLogger::new())
|
|
||||||
.run_symbol(
|
|
||||||
&DummySymbol::new(
|
|
||||||
vec![Ok(false)],
|
|
||||||
vec![Err(Box::new(DummySymbolError::Error(())) as Box<dyn Error>)],
|
|
||||||
),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.unwrap_err();
|
|
||||||
assert_eq!(err.to_string(), "Dummy symbol error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
396
src/setup.rs
396
src/setup.rs
|
|
@ -1,396 +0,0 @@
|
||||||
use crate::resources::{BorrowResource, DefaultResources, Resource};
|
|
||||||
use crate::schema::SymbolRunner;
|
|
||||||
use crate::symbols::Symbol;
|
|
||||||
use crate::to_artifact::ToArtifact;
|
|
||||||
use crate::{DefaultBuilder, DefaultLocator, ResourceLocator, SymbolBuilder};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
pub trait CanHandle<X: ToArtifact> {
|
|
||||||
fn handle(&mut self, x: X) -> Result<(X::Artifact, bool), Box<dyn Error>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! can_handle {
|
|
||||||
( $($name:ident)* ) => (
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, $($name: Resource,)*>
|
|
||||||
CanHandle<($($name,)*)>
|
|
||||||
for Setup<_SR, _L, _R, _B>
|
|
||||||
where
|
|
||||||
$(
|
|
||||||
_B: SymbolBuilder<$name>,
|
|
||||||
<_B as SymbolBuilder<$name>>::Symbol: Runnable + Debug,
|
|
||||||
_L: ResourceLocator<$name>,
|
|
||||||
_R: From<$name> + BorrowResource<$name>,
|
|
||||||
Self: CanHandle<<_B as SymbolBuilder<$name>>::Prerequisites> +
|
|
||||||
CanHandle<<_L as ResourceLocator<$name>>::Prerequisites>
|
|
||||||
),*
|
|
||||||
{
|
|
||||||
fn handle(&mut self, ($($name,)*): ($($name,)*)) -> Result<(($($name::Artifact,)*), bool), Box<dyn Error>>
|
|
||||||
{
|
|
||||||
$(let $name = self.add($name)?;)*
|
|
||||||
Ok((($($name.0,)*), false $(|| $name.1)*))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for_each_tuple!(can_handle);
|
|
||||||
|
|
||||||
// This is for self-referential T
|
|
||||||
// FIXME: Wait for specialization
|
|
||||||
impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<Option<T>>
|
|
||||||
for Setup<_SR, _L, _R, _B>
|
|
||||||
where
|
|
||||||
_B: SymbolBuilder<T>,
|
|
||||||
<_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
|
|
||||||
_L: ResourceLocator<T, Prerequisites = Option<T>>,
|
|
||||||
_R: From<T> + BorrowResource<T>,
|
|
||||||
Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>,
|
|
||||||
{
|
|
||||||
fn handle(
|
|
||||||
&mut self,
|
|
||||||
r: Option<T>,
|
|
||||||
) -> Result<(<Option<T> as ToArtifact>::Artifact, bool), Box<dyn Error>> {
|
|
||||||
Ok(match r {
|
|
||||||
Some(r) => {
|
|
||||||
let (result, did_run) = self.add(r)?;
|
|
||||||
(Some(result), did_run)
|
|
||||||
}
|
|
||||||
None => (None, false),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<T> for Setup<_SR, _L, _R, _B>
|
|
||||||
where
|
|
||||||
_B: SymbolBuilder<T>,
|
|
||||||
<_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
|
|
||||||
_L: ResourceLocator<T>,
|
|
||||||
_R: From<T> + Debug + BorrowResource<T>,
|
|
||||||
Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>
|
|
||||||
+ CanHandle<<_L as ResourceLocator<T>>::Prerequisites>,
|
|
||||||
{
|
|
||||||
fn handle(&mut self, r: T) -> Result<(<T as ToArtifact>::Artifact, bool), Box<dyn Error>> {
|
|
||||||
self.add(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Setup<
|
|
||||||
SR,
|
|
||||||
L = DefaultLocator,
|
|
||||||
R = DefaultResources<'static, &'static str>,
|
|
||||||
B = DefaultBuilder,
|
|
||||||
> {
|
|
||||||
symbol_runner: SR,
|
|
||||||
resources: HashSet<R>,
|
|
||||||
phantom: PhantomData<(L, B)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/rust/issues/27336
|
|
||||||
impl<SR> Setup<SR> {
|
|
||||||
pub fn new(symbol_runner: SR) -> Self {
|
|
||||||
Self {
|
|
||||||
symbol_runner,
|
|
||||||
resources: Default::default(),
|
|
||||||
phantom: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SR, L, R: Hash + Eq, B> Setup<SR, L, R, B> {
|
|
||||||
pub fn new_with(symbol_runner: SR) -> Self {
|
|
||||||
Self {
|
|
||||||
symbol_runner,
|
|
||||||
resources: Default::default(),
|
|
||||||
phantom: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Runnable {
|
|
||||||
fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Symbol + Debug> Runnable for S {
|
|
||||||
fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
||||||
runner.run_symbol(self, force)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! runnable_for_tuple {
|
|
||||||
( $($name:ident)* ) => (
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
impl<$($name: Symbol + Debug,)*> Runnable for ($($name,)*) {
|
|
||||||
#[allow(unused)]
|
|
||||||
fn run<_R: SymbolRunner>(&self, runner: &_R, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
||||||
let ($($name,)*) = self;
|
|
||||||
let mut result = false;
|
|
||||||
$(result = runner.run_symbol($name, force || result)? || result;)*
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for_each_tuple!(runnable_for_tuple);
|
|
||||||
|
|
||||||
impl<SR: SymbolRunner, L, Rs: Hash + Eq, B> Setup<SR, L, Rs, B> {
|
|
||||||
pub fn add<R: Resource>(&mut self, resource: R) -> Result<(R::Artifact, bool), Box<dyn Error>>
|
|
||||||
where
|
|
||||||
B: SymbolBuilder<R>,
|
|
||||||
<B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
|
|
||||||
L: ResourceLocator<R>,
|
|
||||||
Rs: From<R> + BorrowResource<R>,
|
|
||||||
Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
|
|
||||||
{
|
|
||||||
self.add_force(resource, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_force<R: Resource>(
|
|
||||||
&mut self,
|
|
||||||
resource: R,
|
|
||||||
force_run: bool,
|
|
||||||
) -> Result<(R::Artifact, bool), Box<dyn Error>>
|
|
||||||
where
|
|
||||||
B: SymbolBuilder<R>,
|
|
||||||
<B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
|
|
||||||
L: ResourceLocator<R>,
|
|
||||||
Rs: From<R> + BorrowResource<R>,
|
|
||||||
Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
|
|
||||||
{
|
|
||||||
let (target, target_prereqs) = L::locate(&resource);
|
|
||||||
let storable_resource = Rs::from(resource);
|
|
||||||
let did_run = if self.resources.get(&storable_resource).is_some() {
|
|
||||||
assert!(
|
|
||||||
!force_run,
|
|
||||||
"Forcing to run an already-added resource is a logical error"
|
|
||||||
);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
let (_, target_prereqs_did_run) = self.handle(target_prereqs)?;
|
|
||||||
let (symbol, prereqs_did_run) =
|
|
||||||
self.get_symbol(storable_resource.borrow_resource().unwrap(), &target)?;
|
|
||||||
self.resources.insert(storable_resource);
|
|
||||||
self.run_symbol(
|
|
||||||
symbol,
|
|
||||||
force_run || target_prereqs_did_run || prereqs_did_run,
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
Ok((target, did_run))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_symbol<R: Resource>(
|
|
||||||
&mut self,
|
|
||||||
resource: &R,
|
|
||||||
target: &R::Artifact,
|
|
||||||
) -> Result<(<B as SymbolBuilder<R>>::Symbol, bool), Box<dyn Error>>
|
|
||||||
where
|
|
||||||
B: SymbolBuilder<R>,
|
|
||||||
Self: CanHandle<B::Prerequisites>,
|
|
||||||
{
|
|
||||||
let (prereqs, prereqs_did_run) = self.handle(B::prerequisites(resource))?;
|
|
||||||
Ok((B::create(resource, target, prereqs), prereqs_did_run))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_symbol<S: Runnable>(&self, symbol: S, force: bool) -> Result<bool, Box<dyn Error>> {
|
|
||||||
symbol.run(&self.symbol_runner, force)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::resources::{BorrowResource, Resource};
|
|
||||||
use crate::schema::SymbolRunner;
|
|
||||||
use crate::symbols::Symbol;
|
|
||||||
use crate::to_artifact::ToArtifact;
|
|
||||||
use crate::{ResourceLocator, Setup, SymbolBuilder};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
struct TestSymbolRunner {
|
|
||||||
count: Rc<RefCell<usize>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SymbolRunner for TestSymbolRunner {
|
|
||||||
fn run_symbol<S: Symbol + Debug>(
|
|
||||||
&self,
|
|
||||||
symbol: &S,
|
|
||||||
force: bool,
|
|
||||||
) -> Result<bool, Box<dyn Error>> {
|
|
||||||
let run = force || !symbol.target_reached()?;
|
|
||||||
if run {
|
|
||||||
*self.count.borrow_mut() += 1;
|
|
||||||
}
|
|
||||||
Ok(run)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
||||||
struct TestResource<T>(&'static str, T);
|
|
||||||
impl<T> Resource for TestResource<T> {
|
|
||||||
type Artifact = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
|
||||||
enum Resources {
|
|
||||||
A(TestResource<&'static str>),
|
|
||||||
B(TestResource<()>),
|
|
||||||
}
|
|
||||||
impl From<TestResource<&'static str>> for Resources {
|
|
||||||
fn from(from: TestResource<&'static str>) -> Self {
|
|
||||||
Self::A(from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<TestResource<()>> for Resources {
|
|
||||||
fn from(from: TestResource<()>) -> Self {
|
|
||||||
Self::B(from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BorrowResource<TestResource<&'static str>> for Resources {
|
|
||||||
fn borrow_resource(&self) -> Option<&TestResource<&'static str>> {
|
|
||||||
match self {
|
|
||||||
Self::A(a) => Some(a),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl BorrowResource<TestResource<()>> for Resources {
|
|
||||||
fn borrow_resource(&self) -> Option<&TestResource<()>> {
|
|
||||||
match self {
|
|
||||||
Self::B(b) => Some(b),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestResourceLocator;
|
|
||||||
impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
|
|
||||||
type Prerequisites = ();
|
|
||||||
fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
|
|
||||||
((), ())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestSymbolBuilder;
|
|
||||||
impl SymbolBuilder<TestResource<&'static str>> for TestSymbolBuilder {
|
|
||||||
type Symbol = TestSymbol;
|
|
||||||
type Prerequisites = TestResource<()>;
|
|
||||||
|
|
||||||
fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
|
|
||||||
TestResource(resource.1, ())
|
|
||||||
}
|
|
||||||
fn create(
|
|
||||||
resource: &TestResource<&'static str>,
|
|
||||||
(): &(),
|
|
||||||
_inputs: <Self::Prerequisites as ToArtifact>::Artifact,
|
|
||||||
) -> Self::Symbol {
|
|
||||||
TestSymbol {
|
|
||||||
reached: resource.0.chars().next().unwrap().is_uppercase(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl SymbolBuilder<TestResource<()>> for TestSymbolBuilder {
|
|
||||||
type Symbol = TestSymbol;
|
|
||||||
type Prerequisites = ();
|
|
||||||
|
|
||||||
fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
|
|
||||||
fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Symbol {
|
|
||||||
TestSymbol {
|
|
||||||
reached: resource.0.chars().next().unwrap().is_uppercase(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TestSymbol {
|
|
||||||
reached: bool,
|
|
||||||
}
|
|
||||||
impl Symbol for TestSymbol {
|
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
|
||||||
Ok(self.reached)
|
|
||||||
}
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_setup() -> (
|
|
||||||
Rc<RefCell<usize>>,
|
|
||||||
Setup<TestSymbolRunner, TestResourceLocator, Resources, TestSymbolBuilder>,
|
|
||||||
) {
|
|
||||||
let count = Rc::new(RefCell::new(0));
|
|
||||||
let runner = TestSymbolRunner {
|
|
||||||
count: Rc::clone(&count),
|
|
||||||
};
|
|
||||||
(count, Setup::new_with(runner))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn correctly_uses_force() {
|
|
||||||
let (count, mut setup) = get_setup();
|
|
||||||
setup.add(TestResource("A", "b")).unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 2);
|
|
||||||
setup.add(TestResource("A", "b")).unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 2);
|
|
||||||
|
|
||||||
let (count, mut setup) = get_setup();
|
|
||||||
setup.add(TestResource("A", "B")).unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn correctly_handles_symbol_tuples() {
|
|
||||||
let (count, setup) = get_setup();
|
|
||||||
setup
|
|
||||||
.run_symbol(
|
|
||||||
(TestSymbol { reached: false }, TestSymbol { reached: false }),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 2);
|
|
||||||
|
|
||||||
let (count, setup) = get_setup();
|
|
||||||
setup
|
|
||||||
.run_symbol(
|
|
||||||
(TestSymbol { reached: true }, TestSymbol { reached: false }),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 1);
|
|
||||||
|
|
||||||
// An unreached symbol forces all further symbols
|
|
||||||
let (count, setup) = get_setup();
|
|
||||||
setup
|
|
||||||
.run_symbol(
|
|
||||||
(TestSymbol { reached: false }, TestSymbol { reached: true }),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 2);
|
|
||||||
|
|
||||||
let (count, setup) = get_setup();
|
|
||||||
setup
|
|
||||||
.run_symbol(
|
|
||||||
(TestSymbol { reached: true }, TestSymbol { reached: true }),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 0);
|
|
||||||
|
|
||||||
let (count, setup) = get_setup();
|
|
||||||
setup
|
|
||||||
.run_symbol(
|
|
||||||
(TestSymbol { reached: true }, TestSymbol { reached: true }),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(*count.borrow(), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
191
src/setup/core.rs
Normal file
191
src/setup/core.rs
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
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_async($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_async(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_async(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
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/setup/mod.rs
Normal file
10
src/setup/mod.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
mod core;
|
||||||
|
mod util;
|
||||||
|
pub use util::{AddResult, AddableResource};
|
||||||
|
mod symbol_runner;
|
||||||
|
pub use symbol_runner::{
|
||||||
|
DrySymbolRunner, InitializingSymbolRunner, ReportingSymbolRunner, SymbolRunner,
|
||||||
|
};
|
||||||
|
mod runnable;
|
||||||
|
mod setup;
|
||||||
|
pub use setup::Setup;
|
||||||
193
src/setup/runnable.rs
Normal file
193
src/setup/runnable.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
use super::SymbolRunner;
|
||||||
|
use crate::loggers::Logger;
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
pub trait Runnable {
|
||||||
|
async fn run<R: SymbolRunner, L: Logger>(
|
||||||
|
&self,
|
||||||
|
runner: &R,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<S> Runnable for S
|
||||||
|
where
|
||||||
|
Self: Symbol + Debug,
|
||||||
|
{
|
||||||
|
async fn run<R: SymbolRunner, L: Logger>(
|
||||||
|
&self,
|
||||||
|
runner: &R,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
runner.run_symbol(self, logger, force).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! runnable_for_tuple {
|
||||||
|
( $($name:ident)* ) => (
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl<$($name: Symbol + Debug,)*> Runnable for ($($name,)*) {
|
||||||
|
#[allow(unused)]
|
||||||
|
async fn run<_R: SymbolRunner, _L: Logger>(&self, runner: &_R, logger: &_L, force: bool) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let ($($name,)*) = self;
|
||||||
|
let mut result = false;
|
||||||
|
$(result = runner.run_symbol($name, logger, force || result).await? || result;)*
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_tuple!(runnable_for_tuple);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Runnable;
|
||||||
|
use crate::async_utils::run;
|
||||||
|
use crate::loggers::{Logger, StoringLogger};
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use crate::SymbolRunner;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DummySymbol<T, E> {
|
||||||
|
_target_reached: RefCell<T>,
|
||||||
|
_execute: RefCell<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<
|
||||||
|
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
||||||
|
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
||||||
|
> Symbol for DummySymbol<T, E>
|
||||||
|
{
|
||||||
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
|
self._target_reached.borrow_mut().next().unwrap()
|
||||||
|
}
|
||||||
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self._execute.borrow_mut().next().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
||||||
|
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
||||||
|
> DummySymbol<T, E>
|
||||||
|
{
|
||||||
|
fn new<
|
||||||
|
IE: IntoIterator<IntoIter = E, Item = Result<(), Box<dyn Error>>>,
|
||||||
|
IT: IntoIterator<IntoIter = T, Item = Result<bool, Box<dyn Error>>>,
|
||||||
|
>(
|
||||||
|
target_reached: IT,
|
||||||
|
execute: IE,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
_target_reached: RefCell::new(target_reached.into_iter()),
|
||||||
|
_execute: RefCell::new(execute.into_iter()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestSymbolRunner {
|
||||||
|
count: Rc<RefCell<usize>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_runner() -> (Rc<RefCell<usize>>, TestSymbolRunner) {
|
||||||
|
let count = Rc::new(RefCell::new(0));
|
||||||
|
let runner = TestSymbolRunner {
|
||||||
|
count: Rc::clone(&count),
|
||||||
|
};
|
||||||
|
(count, runner)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl SymbolRunner for TestSymbolRunner {
|
||||||
|
async fn run_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
|
&self,
|
||||||
|
symbol: &S,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let run = force || !symbol.target_reached().await?;
|
||||||
|
if run {
|
||||||
|
*self.count.borrow_mut() += 1;
|
||||||
|
}
|
||||||
|
Ok(run)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_symbol(
|
||||||
|
runnable: impl Runnable,
|
||||||
|
force: bool,
|
||||||
|
) -> (Rc<RefCell<usize>>, Result<bool, Box<dyn Error>>) {
|
||||||
|
let (count, runner) = get_runner();
|
||||||
|
let res = run(runnable.run(&runner, &StoringLogger::new(), force));
|
||||||
|
(count, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn correctly_handles_symbol_tuples() {
|
||||||
|
let (count, res) = run_symbol(
|
||||||
|
(
|
||||||
|
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
|
||||||
|
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
res.unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 2);
|
||||||
|
|
||||||
|
let (count, res) = run_symbol(
|
||||||
|
(
|
||||||
|
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
|
||||||
|
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
res.unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 1);
|
||||||
|
|
||||||
|
// An unreached symbol forces all further symbols
|
||||||
|
let (count, res) = run_symbol(
|
||||||
|
(
|
||||||
|
DummySymbol::new(vec![Ok(false)], vec![Ok(())]),
|
||||||
|
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
res.unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 2);
|
||||||
|
|
||||||
|
let (count, res) = run_symbol(
|
||||||
|
(
|
||||||
|
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
|
||||||
|
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
res.unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 0);
|
||||||
|
|
||||||
|
let (count, res) = run_symbol(
|
||||||
|
(
|
||||||
|
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
|
||||||
|
DummySymbol::new(vec![Ok(true)], vec![Ok(())]),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
res.unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
300
src/setup/setup.rs
Normal file
300
src/setup/setup.rs
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
use super::core::{RegularSetupCore, SetupCore};
|
||||||
|
use super::runnable::Runnable;
|
||||||
|
use super::util::{AddResult, AddableResource};
|
||||||
|
use super::SymbolRunner;
|
||||||
|
use crate::async_utils::run;
|
||||||
|
use crate::loggers::Logger;
|
||||||
|
use crate::resources::{DefaultArtifacts, DefaultResources, FromArtifact, FromResource};
|
||||||
|
use crate::{DefaultBuilder, DefaultLocator};
|
||||||
|
use futures::future::FutureExt;
|
||||||
|
use futures::future::Shared;
|
||||||
|
use std::cell::{RefCell, RefMut};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
type Cache<Rs, As> = HashMap<Rs, Shared<Pin<Box<dyn Future<Output = (As, bool)>>>>>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SetupInner<CORE, LOG, Rs, As> {
|
||||||
|
core: CORE,
|
||||||
|
logger: LOG,
|
||||||
|
resources: RefCell<Cache<Rs, As>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Setup<
|
||||||
|
SR,
|
||||||
|
LOG,
|
||||||
|
L = DefaultLocator,
|
||||||
|
B = DefaultBuilder,
|
||||||
|
Rs = DefaultResources<'static, &'static str>,
|
||||||
|
As = DefaultArtifacts<'static, &'static str>,
|
||||||
|
>(Rc<SetupInner<RegularSetupCore<SR, L, B>, LOG, Rs, As>>);
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/rust/issues/27336
|
||||||
|
impl<SR, LOG> Setup<SR, LOG> {
|
||||||
|
pub fn new(symbol_runner: SR, logger: LOG) -> Self {
|
||||||
|
Self::new_with(symbol_runner, logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, B, As, SR, LOG, Rs: Hash + Eq> Setup<SR, LOG, L, B, Rs, As> {
|
||||||
|
pub fn new_with(symbol_runner: SR, logger: LOG) -> Self {
|
||||||
|
Self(Rc::new(SetupInner {
|
||||||
|
core: RegularSetupCore::new(symbol_runner),
|
||||||
|
logger,
|
||||||
|
resources: RefCell::default(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
L: 'static,
|
||||||
|
B: 'static,
|
||||||
|
SR: 'static,
|
||||||
|
LOG: 'static + Logger,
|
||||||
|
Rs: Hash + Eq + 'static,
|
||||||
|
As: 'static,
|
||||||
|
> Setup<SR, LOG, L, B, Rs, As>
|
||||||
|
{
|
||||||
|
fn borrow_resources(&self) -> RefMut<'_, Cache<Rs, As>> {
|
||||||
|
self.0.resources.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn add_async<R: AddableResource>(
|
||||||
|
&self,
|
||||||
|
resource: R,
|
||||||
|
force_run: bool,
|
||||||
|
) -> AddResult<R>
|
||||||
|
where
|
||||||
|
Rs: FromResource<R>,
|
||||||
|
As: FromArtifact<R> + Clone,
|
||||||
|
R::Artifact: Clone,
|
||||||
|
RegularSetupCore<SR, L, B>: SetupCore<R, Self>,
|
||||||
|
{
|
||||||
|
let (storable_resource, weak_resource) = Rs::from_resource(resource);
|
||||||
|
let mut resources = self.borrow_resources();
|
||||||
|
if let Some(future) = resources.remove(&storable_resource) {
|
||||||
|
assert!(
|
||||||
|
!force_run,
|
||||||
|
"Forcing to run an already-added resource is a logical error"
|
||||||
|
);
|
||||||
|
resources.insert(storable_resource, future.clone());
|
||||||
|
drop(resources);
|
||||||
|
Ok(future.await)
|
||||||
|
} else {
|
||||||
|
let inner_weak = Rc::downgrade(&self.0);
|
||||||
|
let future = Box::pin(async move {
|
||||||
|
let this = Self(inner_weak.upgrade().expect("Dangling!"));
|
||||||
|
let resource = weak_resource.upgrade().expect("Dangling!");
|
||||||
|
// Need to convert Box<Error> to String for Clone for Shared
|
||||||
|
this
|
||||||
|
.0
|
||||||
|
.core
|
||||||
|
.add(&this, &this.0.logger, resource, force_run)
|
||||||
|
.await
|
||||||
|
.map(|(t, did_run)| (As::from_artifact(t), did_run))
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
.shared();
|
||||||
|
let future_clone = future.clone();
|
||||||
|
resources.insert(
|
||||||
|
storable_resource,
|
||||||
|
(Box::pin(async move { future_clone.await.unwrap() })
|
||||||
|
as Pin<Box<dyn Future<Output = (As, bool)>>>)
|
||||||
|
.shared(),
|
||||||
|
);
|
||||||
|
drop(resources);
|
||||||
|
future.await.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
.map(|(t, did_run)| (t.into_artifact(), did_run))
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Legacy
|
||||||
|
//
|
||||||
|
pub fn add<R: AddableResource>(&self, resource: R) -> AddResult<R>
|
||||||
|
where
|
||||||
|
RegularSetupCore<SR, L, B>: SetupCore<R, Self>,
|
||||||
|
Rs: FromResource<R>,
|
||||||
|
As: FromArtifact<R> + Clone,
|
||||||
|
R::Artifact: Clone,
|
||||||
|
{
|
||||||
|
run(self.add_async(resource, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_force<R: AddableResource>(&self, resource: R, force_run: bool) -> AddResult<R>
|
||||||
|
where
|
||||||
|
RegularSetupCore<SR, L, B>: SetupCore<R, Self>,
|
||||||
|
Rs: FromResource<R>,
|
||||||
|
As: FromArtifact<R> + Clone,
|
||||||
|
R::Artifact: Clone,
|
||||||
|
{
|
||||||
|
run(self.add_async(resource, force_run))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_symbol<S: Runnable>(&self, symbol: S, force: bool) -> Result<bool, Box<dyn Error>>
|
||||||
|
where
|
||||||
|
RegularSetupCore<SR, L, B>: SymbolRunner,
|
||||||
|
{
|
||||||
|
run(symbol.run(&self.0.core, &self.0.logger, force))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::SymbolRunner;
|
||||||
|
use crate::loggers::{Logger, StoringLogger};
|
||||||
|
use crate::resources::{FromArtifact, FromResource, Resource};
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use crate::to_artifact::ToArtifact;
|
||||||
|
use crate::{ImplementationBuilder, ResourceLocator, Setup};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
|
struct TestSymbolRunner {
|
||||||
|
count: Rc<RefCell<usize>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl SymbolRunner for TestSymbolRunner {
|
||||||
|
async fn run_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
|
&self,
|
||||||
|
symbol: &S,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let run = force || !symbol.target_reached().await?;
|
||||||
|
if run {
|
||||||
|
*self.count.borrow_mut() += 1;
|
||||||
|
}
|
||||||
|
Ok(run)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
struct TestResource<T>(&'static str, T);
|
||||||
|
impl<T> Resource for TestResource<T> {
|
||||||
|
type Artifact = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum Resources {
|
||||||
|
A(Rc<TestResource<&'static str>>),
|
||||||
|
B(Rc<TestResource<()>>),
|
||||||
|
}
|
||||||
|
impl FromResource<TestResource<&'static str>> for Resources {
|
||||||
|
fn from_resource(from: TestResource<&'static str>) -> (Self, Weak<TestResource<&'static str>>) {
|
||||||
|
let inner = Rc::new(from);
|
||||||
|
(Self::A(Rc::clone(&inner)), Rc::downgrade(&inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromResource<TestResource<()>> for Resources {
|
||||||
|
fn from_resource(from: TestResource<()>) -> (Self, Weak<TestResource<()>>) {
|
||||||
|
let inner = Rc::new(from);
|
||||||
|
(Self::B(Rc::clone(&inner)), Rc::downgrade(&inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Artifacts;
|
||||||
|
impl<V> FromArtifact<TestResource<V>> for Artifacts {
|
||||||
|
fn from_artifact(from: ()) -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
fn into_artifact(self) -> () {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestResourceLocator;
|
||||||
|
impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
|
||||||
|
type Prerequisites = ();
|
||||||
|
fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
|
||||||
|
((), ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestImplementationBuilder;
|
||||||
|
impl ImplementationBuilder<TestResource<&'static str>> for TestImplementationBuilder {
|
||||||
|
type Implementation = TestSymbol;
|
||||||
|
type Prerequisites = TestResource<()>;
|
||||||
|
|
||||||
|
fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
|
||||||
|
TestResource(resource.1, ())
|
||||||
|
}
|
||||||
|
fn create(
|
||||||
|
resource: &TestResource<&'static str>,
|
||||||
|
(): &(),
|
||||||
|
_inputs: <Self::Prerequisites as ToArtifact>::Artifact,
|
||||||
|
) -> Self::Implementation {
|
||||||
|
TestSymbol {
|
||||||
|
reached: resource.0.chars().next().unwrap().is_uppercase(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ImplementationBuilder<TestResource<()>> for TestImplementationBuilder {
|
||||||
|
type Implementation = TestSymbol;
|
||||||
|
type Prerequisites = ();
|
||||||
|
|
||||||
|
fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
|
||||||
|
fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Implementation {
|
||||||
|
TestSymbol {
|
||||||
|
reached: resource.0.chars().next().unwrap().is_uppercase(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TestSymbol {
|
||||||
|
reached: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl Symbol for TestSymbol {
|
||||||
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
|
Ok(self.reached)
|
||||||
|
}
|
||||||
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_setup() -> (
|
||||||
|
Rc<RefCell<usize>>,
|
||||||
|
Setup<
|
||||||
|
TestSymbolRunner,
|
||||||
|
StoringLogger,
|
||||||
|
TestResourceLocator,
|
||||||
|
TestImplementationBuilder,
|
||||||
|
Resources,
|
||||||
|
Artifacts,
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
let count = Rc::new(RefCell::new(0));
|
||||||
|
let runner = TestSymbolRunner {
|
||||||
|
count: Rc::clone(&count),
|
||||||
|
};
|
||||||
|
(count, Setup::new_with(runner, StoringLogger::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn correctly_uses_force() {
|
||||||
|
let (count, setup) = get_setup();
|
||||||
|
setup.add(TestResource("A", "b")).unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 2);
|
||||||
|
setup.add(TestResource("A", "b")).unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 2);
|
||||||
|
|
||||||
|
let (count, setup) = get_setup();
|
||||||
|
setup.add(TestResource("A", "B")).unwrap();
|
||||||
|
assert_eq!(*count.borrow(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
318
src/setup/symbol_runner.rs
Normal file
318
src/setup/symbol_runner.rs
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
use crate::loggers::Logger;
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
pub trait SymbolRunner {
|
||||||
|
async fn run_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
|
&self,
|
||||||
|
symbol: &S,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SymbolRunError {
|
||||||
|
Symbol(Box<dyn Error>),
|
||||||
|
ExecuteDidNotReach(()),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for SymbolRunError {
|
||||||
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
|
match self {
|
||||||
|
Self::Symbol(ref e) => Some(&**e),
|
||||||
|
Self::ExecuteDidNotReach(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SymbolRunError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Symbol(ref e) => write!(f, "{}", e),
|
||||||
|
Self::ExecuteDidNotReach(_) => write!(f, "Target not reached after executing symbol"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct InitializingSymbolRunner;
|
||||||
|
|
||||||
|
impl InitializingSymbolRunner {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
|
&self,
|
||||||
|
symbol: &S,
|
||||||
|
logger: &L,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
logger.info(format!("Executing {:?}", symbol));
|
||||||
|
symbol.execute().await?;
|
||||||
|
let target_reached = symbol.target_reached().await?;
|
||||||
|
logger.trace(format!(
|
||||||
|
"Symbol reports target_reached: {:?} (should be true)",
|
||||||
|
target_reached
|
||||||
|
));
|
||||||
|
if target_reached {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Box::new(SymbolRunError::ExecuteDidNotReach(())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl SymbolRunner for InitializingSymbolRunner {
|
||||||
|
async fn run_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
|
&self,
|
||||||
|
symbol: &S,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let executed = if force {
|
||||||
|
logger.debug("Forcing symbol execution");
|
||||||
|
self.exec_symbol(symbol, logger).await?;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let target_reached = symbol.target_reached().await?;
|
||||||
|
if target_reached {
|
||||||
|
logger.debug(format!("{:?} already reached", symbol));
|
||||||
|
} else {
|
||||||
|
logger.trace(format!(
|
||||||
|
"Symbol reports target_reached: {:?}",
|
||||||
|
target_reached
|
||||||
|
));
|
||||||
|
self.exec_symbol(symbol, logger).await?;
|
||||||
|
}
|
||||||
|
!target_reached
|
||||||
|
};
|
||||||
|
Ok(executed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct DrySymbolRunner;
|
||||||
|
|
||||||
|
impl DrySymbolRunner {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl SymbolRunner for DrySymbolRunner {
|
||||||
|
async fn run_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
|
&self,
|
||||||
|
symbol: &S,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let would_execute = if force {
|
||||||
|
logger.info(format!("Would force-execute {:?}", symbol));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let target_reached = symbol.target_reached().await?;
|
||||||
|
logger.debug(format!(
|
||||||
|
"Symbol reports target_reached: {:?}",
|
||||||
|
target_reached
|
||||||
|
));
|
||||||
|
if !target_reached {
|
||||||
|
logger.info(format!("Would execute {:?}", symbol));
|
||||||
|
}
|
||||||
|
!target_reached
|
||||||
|
};
|
||||||
|
Ok(would_execute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ReportingSymbolRunner<R>(R);
|
||||||
|
|
||||||
|
impl<R> ReportingSymbolRunner<R> {
|
||||||
|
pub fn new(symbol_runner: R) -> Self {
|
||||||
|
Self(symbol_runner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<R> SymbolRunner for ReportingSymbolRunner<R>
|
||||||
|
where
|
||||||
|
R: SymbolRunner,
|
||||||
|
{
|
||||||
|
async fn run_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
|
&self,
|
||||||
|
symbol: &S,
|
||||||
|
logger: &L,
|
||||||
|
force: bool,
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
logger.debug(format!("Running symbol {:?}", symbol));
|
||||||
|
let res = self.0.run_symbol(symbol, logger, force).await;
|
||||||
|
if let Err(ref e) = res {
|
||||||
|
logger.info(format!("Failed on {:?} with {}, aborting.", symbol, e))
|
||||||
|
} else {
|
||||||
|
logger.debug(format!("Successfully finished {:?}", symbol))
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{DrySymbolRunner, InitializingSymbolRunner, ReportingSymbolRunner, SymbolRunner};
|
||||||
|
use crate::async_utils::sleep;
|
||||||
|
use crate::async_utils::{run, try_join};
|
||||||
|
use crate::loggers::StoringLogger;
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
enum DummySymbolError {
|
||||||
|
Error(()),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for DummySymbolError {}
|
||||||
|
|
||||||
|
impl fmt::Display for DummySymbolError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Dummy symbol error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DummySymbol<T, E> {
|
||||||
|
_target_reached: RefCell<T>,
|
||||||
|
_execute: RefCell<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<
|
||||||
|
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
||||||
|
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
||||||
|
> Symbol for DummySymbol<T, E>
|
||||||
|
{
|
||||||
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
|
self._target_reached.borrow_mut().next().unwrap()
|
||||||
|
}
|
||||||
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self._execute.borrow_mut().next().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
E: Iterator<Item = Result<(), Box<dyn Error>>>,
|
||||||
|
T: Iterator<Item = Result<bool, Box<dyn Error>>>,
|
||||||
|
> DummySymbol<T, E>
|
||||||
|
{
|
||||||
|
fn new<
|
||||||
|
IE: IntoIterator<IntoIter = E, Item = Result<(), Box<dyn Error>>>,
|
||||||
|
IT: IntoIterator<IntoIter = T, Item = Result<bool, Box<dyn Error>>>,
|
||||||
|
>(
|
||||||
|
target_reached: IT,
|
||||||
|
execute: IE,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
_target_reached: RefCell::new(target_reached.into_iter()),
|
||||||
|
_execute: RefCell::new(execute.into_iter()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_symbol<S: Symbol + Debug>(s: S) -> Result<bool, Box<dyn Error>> {
|
||||||
|
run(InitializingSymbolRunner::new().run_symbol(&s, &StoringLogger::new(), false))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nothing_needed_to_be_done() {
|
||||||
|
let result = run_symbol(DummySymbol::new(vec![Ok(true)], vec![Ok(())]));
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn everything_is_ok() {
|
||||||
|
let result = run_symbol(DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]));
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn executing_did_not_change_state() {
|
||||||
|
let result = run_symbol(DummySymbol::new(vec![Ok(false), Ok(false)], vec![Ok(())]));
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().to_string(),
|
||||||
|
"Target not reached after executing symbol"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn executing_did_not_work() {
|
||||||
|
let result = run_symbol(DummySymbol::new(
|
||||||
|
vec![Ok(false)],
|
||||||
|
vec![Err(Box::new(DummySymbolError::Error(())) as Box<dyn Error>)],
|
||||||
|
));
|
||||||
|
assert_eq!(result.unwrap_err().to_string(), "Dummy symbol error");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SleeperSymbol;
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl Symbol for SleeperSymbol {
|
||||||
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
|
sleep(Duration::from_millis(0)).await;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn actually_support_parallel_execution() {
|
||||||
|
run(async {
|
||||||
|
let s1 = SleeperSymbol;
|
||||||
|
let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
|
||||||
|
|
||||||
|
let l1 = StoringLogger::new();
|
||||||
|
let l2 = StoringLogger::new();
|
||||||
|
let runner1 = InitializingSymbolRunner::new();
|
||||||
|
let result = try_join!(
|
||||||
|
runner1.run_symbol(&s1, &l1, false),
|
||||||
|
runner1.run_symbol(&s2, &l2, false),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result, (false, true));
|
||||||
|
|
||||||
|
let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
|
||||||
|
let l1 = StoringLogger::new();
|
||||||
|
let l2 = StoringLogger::new();
|
||||||
|
let runner2 = DrySymbolRunner::new();
|
||||||
|
let result = try_join!(
|
||||||
|
runner2.run_symbol(&s1, &l1, false),
|
||||||
|
runner2.run_symbol(&s2, &l2, false),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result, (false, true));
|
||||||
|
|
||||||
|
let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
|
||||||
|
let l1 = StoringLogger::new();
|
||||||
|
let l2 = StoringLogger::new();
|
||||||
|
let runner3 = ReportingSymbolRunner::new(runner1);
|
||||||
|
let result = try_join!(
|
||||||
|
runner3.run_symbol(&s1, &l1, false),
|
||||||
|
runner3.run_symbol(&s2, &l2, false),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result, (false, true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/setup/util.rs
Normal file
9
src/setup/util.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::resources::Resource;
|
||||||
|
use crate::to_artifact::ToArtifact;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
pub trait AddableResource: 'static + Resource + Debug {}
|
||||||
|
impl<R> AddableResource for R where R: 'static + Resource + Debug {}
|
||||||
|
|
||||||
|
pub type AddResult<R> = Result<(<R as ToArtifact>::Artifact, bool), Box<dyn Error>>;
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::File as FsFile;
|
use std::fs::File as FsFile;
|
||||||
|
|
@ -44,13 +45,17 @@ impl<_C, C, D, P> Cert<_C, C, D, P> {
|
||||||
|
|
||||||
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
|
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for Cert<_C, C, D, P> {
|
impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for Cert<_C, C, D, P> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.cert_path.as_ref().exists() {
|
if !self.cert_path.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = self.command_runner.borrow().run_with_args(
|
let output = self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.run_with_args(
|
||||||
"openssl",
|
"openssl",
|
||||||
args![
|
args![
|
||||||
"x509",
|
"x509",
|
||||||
|
|
@ -61,7 +66,8 @@ impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for
|
||||||
"-checkend",
|
"-checkend",
|
||||||
(30 * DAYS_IN_SECONDS).to_string(),
|
(30 * DAYS_IN_SECONDS).to_string(),
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
if output.status.success()
|
if output.status.success()
|
||||||
&& output.stdout
|
&& output.stdout
|
||||||
== format!(
|
== format!(
|
||||||
|
|
@ -83,6 +89,7 @@ impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for
|
||||||
self.cert_path.as_ref(),
|
self.cert_path.as_ref(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
.is_ok(),
|
.is_ok(),
|
||||||
)
|
)
|
||||||
} else if output.status.code() == Some(1)
|
} else if output.status.code() == Some(1)
|
||||||
|
|
@ -99,8 +106,11 @@ impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
let output = self.command_runner.borrow().get_output(
|
let output = self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.get_output(
|
||||||
"acme-tiny",
|
"acme-tiny",
|
||||||
args![
|
args![
|
||||||
"--account-key",
|
"--account-key",
|
||||||
|
|
@ -110,7 +120,8 @@ impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for
|
||||||
"--acme-dir",
|
"--acme-dir",
|
||||||
self.challenges_path.as_ref(),
|
self.challenges_path.as_ref(),
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
let mut file = FsFile::create(self.cert_path.as_ref())?;
|
let mut file = FsFile::create(self.cert_path.as_ref())?;
|
||||||
file.write_all(&output)?;
|
file.write_all(&output)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::{metadata, File};
|
use std::fs::{metadata, File};
|
||||||
use std::io::copy;
|
use std::io::copy;
|
||||||
|
|
@ -22,8 +23,9 @@ impl<S, D, I> Concat<S, D, I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<S: AsRef<[I]>, D: AsRef<Path>, I: AsRef<Path>> Symbol for Concat<S, D, I> {
|
impl<S: AsRef<[I]>, D: AsRef<Path>, I: AsRef<Path>> Symbol for Concat<S, D, I> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let target = self.target.as_ref();
|
let target = self.target.as_ref();
|
||||||
if !target.exists() {
|
if !target.exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|
@ -37,7 +39,7 @@ impl<S: AsRef<[I]>, D: AsRef<Path>, I: AsRef<Path>> Symbol for Concat<S, D, I> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
let mut file = File::create(self.target.as_ref())?;
|
let mut file = File::create(self.target.as_ref())?;
|
||||||
for source in self.sources.as_ref() {
|
for source in self.sources.as_ref() {
|
||||||
copy(&mut File::open(source)?, &mut file)?;
|
copy(&mut File::open(source)?, &mut file)?;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::{is_success, CommandRunner};
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -19,23 +20,27 @@ impl<'r, U, R> Cron<'r, String, U, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<C: AsRef<str>, U: AsRef<str>, R: CommandRunner> Symbol for Cron<'_, C, U, R> {
|
impl<C: AsRef<str>, U: AsRef<str>, R: CommandRunner> Symbol for Cron<'_, C, U, R> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let tab = self
|
let tab = self
|
||||||
.command_runner
|
.command_runner
|
||||||
.get_output("crontab", args!["-l", "-u", self.user.as_ref()])?;
|
.get_output("crontab", args!["-l", "-u", self.user.as_ref()])
|
||||||
|
.await?;
|
||||||
Ok(tab == self.content.as_ref().as_bytes())
|
Ok(tab == self.content.as_ref().as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
let output = self.command_runner.run_with_args_and_stdin(
|
is_success(
|
||||||
|
self
|
||||||
|
.command_runner
|
||||||
|
.run(
|
||||||
"crontab",
|
"crontab",
|
||||||
args!["-u", self.user.as_ref(), "-",],
|
args!["-u", self.user.as_ref(), "-",],
|
||||||
self.content.as_ref(),
|
self.content.as_ref(),
|
||||||
|
)
|
||||||
|
.await,
|
||||||
)?;
|
)?;
|
||||||
if !output.status.success() {
|
|
||||||
return Err(String::from_utf8(output.stderr)?.into());
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
@ -15,8 +16,9 @@ impl<P> Dir<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<P: AsRef<Path>> Symbol for Dir<P> {
|
impl<P: AsRef<Path>> Symbol for Dir<P> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.path.as_ref().exists() {
|
if !self.path.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +32,7 @@ impl<P: AsRef<Path>> Symbol for Dir<P> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
fs::create_dir(self.path.as_ref()).map_err(|e| Box::new(e) as Box<dyn Error>)
|
fs::create_dir(self.path.as_ref()).map_err(|e| Box::new(e) as Box<dyn Error>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::File as FsFile;
|
use std::fs::File as FsFile;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
@ -16,8 +17,9 @@ impl<D, C> File<D, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<D: AsRef<Path>, C: AsRef<str>> Symbol for File<D, C> {
|
impl<D: AsRef<Path>, C: AsRef<str>> Symbol for File<D, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.path.as_ref().exists() {
|
if !self.path.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +37,7 @@ impl<D: AsRef<Path>, C: AsRef<str>> Symbol for File<D, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
let mut file = FsFile::create(self.path.as_ref())?;
|
let mut file = FsFile::create(self.path.as_ref())?;
|
||||||
file.write_all(self.content.as_ref().as_bytes())?;
|
file.write_all(self.content.as_ref().as_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
use crate::async_utils::try_join;
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
@ -28,30 +30,44 @@ impl<C, _C, P, S, B> Checkout<_C, C, P, S, B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: CommandRunner, _C: Borrow<C>, P: AsRef<Path>, S, B> Checkout<C, _C, P, S, B> {
|
impl<C: CommandRunner, _C: Borrow<C>, P: AsRef<Path>, S, B> Checkout<C, _C, P, S, B> {
|
||||||
fn _run_in_target_repo(&self, args: &[impl AsRef<OsStr>]) -> Result<Vec<u8>, Box<dyn Error>> {
|
async fn run_git(&self, args: &[impl AsRef<OsStr>]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let mut new_args = vec![OsStr::new("-C"), self.target.as_ref().as_ref()];
|
let mut new_args = Vec::with_capacity(args.len() + 2);
|
||||||
|
new_args.extend_from_slice(args!["-C", self.target.as_ref()]);
|
||||||
new_args.extend(args.iter().map(AsRef::as_ref));
|
new_args.extend(args.iter().map(AsRef::as_ref));
|
||||||
self.command_runner.borrow().get_output("git", &new_args)
|
self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.get_output("git", &new_args)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<C: CommandRunner, _C: Borrow<C>, P: AsRef<Path>, S: AsRef<str>, B: AsRef<str>> Symbol
|
impl<C: CommandRunner, _C: Borrow<C>, P: AsRef<Path>, S: AsRef<str>, B: AsRef<str>> Symbol
|
||||||
for Checkout<C, _C, P, S, B>
|
for Checkout<C, _C, P, S, B>
|
||||||
{
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.target.as_ref().exists() {
|
if !self.target.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
self._run_in_target_repo(&["fetch", self.source.as_ref(), self.branch.as_ref()])?;
|
let fetch_head_f = async {
|
||||||
|
self
|
||||||
|
.run_git(args!["fetch", self.source, self.branch])
|
||||||
|
.await?;
|
||||||
// git rev-list resolves tag objects
|
// git rev-list resolves tag objects
|
||||||
let fetch_head = self._run_in_target_repo(&["rev-list", "-1", "FETCH_HEAD"])?;
|
self.run_git(&["rev-list", "-1", "FETCH_HEAD"]).await
|
||||||
let head = self._run_in_target_repo(&["rev-list", "-1", "HEAD"])?;
|
};
|
||||||
|
let head_f = self.run_git(&["rev-list", "-1", "HEAD"]);
|
||||||
|
let (fetch_head, head) = try_join!(fetch_head_f, head_f)?;
|
||||||
Ok(fetch_head == head)
|
Ok(fetch_head == head)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
if !self.target.as_ref().exists() {
|
if !self.target.as_ref().exists() {
|
||||||
return self.command_runner.borrow().run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.run_successfully(
|
||||||
"git",
|
"git",
|
||||||
args![
|
args![
|
||||||
"clone",
|
"clone",
|
||||||
|
|
@ -62,13 +78,73 @@ impl<C: CommandRunner, _C: Borrow<C>, P: AsRef<Path>, S: AsRef<str>, B: AsRef<st
|
||||||
self.source.as_ref(),
|
self.source.as_ref(),
|
||||||
self.target.as_ref(),
|
self.target.as_ref(),
|
||||||
],
|
],
|
||||||
);
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
.run_git(&["fetch", self.source.as_ref(), self.branch.as_ref()])
|
||||||
|
.await?;
|
||||||
|
self.run_git(&["merge", "FETCH_HEAD"]).await?;
|
||||||
}
|
}
|
||||||
self._run_in_target_repo(&["fetch", self.source.as_ref(), self.branch.as_ref()])?;
|
|
||||||
self._run_in_target_repo(&["merge", "FETCH_HEAD"])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {}
|
mod test {
|
||||||
|
use super::Checkout;
|
||||||
|
use crate::async_utils::run;
|
||||||
|
use crate::async_utils::sleep;
|
||||||
|
use crate::command_runner::CommandRunner;
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
|
use std::io::Result as IoResult;
|
||||||
|
use std::os::unix::process::ExitStatusExt;
|
||||||
|
use std::process::{ExitStatus, Output};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
struct DummyCommandRunner {
|
||||||
|
pub args: RefCell<Vec<Vec<OsString>>>,
|
||||||
|
}
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl CommandRunner for DummyCommandRunner {
|
||||||
|
async fn run(&self, program: &str, args: &[&OsStr], stdin: &str) -> IoResult<Output> {
|
||||||
|
assert_eq!(program, "git");
|
||||||
|
assert_eq!(stdin, "");
|
||||||
|
sleep(Duration::from_millis(50)).await;
|
||||||
|
self
|
||||||
|
.args
|
||||||
|
.borrow_mut()
|
||||||
|
.push(args.iter().map(|a| a.to_os_string()).collect());
|
||||||
|
Ok(Output {
|
||||||
|
status: ExitStatus::from_raw(0),
|
||||||
|
stdout: vec![],
|
||||||
|
stderr: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let c = DummyCommandRunner {
|
||||||
|
args: RefCell::new(vec![]),
|
||||||
|
};
|
||||||
|
let checkout: Checkout<DummyCommandRunner, _, _, _, _> =
|
||||||
|
Checkout::new("target", "source", "branch", &c);
|
||||||
|
let start = Instant::now();
|
||||||
|
assert!(run(checkout.target_reached()).unwrap());
|
||||||
|
let end = Instant::now();
|
||||||
|
assert_eq!(
|
||||||
|
c.args.into_inner(),
|
||||||
|
[
|
||||||
|
["-C", "target", "fetch", "source", "branch"],
|
||||||
|
["-C", "target", "rev-list", "-1", "HEAD"],
|
||||||
|
["-C", "target", "rev-list", "-1", "FETCH_HEAD"]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert!((end - start).as_millis() >= 100);
|
||||||
|
assert!((end - start).as_millis() < 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
mod checkout;
|
mod checkout;
|
||||||
//pub mod submodules;
|
pub mod submodules;
|
||||||
|
|
||||||
pub use checkout::Checkout;
|
pub use checkout::Checkout;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
|
use crate::command_runner::CommandRunner;
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::command_runner::CommandRunner;
|
#[derive(Debug)]
|
||||||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
pub struct GitSubmodules<'a, P, C> {
|
||||||
|
|
||||||
pub struct GitSubmodules<'a, P: AsRef<Path>, C: CommandRunner> {
|
|
||||||
target: P,
|
target: P,
|
||||||
command_runner: &'a C,
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, P: AsRef<Path>, C: CommandRunner> GitSubmodules<'a, P, C> {
|
impl<'a, P, C> GitSubmodules<'a, P, C> {
|
||||||
pub fn new(target: P, command_runner: &'a C) -> Self {
|
pub fn new(target: P, command_runner: &'a C) -> Self {
|
||||||
GitSubmodules {
|
Self {
|
||||||
target,
|
target,
|
||||||
command_runner,
|
command_runner,
|
||||||
}
|
}
|
||||||
|
|
@ -27,20 +28,25 @@ impl<P: AsRef<Path>, C: CommandRunner> fmt::Display for GitSubmodules<'_, P, C>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: AsRef<Path>, C: CommandRunner> GitSubmodules<'_, P, C> {
|
impl<P: AsRef<Path>, C: CommandRunner> GitSubmodules<'_, P, C> {
|
||||||
fn _run_in_target_repo(&self, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
async fn _run_in_target_repo(&self, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let mut new_args: Vec<&OsStr> = vec![];
|
let mut new_args: Vec<&OsStr> = vec![];
|
||||||
new_args.extend_from_slice(args!["-C", self.target.as_ref()]);
|
new_args.extend_from_slice(args!["-C", self.target.as_ref()]);
|
||||||
new_args.extend_from_slice(args);
|
new_args.extend_from_slice(args);
|
||||||
self.command_runner.get_output("git", &new_args)
|
self.command_runner.get_output("git", &new_args).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<P: AsRef<Path>, C: CommandRunner> Symbol for GitSubmodules<'_, P, C> {
|
impl<P: AsRef<Path>, C: CommandRunner> Symbol for GitSubmodules<'_, P, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.target.as_ref().exists() {
|
if !self.target.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let output = String::from_utf8(self._run_in_target_repo(args!["submodule", "status"])?)?;
|
let output = String::from_utf8(
|
||||||
|
self
|
||||||
|
._run_in_target_repo(args!["submodule", "status"])
|
||||||
|
.await?,
|
||||||
|
)?;
|
||||||
Ok(
|
Ok(
|
||||||
output
|
output
|
||||||
.lines()
|
.lines()
|
||||||
|
|
@ -48,21 +54,12 @@ impl<P: AsRef<Path>, C: CommandRunner> Symbol for GitSubmodules<'_, P, C> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self._run_in_target_repo(args!["submodule", "update", "--init"])?;
|
self
|
||||||
|
._run_in_target_repo(args!["submodule", "update", "--init"])
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
|
||||||
Box::new(SymbolAction::new(runner, self))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
|
|
||||||
where
|
|
||||||
Self: 'b,
|
|
||||||
{
|
|
||||||
Box::new(OwnedSymbolAction::new(runner, *self))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -19,27 +20,34 @@ impl<'a, D, S, C: CommandRunner> Database<'a, D, S, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
async fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = self
|
let b = self
|
||||||
.command_runner
|
.command_runner
|
||||||
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
|
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])
|
||||||
|
.await?;
|
||||||
Ok(String::from_utf8(b)?)
|
Ok(String::from_utf8(b)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<D: AsRef<str>, S: Storage, C: CommandRunner> Symbol for Database<'_, D, S, C> {
|
impl<D: AsRef<str>, S: Storage, C: CommandRunner> Symbol for Database<'_, D, S, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
Ok(
|
Ok(
|
||||||
self
|
self
|
||||||
.run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name.as_ref()))?
|
.run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name.as_ref()))
|
||||||
|
.await?
|
||||||
.trim_end()
|
.trim_end()
|
||||||
== self.db_name.as_ref(),
|
== self.db_name.as_ref(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.run_sql(&format!("CREATE DATABASE {}", self.db_name.as_ref()))?;
|
self
|
||||||
self.command_runner.run_successfully(
|
.run_sql(&format!("CREATE DATABASE {}", self.db_name.as_ref()))
|
||||||
|
.await?;
|
||||||
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"sh",
|
"sh",
|
||||||
args![
|
args![
|
||||||
"-c",
|
"-c",
|
||||||
|
|
@ -50,6 +58,7 @@ impl<D: AsRef<str>, S: Storage, C: CommandRunner> Symbol for Database<'_, D, S,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::error::Error;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Dump<'a, N, C, S> {
|
pub struct Dump<'a, N, C, S> {
|
||||||
|
|
@ -21,24 +21,28 @@ impl<'a, N, C: CommandRunner, S> Dump<'a, N, C, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
async fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = self
|
let b = self
|
||||||
.command_runner
|
.command_runner
|
||||||
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
|
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])
|
||||||
|
.await?;
|
||||||
Ok(String::from_utf8(b)?)
|
Ok(String::from_utf8(b)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for Dump<'_, N, C, S> {
|
impl<N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for Dump<'_, N, C, S> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let dump_date = self.storage.recent_date()?;
|
let dump_date = self.storage.recent_date()?;
|
||||||
let _modified_date = self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref()))?;
|
let _modified_date = self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref())).await?;
|
||||||
let modified_date = _modified_date.trim_end();
|
let modified_date = _modified_date.trim_end();
|
||||||
Ok(modified_date != "NULL" && u64::from_str(modified_date)? <= dump_date)
|
Ok(modified_date != "NULL" && u64::from_str(modified_date)? <= dump_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"sh",
|
"sh",
|
||||||
args![
|
args![
|
||||||
"-c",
|
"-c",
|
||||||
|
|
@ -49,6 +53,7 @@ impl<N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for Dump<'_, N, C, S> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -16,32 +17,37 @@ impl<'a, U: AsRef<str>, C: CommandRunner> User<'a, U, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
async fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = self
|
let b = self
|
||||||
.command_runner
|
.command_runner
|
||||||
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
|
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])
|
||||||
|
.await?;
|
||||||
Ok(String::from_utf8(b)?)
|
Ok(String::from_utf8(b)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<U: AsRef<str>, C: CommandRunner> Symbol for User<'_, U, C> {
|
impl<U: AsRef<str>, C: CommandRunner> Symbol for User<'_, U, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
Ok(
|
Ok(
|
||||||
self
|
self
|
||||||
.run_sql(&format!(
|
.run_sql(&format!(
|
||||||
"SELECT User FROM mysql.user WHERE User = '{}' AND plugin = 'unix_socket'",
|
"SELECT User FROM mysql.user WHERE User = '{}' AND plugin = 'unix_socket'",
|
||||||
self.user_name.as_ref()
|
self.user_name.as_ref()
|
||||||
))?
|
))
|
||||||
|
.await?
|
||||||
.trim_end()
|
.trim_end()
|
||||||
== self.user_name.as_ref(),
|
== self.user_name.as_ref(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.run_sql(&format!(
|
self
|
||||||
|
.run_sql(&format!(
|
||||||
"GRANT ALL ON {0}.* TO {0} IDENTIFIED VIA unix_socket",
|
"GRANT ALL ON {0}.* TO {0} IDENTIFIED VIA unix_socket",
|
||||||
self.user_name.as_ref()
|
self.user_name.as_ref()
|
||||||
))?;
|
))
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
// Symbol
|
// Symbol
|
||||||
|
#[async_trait(?Send)]
|
||||||
pub trait Symbol {
|
pub trait Symbol {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>>;
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>>;
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>>;
|
async fn execute(&self) -> Result<(), Box<dyn Error>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod acme;
|
pub mod acme;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -29,28 +30,29 @@ impl<T: AsRef<Path>, C: CommandRunner> fmt::Display for Install<'_, T, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<T: AsRef<Path>, C: CommandRunner> Symbol for Install<'_, T, C> {
|
impl<T: AsRef<Path>, C: CommandRunner> Symbol for Install<'_, T, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.target.as_ref().exists() {
|
if !self.target.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let result = self.command_runner.run_with_args(
|
let output = self
|
||||||
|
.command_runner
|
||||||
|
.get_output(
|
||||||
"sh",
|
"sh",
|
||||||
args![
|
args![
|
||||||
"-c",
|
"-c",
|
||||||
format!("cd '{}' && npm ls", self.target.as_ref().to_str().unwrap()),
|
format!("cd '{}' && npm ls", self.target.as_ref().to_str().unwrap()),
|
||||||
],
|
],
|
||||||
)?;
|
|
||||||
Ok(
|
|
||||||
result.status.success()
|
|
||||||
&& !String::from_utf8(result.stdout)
|
|
||||||
.unwrap()
|
|
||||||
.contains("(empty)"),
|
|
||||||
)
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(!String::from_utf8(output).unwrap().contains("(empty)"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"sh",
|
"sh",
|
||||||
args![
|
args![
|
||||||
"-c",
|
"-c",
|
||||||
|
|
@ -60,6 +62,7 @@ impl<T: AsRef<Path>, C: CommandRunner> Symbol for Install<'_, T, C> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
@ -27,10 +28,11 @@ impl<_C, C, P, U> Owner<_C, C, P, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, U: AsRef<str>> Symbol
|
impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, U: AsRef<str>> Symbol
|
||||||
for Owner<_C, C, P, U>
|
for Owner<_C, C, P, U>
|
||||||
{
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.path.as_ref().exists() {
|
if !self.path.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
@ -39,10 +41,14 @@ impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, U: AsRef<str>> Symbol
|
||||||
Ok(actual_uid == target_uid)
|
Ok(actual_uid == target_uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.borrow().run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.run_successfully(
|
||||||
"chown",
|
"chown",
|
||||||
args!["-R", self.user_name.as_ref(), self.path.as_ref()],
|
args!["-R", self.user_name.as_ref(), self.path.as_ref()],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PostgreSQLDatabase<'a, N: AsRef<str>, S: AsRef<str>, C: CommandRunner> {
|
pub struct PostgreSQLDatabase<'a, N: AsRef<str>, S: AsRef<str>, C: CommandRunner> {
|
||||||
name: N,
|
name: N,
|
||||||
seed_file: S,
|
seed_file: S,
|
||||||
|
|
@ -18,11 +20,14 @@ impl<'a, N: AsRef<str>, S: AsRef<str>, C: CommandRunner> PostgreSQLDatabase<'a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
async fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = self.command_runner.get_output(
|
let b = self
|
||||||
|
.command_runner
|
||||||
|
.get_output(
|
||||||
"su",
|
"su",
|
||||||
args!["-", "postgres", "-c", format!("psql -t -c \"{}\"", sql)],
|
args!["-", "postgres", "-c", format!("psql -t -c \"{}\"", sql)],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
Ok(String::from_utf8(b)?)
|
Ok(String::from_utf8(b)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -35,21 +40,25 @@ impl<N: AsRef<str>, S: AsRef<str>, C: CommandRunner> fmt::Display
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<N: AsRef<str>, S: AsRef<str>, C: CommandRunner> Symbol for PostgreSQLDatabase<'_, N, S, C> {
|
impl<N: AsRef<str>, S: AsRef<str>, C: CommandRunner> Symbol for PostgreSQLDatabase<'_, N, S, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
Ok(
|
Ok(
|
||||||
self
|
self
|
||||||
.run_sql(&format!(
|
.run_sql(&format!(
|
||||||
"SELECT datname FROM pg_database WHERE datname LIKE '{}'",
|
"SELECT datname FROM pg_database WHERE datname LIKE '{}'",
|
||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
))?
|
))
|
||||||
|
.await?
|
||||||
.trim()
|
.trim()
|
||||||
== self.name.as_ref(),
|
== self.name.as_ref(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"su",
|
"su",
|
||||||
args![
|
args![
|
||||||
"-",
|
"-",
|
||||||
|
|
@ -57,8 +66,11 @@ impl<N: AsRef<str>, S: AsRef<str>, C: CommandRunner> Symbol for PostgreSQLDataba
|
||||||
"-c",
|
"-c",
|
||||||
format!("createuser {}", self.name.as_ref())
|
format!("createuser {}", self.name.as_ref())
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
self.command_runner.run_successfully(
|
.await?;
|
||||||
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"su",
|
"su",
|
||||||
args![
|
args![
|
||||||
"-",
|
"-",
|
||||||
|
|
@ -69,8 +81,11 @@ impl<N: AsRef<str>, S: AsRef<str>, C: CommandRunner> Symbol for PostgreSQLDataba
|
||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
self.command_runner.run_successfully(
|
.await?;
|
||||||
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"su",
|
"su",
|
||||||
args![
|
args![
|
||||||
"-",
|
"-",
|
||||||
|
|
@ -83,6 +98,7 @@ impl<N: AsRef<str>, S: AsRef<str>, C: CommandRunner> Symbol for PostgreSQLDataba
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
@ -36,10 +37,11 @@ impl<_C, C, P, S> SavedDirectory<_C, C, P, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, S: Storage> Symbol
|
impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, S: Storage> Symbol
|
||||||
for SavedDirectory<_C, C, P, S>
|
for SavedDirectory<_C, C, P, S>
|
||||||
{
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let metadata = fs::metadata(self.path.as_ref());
|
let metadata = fs::metadata(self.path.as_ref());
|
||||||
// Check if dir exists
|
// Check if dir exists
|
||||||
if let Err(e) = metadata {
|
if let Err(e) = metadata {
|
||||||
|
|
@ -57,7 +59,10 @@ impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, S: Storage> Symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
let dump_date = self.storage.recent_date()?;
|
let dump_date = self.storage.recent_date()?;
|
||||||
let output = self.command_runner.borrow().get_output(
|
let output = self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.get_output(
|
||||||
"sh",
|
"sh",
|
||||||
args![
|
args![
|
||||||
"-c",
|
"-c",
|
||||||
|
|
@ -66,17 +71,22 @@ impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, S: Storage> Symbol
|
||||||
self.path.as_ref().to_str().unwrap()
|
self.path.as_ref().to_str().unwrap()
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
let modified_date = u64::from_str(String::from_utf8(output)?.trim_end())?;
|
let modified_date = u64::from_str(String::from_utf8(output)?.trim_end())?;
|
||||||
if if self.dir == StorageDirection::Store {
|
if if self.dir == StorageDirection::Store {
|
||||||
modified_date > dump_date
|
modified_date > dump_date
|
||||||
} else {
|
} else {
|
||||||
dump_date > modified_date
|
dump_date > modified_date
|
||||||
} {
|
} {
|
||||||
let output = self.command_runner.borrow().run_with_args(
|
let output = self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.run_with_args(
|
||||||
"diff",
|
"diff",
|
||||||
args!["-rq", self.storage.read_filename()?, self.path.as_ref()],
|
args!["-rq", self.storage.read_filename()?, self.path.as_ref()],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
match output.status.code() {
|
match output.status.code() {
|
||||||
Some(0) => Ok(true),
|
Some(0) => Ok(true),
|
||||||
Some(1) => Ok(false),
|
Some(1) => Ok(false),
|
||||||
|
|
@ -87,21 +97,30 @@ impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, S: Storage> Symbol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
if self.dir == StorageDirection::Load {
|
if self.dir == StorageDirection::Load {
|
||||||
self
|
self
|
||||||
.command_runner
|
.command_runner
|
||||||
.borrow()
|
.borrow()
|
||||||
.run_successfully("rm", args!["-rf", self.path.as_ref()])?;
|
.run_successfully("rm", args!["-rf", self.path.as_ref()])
|
||||||
self.command_runner.borrow().run_successfully(
|
.await?;
|
||||||
|
self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.run_successfully(
|
||||||
"cp",
|
"cp",
|
||||||
args!["-a", self.storage.read_filename()?, self.path.as_ref()],
|
args!["-a", self.storage.read_filename()?, self.path.as_ref()],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
} else {
|
} else {
|
||||||
self.command_runner.borrow().run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.run_successfully(
|
||||||
"cp",
|
"cp",
|
||||||
args!["-a", self.path.as_ref(), self.storage.write_filename()],
|
args!["-a", self.path.as_ref(), self.storage.write_filename()],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
@ -21,15 +22,20 @@ impl<_C, C, S> ReloadService<_C, C, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<S: AsRef<str>, _C: CommandRunner, C: Borrow<_C>> Symbol for ReloadService<_C, C, S> {
|
impl<S: AsRef<str>, _C: CommandRunner, C: Borrow<_C>> Symbol for ReloadService<_C, C, S> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.borrow().run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.borrow()
|
||||||
|
.run_successfully(
|
||||||
"systemctl",
|
"systemctl",
|
||||||
args!["reload-or-restart", self.service.as_ref()],
|
args!["reload-or-restart", self.service.as_ref()],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
|
use crate::async_utils::sleep;
|
||||||
use crate::command_runner::{CommandRunner, SetuidCommandRunner};
|
use crate::command_runner::{CommandRunner, SetuidCommandRunner};
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -29,10 +30,10 @@ impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> UserService<'static, S, U,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> UserService<'_, S, U, R> {
|
impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> UserService<'_, S, U, R> {
|
||||||
fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result<String, Box<dyn Error>> {
|
async fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result<String, Box<dyn Error>> {
|
||||||
let mut tries = 5;
|
let mut tries = 5;
|
||||||
loop {
|
loop {
|
||||||
let result = self.command_runner.run_with_args("systemctl", args)?;
|
let result = self.command_runner.run_with_args("systemctl", args).await?;
|
||||||
if result.status.success() {
|
if result.status.success() {
|
||||||
return Ok(String::from_utf8(result.stdout)?.trim_end().to_string());
|
return Ok(String::from_utf8(result.stdout)?.trim_end().to_string());
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -46,28 +47,35 @@ impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> UserService<'_, S, U, R> {
|
||||||
if tries == 0 {
|
if tries == 0 {
|
||||||
return Err("Gave up waiting for dbus to appear".to_string().into());
|
return Err("Gave up waiting for dbus to appear".to_string().into());
|
||||||
}
|
}
|
||||||
sleep(Duration::from_millis(500));
|
sleep(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
|
async fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
loop {
|
loop {
|
||||||
let active_state = self.systemctl_wait_for_dbus(args![
|
let active_state = self
|
||||||
|
.systemctl_wait_for_dbus(args![
|
||||||
"--user",
|
"--user",
|
||||||
"show",
|
"show",
|
||||||
"--property",
|
"--property",
|
||||||
"ActiveState",
|
"ActiveState",
|
||||||
self.service_name,
|
self.service_name,
|
||||||
])?;
|
])
|
||||||
|
.await?;
|
||||||
match active_state.as_ref() {
|
match active_state.as_ref() {
|
||||||
"ActiveState=activating" => sleep(Duration::from_millis(500)),
|
"ActiveState=activating" => sleep(Duration::from_millis(500)).await,
|
||||||
"ActiveState=active" => return Ok(true),
|
"ActiveState=active" => return Ok(true),
|
||||||
"ActiveState=failed" => {
|
"ActiveState=failed" => {
|
||||||
return Err(
|
return Err(
|
||||||
String::from_utf8(self.command_runner.get_output(
|
String::from_utf8(
|
||||||
|
self
|
||||||
|
.command_runner
|
||||||
|
.get_output(
|
||||||
"journalctl",
|
"journalctl",
|
||||||
args!["--user", format!("--user-unit={}", self.service_name)],
|
args!["--user", format!("--user-unit={}", self.service_name)],
|
||||||
)?)?
|
)
|
||||||
|
.await?,
|
||||||
|
)?
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -77,24 +85,29 @@ impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> UserService<'_, S, U, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> Symbol for UserService<'_, S, U, R> {
|
impl<S: AsRef<Path>, U: AsRef<str>, R: CommandRunner> Symbol for UserService<'_, S, U, R> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
self.check_if_service()
|
self.check_if_service().await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.systemctl_wait_for_dbus(args!["--user", "enable", self.service_name])?;
|
self
|
||||||
self.systemctl_wait_for_dbus(args!["--user", "restart", self.service_name])?;
|
.systemctl_wait_for_dbus(args!["--user", "enable", self.service_name])
|
||||||
|
.await?;
|
||||||
|
self
|
||||||
|
.systemctl_wait_for_dbus(args!["--user", "restart", self.service_name])
|
||||||
|
.await?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if !(self.check_if_service()?) {
|
if !(self.check_if_service().await?) {
|
||||||
return Err("Generic error".into());
|
return Err("Generic error".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.socket_path.as_ref().exists() {
|
if self.socket_path.as_ref().exists() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
sleep(Duration::from_millis(500));
|
sleep(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
@ -18,16 +19,18 @@ impl<'a, U, C> UserSession<'a, U, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<U: AsRef<str>, C: CommandRunner> Symbol for UserSession<'_, U, C> {
|
impl<U: AsRef<str>, C: CommandRunner> Symbol for UserSession<'_, U, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let path = Path::new("/var/lib/systemd/linger").join(self.user_name.as_ref());
|
let path = Path::new("/var/lib/systemd/linger").join(self.user_name.as_ref());
|
||||||
Ok(path.exists())
|
Ok(path.exists())
|
||||||
// Could also do `loginctl show-user ${self.user_name} | grep -F 'Linger=yes`
|
// Could also do `loginctl show-user ${self.user_name} | grep -F 'Linger=yes`
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self
|
self
|
||||||
.command_runner
|
.command_runner
|
||||||
.run_successfully("loginctl", args!["enable-linger", self.user_name.as_ref()])
|
.run_successfully("loginctl", args!["enable-linger", self.user_name.as_ref()])
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -23,23 +24,29 @@ impl<C, D, K, P> Csr<C, D, K, P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<C: CommandRunner, D: Borrow<str>, K: Borrow<Path>, P: Borrow<Path>> Symbol
|
impl<C: CommandRunner, D: Borrow<str>, K: Borrow<Path>, P: Borrow<Path>> Symbol
|
||||||
for Csr<C, D, K, P>
|
for Csr<C, D, K, P>
|
||||||
{
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.csr_path.borrow().exists() {
|
if !self.csr_path.borrow().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = self.command_runner.get_stderr(
|
let output = self
|
||||||
|
.command_runner
|
||||||
|
.get_stderr(
|
||||||
"openssl",
|
"openssl",
|
||||||
args!["req", "-in", self.csr_path.borrow(), "-noout", "-verify",],
|
args!["req", "-in", self.csr_path.borrow(), "-noout", "-verify",],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
Ok(output == b"verify OK\n")
|
Ok(output == b"verify OK\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"openssl",
|
"openssl",
|
||||||
args![
|
args![
|
||||||
"req",
|
"req",
|
||||||
|
|
@ -52,8 +59,8 @@ impl<C: CommandRunner, D: Borrow<str>, K: Borrow<Path>, P: Borrow<Path>> Symbol
|
||||||
"-subj",
|
"-subj",
|
||||||
format!("/CN={}", self.domain.borrow()),
|
format!("/CN={}", self.domain.borrow()),
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
Ok(())
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
@ -22,13 +23,16 @@ impl<C, P> Key<C, P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<C: CommandRunner, P: AsRef<Path>> Symbol for Key<C, P> {
|
impl<C: CommandRunner, P: AsRef<Path>> Symbol for Key<C, P> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.file_path.as_ref().exists() {
|
if !self.file_path.as_ref().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = self.command_runner.get_output(
|
let stdout = self
|
||||||
|
.command_runner
|
||||||
|
.get_output(
|
||||||
"openssl",
|
"openssl",
|
||||||
args![
|
args![
|
||||||
"rsa",
|
"rsa",
|
||||||
|
|
@ -38,13 +42,16 @@ impl<C: CommandRunner, P: AsRef<Path>> Symbol for Key<C, P> {
|
||||||
"-check",
|
"-check",
|
||||||
"-text",
|
"-text",
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
// FIXME check bytes
|
// FIXME check bytes
|
||||||
Ok(stdout.ends_with(b"RSA key ok\n"))
|
Ok(stdout.ends_with(b"RSA key ok\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"openssl",
|
"openssl",
|
||||||
args![
|
args![
|
||||||
"genrsa",
|
"genrsa",
|
||||||
|
|
@ -53,6 +60,7 @@ impl<C: CommandRunner, P: AsRef<Path>> Symbol for Key<C, P> {
|
||||||
self.get_bytes().to_string(),
|
self.get_bytes().to_string(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -17,11 +18,13 @@ impl<U, C> User<U, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<U: AsRef<str>, C: CommandRunner> Symbol for User<U, C> {
|
impl<U: AsRef<str>, C: CommandRunner> Symbol for User<U, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let output = self
|
let output = self
|
||||||
.command_runner
|
.command_runner
|
||||||
.run_with_args("getent", args!["passwd", self.user_name.as_ref()])?;
|
.run_with_args("getent", args!["passwd", self.user_name.as_ref()])
|
||||||
|
.await?;
|
||||||
match output.status.code() {
|
match output.status.code() {
|
||||||
Some(2) => Ok(false),
|
Some(2) => Ok(false),
|
||||||
Some(0) => Ok(true),
|
Some(0) => Ok(true),
|
||||||
|
|
@ -29,21 +32,24 @@ impl<U: AsRef<str>, C: CommandRunner> Symbol for User<U, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully(
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"adduser",
|
"adduser",
|
||||||
args![
|
args![
|
||||||
// "-m", // Necessary for Fedora, not accepted in Debian
|
// "-m", // Necessary for Fedora, not accepted in Debian
|
||||||
"--system",
|
"--system",
|
||||||
self.user_name.as_ref(),
|
self.user_name.as_ref(),
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
Ok(())
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use crate::async_utils::run;
|
||||||
use crate::command_runner::StdCommandRunner;
|
use crate::command_runner::StdCommandRunner;
|
||||||
use crate::symbols::user::User;
|
use crate::symbols::user::User;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
|
@ -54,7 +60,7 @@ mod test {
|
||||||
user_name: "nonexisting",
|
user_name: "nonexisting",
|
||||||
command_runner: StdCommandRunner,
|
command_runner: StdCommandRunner,
|
||||||
};
|
};
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
assert_eq!(run(symbol.target_reached()).unwrap(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -63,6 +69,6 @@ mod test {
|
||||||
user_name: "root",
|
user_name: "root",
|
||||||
command_runner: StdCommandRunner,
|
command_runner: StdCommandRunner,
|
||||||
};
|
};
|
||||||
assert_eq!(symbol.target_reached().unwrap(), true);
|
assert_eq!(run(symbol.target_reached()).unwrap(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::command_runner::CommandRunner;
|
||||||
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::File as FsFile;
|
use std::fs::File as FsFile;
|
||||||
|
|
@ -5,9 +8,6 @@ use std::io;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::command_runner::CommandRunner;
|
|
||||||
use crate::symbols::Symbol;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Plugin<'a, P, N, R> {
|
pub struct Plugin<'a, P, N, R> {
|
||||||
base: P,
|
base: P,
|
||||||
|
|
@ -33,8 +33,9 @@ impl<'a, P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> Plugin<'a, P, N, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> Symbol for Plugin<'_, P, N, R> {
|
impl<P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> Symbol for Plugin<'_, P, N, R> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let base_path = self.get_path();
|
let base_path = self.get_path();
|
||||||
if !base_path.exists() {
|
if !base_path.exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|
@ -65,7 +66,9 @@ impl<P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> Symbol for Plugin<'_, P, N
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let upstream = self.command_runner.get_output(
|
let upstream = self
|
||||||
|
.command_runner
|
||||||
|
.get_output(
|
||||||
"curl",
|
"curl",
|
||||||
args![
|
args![
|
||||||
"--form",
|
"--form",
|
||||||
|
|
@ -77,11 +80,12 @@ impl<P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> Symbol for Plugin<'_, P, N
|
||||||
),
|
),
|
||||||
"https://api.wordpress.org/plugins/update-check/1.1/",
|
"https://api.wordpress.org/plugins/update-check/1.1/",
|
||||||
],
|
],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
Ok(String::from_utf8(upstream)?.contains(r###""plugins":[]"###))
|
Ok(String::from_utf8(upstream)?.contains(r###""plugins":[]"###))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
let source = format!(
|
let source = format!(
|
||||||
"https://downloads.wordpress.org/plugin/{}.zip",
|
"https://downloads.wordpress.org/plugin/{}.zip",
|
||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
|
|
@ -89,13 +93,18 @@ impl<P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> Symbol for Plugin<'_, P, N
|
||||||
let zip = format!("/tmp/{}.zip", self.name.as_ref());
|
let zip = format!("/tmp/{}.zip", self.name.as_ref());
|
||||||
self
|
self
|
||||||
.command_runner
|
.command_runner
|
||||||
.run_successfully("curl", args![source, "-o", zip])?;
|
.run_successfully("curl", args![source, "-o", zip])
|
||||||
|
.await?;
|
||||||
self
|
self
|
||||||
.command_runner
|
.command_runner
|
||||||
.run_successfully("rm", args!["-rf", self.get_path()])?;
|
.run_successfully("rm", args!["-rf", self.get_path()])
|
||||||
self.command_runner.run_successfully(
|
.await?;
|
||||||
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully(
|
||||||
"unzip",
|
"unzip",
|
||||||
args![zip, "-d", self.base.as_ref().join("wp-content/plugins")],
|
args![zip, "-d", self.base.as_ref().join("wp-content/plugins")],
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::CommandRunner;
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
|
use async_trait::async_trait;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
@ -59,8 +60,9 @@ impl<C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Translation<'_, C, D, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl<C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Symbol for Translation<'_, C, D, R> {
|
impl<C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Symbol for Translation<'_, C, D, R> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let mut newest = String::new();
|
let mut newest = String::new();
|
||||||
let match_date = Regex::new("(?m)^\"PO-Revision-Date: (.+)\\+0000\\\\n\"$").unwrap();
|
let match_date = Regex::new("(?m)^\"PO-Revision-Date: (.+)\\+0000\\\\n\"$").unwrap();
|
||||||
for (_, target) in self.get_pairs() {
|
for (_, target) in self.get_pairs() {
|
||||||
|
|
@ -86,14 +88,17 @@ impl<C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Symbol for Translation<'_,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let upstream = self.command_runner.get_output(
|
let upstream = self
|
||||||
|
.command_runner
|
||||||
|
.get_output(
|
||||||
"curl",
|
"curl",
|
||||||
args![format!(
|
args![format!(
|
||||||
"https://api.wordpress.org/core/version-check/1.7/?version={}&locale={}",
|
"https://api.wordpress.org/core/version-check/1.7/?version={}&locale={}",
|
||||||
self.version,
|
self.version,
|
||||||
self.locale.as_ref()
|
self.locale.as_ref()
|
||||||
)],
|
)],
|
||||||
)?;
|
)
|
||||||
|
.await?;
|
||||||
Ok(String::from_utf8(upstream)?.contains(&format!(
|
Ok(String::from_utf8(upstream)?.contains(&format!(
|
||||||
r###"language":"{}","version":"{}","updated":"{}"###,
|
r###"language":"{}","version":"{}","updated":"{}"###,
|
||||||
self.locale.as_ref(),
|
self.locale.as_ref(),
|
||||||
|
|
@ -102,11 +107,12 @@ impl<C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Symbol for Translation<'_,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
for (source, target) in self.get_pairs() {
|
for (source, target) in self.get_pairs() {
|
||||||
self
|
self
|
||||||
.command_runner
|
.command_runner
|
||||||
.run_successfully("curl", args!["--compressed", "-o", target, source,])?;
|
.run_successfully("curl", args!["--compressed", "-o", target, source,])
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ impl<T: AsRef<Path>> SocketSpec for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct LocalTcpSocket(usize);
|
pub struct LocalTcpSocket(usize);
|
||||||
|
|
||||||
impl LocalTcpSocket {
|
impl LocalTcpSocket {
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,5 @@ impl<T: Resource> ToArtifact for Option<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Resource> ToArtifact for T {
|
impl<T: Resource> ToArtifact for T {
|
||||||
type Artifact = T::Artifact;
|
type Artifact = <Self as Resource>::Artifact;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use schematics::async_utils::run;
|
||||||
use schematics::symbols::file::File as FileSymbol;
|
use schematics::symbols::file::File as FileSymbol;
|
||||||
use schematics::symbols::Symbol;
|
use schematics::symbols::Symbol;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
@ -30,7 +31,9 @@ fn already_reached() {
|
||||||
let dir = get_dir(Some("target content"));
|
let dir = get_dir(Some("target content"));
|
||||||
let symbol = get_symbol(dir.path());
|
let symbol = get_symbol(dir.path());
|
||||||
|
|
||||||
assert_eq!(symbol.target_reached().unwrap(), true);
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bad
|
// Bad
|
||||||
|
|
@ -39,9 +42,11 @@ fn wrong_prefix() {
|
||||||
let dir = get_dir(Some("not target content"));
|
let dir = get_dir(Some("not target content"));
|
||||||
let symbol = get_symbol(dir.path());
|
let symbol = get_symbol(dir.path());
|
||||||
|
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
run(async {
|
||||||
symbol.execute().unwrap();
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
assert_eq!(symbol.target_reached().unwrap(), true);
|
symbol.execute().await.unwrap();
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -49,9 +54,11 @@ fn wrong_postfix() {
|
||||||
let dir = get_dir(Some("target content not"));
|
let dir = get_dir(Some("target content not"));
|
||||||
let symbol = get_symbol(dir.path());
|
let symbol = get_symbol(dir.path());
|
||||||
|
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
run(async {
|
||||||
symbol.execute().unwrap();
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
assert_eq!(symbol.target_reached().unwrap(), true);
|
symbol.execute().await.unwrap();
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -59,9 +66,11 @@ fn empty_file() {
|
||||||
let dir = get_dir(Some(""));
|
let dir = get_dir(Some(""));
|
||||||
let symbol = get_symbol(dir.path());
|
let symbol = get_symbol(dir.path());
|
||||||
|
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
run(async {
|
||||||
symbol.execute().unwrap();
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
assert_eq!(symbol.target_reached().unwrap(), true);
|
symbol.execute().await.unwrap();
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -69,9 +78,11 @@ fn no_file() {
|
||||||
let dir = get_dir(None);
|
let dir = get_dir(None);
|
||||||
let symbol = get_symbol(dir.path());
|
let symbol = get_symbol(dir.path());
|
||||||
|
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
run(async {
|
||||||
symbol.execute().unwrap();
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
assert_eq!(symbol.target_reached().unwrap(), true);
|
symbol.execute().await.unwrap();
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exceptional cases
|
// Exceptional cases
|
||||||
|
|
@ -80,23 +91,29 @@ fn no_file() {
|
||||||
fn may_not_read_file() {
|
fn may_not_read_file() {
|
||||||
let symbol = FileSymbol::new(Path::new("/etc/shadow"), "");
|
let symbol = FileSymbol::new(Path::new("/etc/shadow"), "");
|
||||||
|
|
||||||
assert_eq!(symbol.target_reached().is_err(), true);
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.is_err(), true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn may_not_create_file() {
|
fn may_not_create_file() {
|
||||||
let symbol = get_symbol(&Path::new("/proc/somefile"));
|
let symbol = get_symbol(&Path::new("/proc/somefile"));
|
||||||
|
|
||||||
|
run(async {
|
||||||
// Could also return an error
|
// Could also return an error
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
assert!(symbol.execute().is_err());
|
assert!(symbol.execute().await.is_err());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn directory_missing() {
|
fn directory_missing() {
|
||||||
let symbol = get_symbol(&Path::new("/nonexisting"));
|
let symbol = get_symbol(&Path::new("/nonexisting"));
|
||||||
|
|
||||||
|
run(async {
|
||||||
// Could also return an error
|
// Could also return an error
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
assert!(symbol.execute().is_err());
|
assert!(symbol.execute().await.is_err());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,52 @@
|
||||||
use schematics::resources::{Cert, Csr, GitCheckout};
|
use async_trait::async_trait;
|
||||||
use schematics::schema::SymbolRunner;
|
use schematics::async_utils::sleep;
|
||||||
|
use schematics::loggers::{Logger, StoringLogger};
|
||||||
|
use schematics::resources::{AcmeUser, Cert, Csr, GitCheckout};
|
||||||
use schematics::symbols::Symbol;
|
use schematics::symbols::Symbol;
|
||||||
use schematics::Setup;
|
use schematics::Setup;
|
||||||
|
use schematics::SymbolRunner;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
struct TestSymbolRunner {
|
struct TestSymbolRunner {
|
||||||
count: Rc<RefCell<usize>>,
|
count: Rc<RefCell<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
impl SymbolRunner for TestSymbolRunner {
|
impl SymbolRunner for TestSymbolRunner {
|
||||||
fn run_symbol<S: Symbol + Debug>(
|
async fn run_symbol<S: Symbol + Debug, L: Logger>(
|
||||||
&self,
|
&self,
|
||||||
_symbol: &S,
|
_symbol: &S,
|
||||||
|
_logger: &L,
|
||||||
_force: bool,
|
_force: bool,
|
||||||
) -> Result<bool, Box<dyn Error>> {
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
*self.count.borrow_mut() += 1;
|
*self.count.borrow_mut() += 1;
|
||||||
|
sleep(Duration::from_millis(0)).await;
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_create_an_acme_user() {
|
||||||
|
let count = Rc::new(RefCell::new(0));
|
||||||
|
let runner = TestSymbolRunner {
|
||||||
|
count: Rc::clone(&count),
|
||||||
|
};
|
||||||
|
let setup = Setup::new(runner, StoringLogger::new());
|
||||||
|
assert_eq!((setup.add(AcmeUser).unwrap().0).0, "acme");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn runs_only_once() {
|
fn runs_only_once() {
|
||||||
let count = Rc::new(RefCell::new(0));
|
let count = Rc::new(RefCell::new(0));
|
||||||
let runner = TestSymbolRunner {
|
let runner = TestSymbolRunner {
|
||||||
count: Rc::clone(&count),
|
count: Rc::clone(&count),
|
||||||
};
|
};
|
||||||
let mut setup = Setup::new(runner);
|
let setup = Setup::new(runner, StoringLogger::new());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(setup.add(Csr("somehost")).unwrap().0)
|
(setup.add(Csr("somehost")).unwrap().0)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -52,7 +70,7 @@ fn can_create_an_acme_cert() {
|
||||||
let runner = TestSymbolRunner {
|
let runner = TestSymbolRunner {
|
||||||
count: Rc::clone(&count),
|
count: Rc::clone(&count),
|
||||||
};
|
};
|
||||||
let mut setup = Setup::new(runner);
|
let setup = Setup::new(runner, StoringLogger::new());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(setup.add(Cert("somehost")).unwrap().0)
|
(setup.add(Cert("somehost")).unwrap().0)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -69,7 +87,7 @@ fn can_create_a_git_checkout() {
|
||||||
let runner = TestSymbolRunner {
|
let runner = TestSymbolRunner {
|
||||||
count: Rc::clone(&count),
|
count: Rc::clone(&count),
|
||||||
};
|
};
|
||||||
let mut setup = Setup::new(runner);
|
let setup = Setup::new(runner, StoringLogger::new());
|
||||||
setup
|
setup
|
||||||
.add(GitCheckout(
|
.add(GitCheckout(
|
||||||
"/tmp/somepath".into(),
|
"/tmp/somepath".into(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue