use std::any::Any;
use std::fmt::Display;
use std::sync::Arc;
use log::warn;
use crate::bodies::{HookWrapper, InnerBody};
use crate::validation::Action;
use crate::{AnyError, Spirit};
pub trait IntoResult<T>: Sized {
fn into_result(self) -> Result<T, AnyError>;
}
impl<T> IntoResult<T> for Result<T, AnyError> {
fn into_result(self) -> Result<T, AnyError> {
self
}
}
impl<T> IntoResult<T> for T {
fn into_result(self) -> Result<T, AnyError> {
Ok(self)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub enum Autojoin {
TerminateAndJoin,
Join,
Abandon,
}
pub trait Extensible: Sized {
type Opts;
type Config;
type Ok;
const STARTED: bool;
fn before_config<F>(self, cback: F) -> Result<Self::Ok, AnyError>
where
F: FnOnce(&Self::Config, &Self::Opts) -> Result<(), AnyError> + Send + 'static;
fn config_validator<F>(self, f: F) -> Result<Self::Ok, AnyError>
where
F: FnMut(&Arc<Self::Config>, &Arc<Self::Config>, &Self::Opts) -> Result<Action, AnyError>
+ Send
+ 'static;
fn config_mutator<F>(self, f: F) -> Self
where
F: FnMut(&mut Self::Config) + Send + 'static;
fn on_config<F>(self, hook: F) -> Self
where
F: FnMut(&Self::Opts, &Arc<Self::Config>) + Send + 'static;
fn on_signal<F>(self, signal: libc::c_int, hook: F) -> Result<Self::Ok, AnyError>
where
F: FnMut() + Send + 'static;
fn on_terminate<F>(self, hook: F) -> Self
where
F: FnOnce() + Send + 'static;
fn run_before<B>(self, body: B) -> Result<Self::Ok, AnyError>
where
B: FnOnce(&Arc<Spirit<Self::Opts, Self::Config>>) -> Result<(), AnyError> + Send + 'static;
fn run_around<W>(self, wrapper: W) -> Result<Self::Ok, AnyError>
where
W: FnOnce(&Arc<Spirit<Self::Opts, Self::Config>>, InnerBody) -> Result<(), AnyError>
+ Send
+ 'static;
fn around_hooks<W>(self, wrapper: W) -> Self
where
W: HookWrapper;
fn with<E>(self, ext: E) -> Result<Self::Ok, AnyError>
where
E: Extension<Self::Ok>;
fn singleton<T: 'static>(&mut self) -> bool;
fn with_singleton<T>(self, singleton: T) -> Result<Self::Ok, AnyError>
where
T: Extension<Self::Ok> + 'static;
fn keep_guard<G: Any + Send>(self, guard: G) -> Self;
fn autojoin_bg_thread(self, autojoin: Autojoin) -> Self;
}
impl<C> Extensible for Result<C, AnyError>
where
C: Extensible<Ok = C>,
{
type Opts = C::Opts;
type Config = C::Config;
type Ok = C;
const STARTED: bool = C::STARTED;
fn before_config<F>(self, cback: F) -> Result<<Self as Extensible>::Ok, AnyError>
where
F: FnOnce(&Self::Config, &Self::Opts) -> Result<(), AnyError> + Send + 'static,
{
self.and_then(|c| c.before_config(cback))
}
fn config_mutator<F>(self, f: F) -> Self
where
F: FnMut(&mut Self::Config) + Send + 'static,
{
self.map(|c| c.config_mutator(f))
}
fn config_validator<F>(self, f: F) -> Result<<Self as Extensible>::Ok, AnyError>
where
F: FnMut(&Arc<Self::Config>, &Arc<Self::Config>, &Self::Opts) -> Result<Action, AnyError>
+ Send
+ 'static,
{
self.and_then(|c| c.config_validator(f))
}
fn on_config<F>(self, hook: F) -> Self
where
F: FnMut(&Self::Opts, &Arc<Self::Config>) + Send + 'static,
{
self.map(|c| c.on_config(hook))
}
fn on_signal<F>(
self,
signal: libc::c_int,
hook: F,
) -> Result<<Self as Extensible>::Ok, AnyError>
where
F: FnMut() + Send + 'static,
{
self.and_then(|c| c.on_signal(signal, hook))
}
fn on_terminate<F>(self, hook: F) -> Self
where
F: FnOnce() + Send + 'static,
{
self.map(|c| c.on_terminate(hook))
}
fn run_before<B>(self, body: B) -> Result<<Self as Extensible>::Ok, AnyError>
where
B: FnOnce(&Arc<Spirit<Self::Opts, Self::Config>>) -> Result<(), AnyError> + Send + 'static,
{
self.and_then(|c| c.run_before(body))
}
fn run_around<W>(self, wrapper: W) -> Result<<Self as Extensible>::Ok, AnyError>
where
W: FnOnce(&Arc<Spirit<Self::Opts, Self::Config>>, InnerBody) -> Result<(), AnyError>
+ Send
+ 'static,
{
self.and_then(|c| c.run_around(wrapper))
}
fn around_hooks<W>(self, wrapper: W) -> Self
where
W: HookWrapper,
{
self.map(|c| c.around_hooks(wrapper))
}
fn with<E>(self, ext: E) -> Result<<Self as Extensible>::Ok, AnyError>
where
E: Extension<<Self as Extensible>::Ok>,
{
self.and_then(|c| c.with(ext))
}
fn singleton<T: 'static>(&mut self) -> bool {
self.as_mut()
.map(Extensible::singleton::<T>)
.unwrap_or_default()
}
fn with_singleton<T>(self, singleton: T) -> Result<<Self as Extensible>::Ok, AnyError>
where
T: Extension<<Self as Extensible>::Ok> + 'static,
{
self.and_then(|c| c.with_singleton(singleton))
}
fn keep_guard<G: Any + Send>(self, guard: G) -> Self {
self.map(|s| s.keep_guard(guard))
}
fn autojoin_bg_thread(self, autojoin: Autojoin) -> Self {
self.map(|me| me.autojoin_bg_thread(autojoin))
}
}
pub trait Extension<B> {
fn apply(self, builder: B) -> Result<B, AnyError>;
}
impl<B, F, R> Extension<B> for F
where
F: FnOnce(B) -> R,
R: IntoResult<B>,
{
fn apply(self, builder: B) -> Result<B, AnyError> {
self(builder).into_result()
}
}
pub fn immutable_cfg_init<Ext, R, E, F, N>(extractor: E, init: F, name: N) -> impl Extension<Ext>
where
Ext: Extensible,
E: for<'a> Fn(&'a Ext::Config) -> &R + Send + 'static,
F: FnOnce(&R) + Send + 'static,
R: Clone + PartialEq + Send + 'static,
N: Display + Send + 'static,
{
let mut first = None;
let mut init = Some(init);
let on_cfg = move |_o: &Ext::Opts, c: &Arc<Ext::Config>| {
let extracted = extractor(c);
if first.is_none() {
first = Some(extracted.clone());
(init.take().expect("Init called multiple times"))(extracted);
} else if first.as_ref() != Some(extracted) {
warn!("Configuration {} can't be changed at runtime", name);
}
};
|ext: Ext| ext.on_config(on_cfg)
}
pub fn immutable_cfg<Ext, R, E, N>(extractor: E, name: N) -> impl Extension<Ext>
where
Ext: Extensible,
E: for<'a> Fn(&'a Ext::Config) -> &R + Send + 'static,
R: Clone + PartialEq + Send + 'static,
N: Display + Send + 'static,
{
immutable_cfg_init(extractor, |_| (), name)
}