use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cell::RefCell;
#[inline]
pub fn run_with_config<R, A, F>(config: &R, computation: F) -> A
where
F: FnOnce(&R) -> A,
{
computation(config)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn run_with_env<R, A, F>(env: R, computation: F) -> A
where
F: FnOnce(&R) -> A,
{
computation(&env)
}
#[inline]
pub fn run_with_local<R, A, M, F>(env: &R, modifier: M, computation: F) -> A
where
R: Clone,
M: FnOnce(R) -> R,
F: FnOnce(&R) -> A,
{
let local_env = modifier(env.clone());
computation(&local_env)
}
pub struct Reader<R, A> {
run: Box<dyn FnOnce(&R) -> A>,
}
impl<R: 'static, A: 'static> Reader<R, A> {
#[inline]
pub fn new<F>(f: F) -> Self
where
F: FnOnce(&R) -> A + 'static,
{
Reader { run: Box::new(f) }
}
#[inline]
pub fn run(self, env: &R) -> A {
(self.run)(env)
}
#[inline]
pub fn map<B: 'static, F>(self, f: F) -> Reader<R, B>
where
F: FnOnce(A) -> B + 'static,
{
Reader::new(move |r| f((self.run)(r)))
}
#[inline]
pub fn and_then<B: 'static, F>(self, f: F) -> Reader<R, B>
where
F: FnOnce(A) -> Reader<R, B> + 'static,
{
Reader::new(move |r| {
let a = (self.run)(r);
f(a).run(r)
})
}
#[inline]
pub fn then<B: 'static>(self, next: Reader<R, B>) -> Reader<R, B> {
Reader::new(move |r| {
let _ = (self.run)(r);
next.run(r)
})
}
}
#[inline]
pub fn reader_pure<R: 'static, A: 'static>(value: A) -> Reader<R, A> {
Reader::new(move |_| value)
}
#[inline]
pub fn ask<R: Clone + 'static>() -> Reader<R, R> {
Reader::new(|r: &R| r.clone())
}
#[inline]
pub fn asks<R: 'static, A: 'static, F>(f: F) -> Reader<R, A>
where
F: FnOnce(&R) -> A + 'static,
{
Reader::new(f)
}
pub fn local<R: Clone + 'static, A: 'static, M, C>(modifier: M, computation: C) -> Reader<R, A>
where
M: FnOnce(R) -> R + 'static,
C: FnOnce() -> Reader<R, A> + 'static,
{
Reader::new(move |r: &R| {
let local_r = modifier(r.clone());
computation().run(&local_r)
})
}
pub struct Dependencies<D> {
deps: Arc<D>,
}
impl<D> Dependencies<D> {
pub fn new(deps: D) -> Self {
Dependencies {
deps: Arc::new(deps),
}
}
pub fn run<A, F>(&self, f: F) -> A
where
F: FnOnce(&D) -> A,
{
f(&self.deps)
}
pub fn get(&self) -> &D {
&self.deps
}
pub fn map<B, F>(&self, f: F) -> B
where
F: FnOnce(&D) -> B,
{
f(&self.deps)
}
}
impl<D> Clone for Dependencies<D> {
fn clone(&self) -> Self {
Dependencies {
deps: Arc::clone(&self.deps),
}
}
}
pub trait HasDependencies {
type Deps;
fn deps(&self) -> &Self::Deps;
}
pub struct ConfigBuilder<C> {
config: C,
errors: Vec<alloc::string::String>,
}
impl<C: Default> ConfigBuilder<C> {
pub fn new() -> Self {
ConfigBuilder {
config: C::default(),
errors: Vec::new(),
}
}
}
impl<C> ConfigBuilder<C> {
pub fn from(config: C) -> Self {
ConfigBuilder {
config,
errors: Vec::new(),
}
}
pub fn with<F>(mut self, modifier: F) -> Self
where
F: FnOnce(&mut C),
{
modifier(&mut self.config);
self
}
pub fn validate<F>(mut self, predicate: F, error: &str) -> Self
where
F: FnOnce(&C) -> bool,
{
if !predicate(&self.config) {
self.errors.push(alloc::string::String::from(error));
}
self
}
pub fn build(self) -> Result<C, Vec<alloc::string::String>> {
if self.errors.is_empty() {
Ok(self.config)
} else {
Err(self.errors)
}
}
pub fn build_or_panic(self) -> C {
if self.errors.is_empty() {
self.config
} else {
panic!("Configuration validation failed: {:?}", self.errors)
}
}
}
impl<C: Default> Default for ConfigBuilder<C> {
fn default() -> Self {
Self::new()
}
}
pub struct LayeredEnv<E> {
layers: Vec<E>,
}
impl<E> LayeredEnv<E> {
pub fn new() -> Self {
LayeredEnv { layers: Vec::new() }
}
pub fn push(&mut self, layer: E) {
self.layers.push(layer);
}
pub fn pop(&mut self) -> Option<E> {
self.layers.pop()
}
pub fn top(&self) -> Option<&E> {
self.layers.last()
}
pub fn with_layer<A, F>(&mut self, layer: E, f: F) -> A
where
F: FnOnce(&Self) -> A,
{
self.push(layer);
let result = f(self);
self.pop();
result
}
pub fn layers(&self) -> &[E] {
&self.layers
}
}
impl<E> Default for LayeredEnv<E> {
fn default() -> Self {
Self::new()
}
}
impl<E: Clone> LayeredEnv<E> {
pub fn merge<F>(&self, combiner: F) -> Option<E>
where
F: Fn(E, &E) -> E,
{
if self.layers.is_empty() {
return None;
}
let mut result = self.layers[0].clone();
for layer in &self.layers[1..] {
result = combiner(result, layer);
}
Some(result)
}
}
pub struct LocalEnv<E> {
cell: RefCell<Option<E>>,
}
impl<E> LocalEnv<E> {
pub const fn new() -> Self {
LocalEnv {
cell: RefCell::new(None),
}
}
pub fn run<A, F>(&self, env: E, f: F) -> A
where
F: FnOnce() -> A,
{
*self.cell.borrow_mut() = Some(env);
let result = f();
*self.cell.borrow_mut() = None;
result
}
pub fn with<A, F>(&self, f: F) -> A
where
F: FnOnce(&E) -> A,
{
let borrow = self.cell.borrow();
f(borrow.as_ref().expect("LocalEnv: not in run context"))
}
}
impl<E> Default for LocalEnv<E> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_run_with_config() {
let result = run_with_config(&42, |config| *config * 2);
assert_eq!(result, 84);
}
#[test]
fn test_run_with_local() {
let result = run_with_local(&42, |x| x + 10, |config| *config * 2);
assert_eq!(result, 104);
}
#[test]
fn test_reader_monad() {
let comp = ask::<i32>().map(|x| x * 2);
let result = comp.run(&21);
assert_eq!(result, 42);
}
#[test]
fn test_asks() {
let comp = asks::<i32, _, _>(|x| x + 1);
let result = comp.run(&41);
assert_eq!(result, 42);
}
#[test]
fn test_reader_and_then() {
let comp = ask::<i32>().and_then(|x| asks(move |y: &i32| x + y));
let result = comp.run(&21);
assert_eq!(result, 42); }
#[test]
fn test_dependencies() {
struct Deps {
value: i32,
}
let deps = Dependencies::new(Deps { value: 42 });
let result = deps.run(|d| d.value * 2);
assert_eq!(result, 84);
}
#[test]
fn test_config_builder() {
#[derive(Default)]
struct Config {
timeout: u64,
}
let config = ConfigBuilder::<Config>::new()
.with(|c| c.timeout = 1000)
.validate(|c| c.timeout > 0, "timeout must be positive")
.build()
.expect("ConfigBuilder with valid timeout should build successfully");
assert_eq!(config.timeout, 1000);
}
#[test]
fn test_config_builder_validation_error() {
#[derive(Default)]
struct Config {
timeout: u64,
}
let result = ConfigBuilder::<Config>::new()
.validate(|c| c.timeout > 0, "timeout must be positive")
.build();
assert!(result.is_err());
}
#[test]
fn test_layered_env() {
let mut env = LayeredEnv::new();
env.push(10);
env.push(20);
let result = env.with_layer(30, |e| {
*e.top()
.expect("layer stack should have top element while inside with_layer")
});
assert_eq!(result, 30);
assert_eq!(
*env.top()
.expect("original layer should remain after with_layer exits"),
20
);
}
#[test]
fn test_local_env() {
let env: LocalEnv<i32> = LocalEnv::new();
let result = env.run(42, || env.with(|x| *x * 2));
assert_eq!(result, 84);
}
}