use std::collections::HashSet;
use std::sync::{Arc, Mutex};
pub struct TypedLayer<O, E> {
build_fn: Box<dyn Fn() -> Result<O, E> + Send + Sync>,
provides: HashSet<String>,
requires: HashSet<String>,
}
impl<O, E> TypedLayer<O, E> {
#[inline]
pub fn from_fn<F>(f: F) -> Self
where
F: Fn() -> Result<O, E> + Send + Sync + 'static,
{
Self {
build_fn: Box::new(f),
provides: HashSet::new(),
requires: HashSet::new(),
}
}
#[inline]
pub fn from_fn_infallible<F>(f: F) -> Self
where
F: Fn() -> O + Send + Sync + 'static,
E: Default,
{
Self {
build_fn: Box::new(move || Ok(f())),
provides: HashSet::new(),
requires: HashSet::new(),
}
}
#[inline]
pub fn providing(mut self, name: &str) -> Self {
self.provides.insert(name.to_string());
self
}
#[inline]
pub fn requiring(mut self, name: &str) -> Self {
self.requires.insert(name.to_string());
self
}
#[inline]
pub fn with_requirements<F, I>(f: F, requires: I) -> Self
where
F: Fn() -> Result<O, E> + Send + Sync + 'static,
I: IntoIterator,
I::Item: Into<String>,
{
Self {
build_fn: Box::new(f),
provides: HashSet::new(),
requires: requires.into_iter().map(Into::into).collect(),
}
}
#[inline]
pub fn provides(&self) -> &HashSet<String> {
&self.provides
}
#[inline]
pub fn requires(&self) -> &HashSet<String> {
&self.requires
}
#[inline]
pub fn build(&self) -> Result<O, E> {
(self.build_fn)()
}
#[inline]
pub fn build_with_dependencies<R>(&self, _ctx: &R) -> Result<O, E>
where
E: From<LayerError>,
{
if !self.requires.is_empty() {
return Err(LayerError::MissingDependencies {
missing: self.requires.iter().cloned().collect(),
}.into());
}
self.build()
}
pub fn merge_all(layers: Vec<Self>) -> MergedLayer<O, E>
where
O: 'static,
E: 'static,
{
let mut provides = HashSet::new();
let mut requires = HashSet::new();
for layer in &layers {
provides.extend(layer.provides.iter().cloned());
requires.extend(layer.requires.iter().cloned());
}
requires.retain(|r| !provides.contains(r));
MergedLayer {
layers,
provides,
requires,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum LayerError {
MissingDependencies {
missing: Vec<String>,
},
}
impl std::fmt::Display for LayerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LayerError::MissingDependencies { missing } => {
write!(f, "missing dependencies: {}", missing.join(", "))
}
}
}
}
impl std::error::Error for LayerError {}
pub struct MergedLayer<O, E> {
#[allow(dead_code)]
layers: Vec<TypedLayer<O, E>>,
provides: HashSet<String>,
requires: HashSet<String>,
}
impl<O, E> MergedLayer<O, E> {
pub fn provides(&self) -> &HashSet<String> {
&self.provides
}
pub fn requires(&self) -> &HashSet<String> {
&self.requires
}
}
pub trait TypedLayerExt<O, E> {
fn memoized(self) -> MemoizedLayer<O, E>;
}
impl<O, E> TypedLayerExt<O, E> for TypedLayer<O, E>
where
O: Clone + Send + Sync + 'static,
E: Clone + Send + Sync + 'static,
{
fn memoized(self) -> MemoizedLayer<O, E> {
MemoizedLayer {
inner: self,
cached: Arc::new(Mutex::new(None)),
}
}
}
pub struct MemoizedLayer<O, E> {
inner: TypedLayer<O, E>,
cached: Arc<Mutex<Option<Result<O, E>>>>,
}
impl<O, E> MemoizedLayer<O, E>
where
O: Clone,
E: Clone,
{
pub fn build(&self) -> Result<O, E> {
if let Some(cached) = self.cached.lock().unwrap().clone() {
return cached;
}
let result = self.inner.build();
*self.cached.lock().unwrap() = Some(result.clone());
result
}
pub fn provides(&self) -> &HashSet<String> {
self.inner.provides()
}
pub fn requires(&self) -> &HashSet<String> {
self.inner.requires()
}
}