use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Deref;
pub fn pipe<T>(value: T, fns: Vec<fn(T) -> T>) -> T {
fns.into_iter().fold(value, |v, f| f(v))
}
pub fn compose<A, B, C>(f: impl Fn(B) -> C, g: impl Fn(A) -> B) -> impl Fn(A) -> C {
move |x| f(g(x))
}
pub fn tap<T>(value: T, f: impl FnOnce(&T)) -> T {
f(&value);
value
}
pub fn apply<T, U>(value: T, f: impl FnOnce(T) -> U) -> U {
f(value)
}
pub fn or_default<T: Default>(opt: Option<T>) -> T {
opt.unwrap_or_default()
}
pub fn retry<T, E>(times: usize, f: impl FnMut() -> Result<T, E>) -> Result<T, E> {
let mut f = f;
let mut last_err = None;
for _ in 0..times {
match f() {
ok @ Ok(_) => return ok,
Err(e) => last_err = Some(e),
}
}
Err(last_err.unwrap())
}
#[cfg(feature = "random")]
pub fn shuffle<T: Clone>(arr: &[T]) -> Vec<T> {
use rand::seq::SliceRandom;
let mut rng = rand::thread_rng();
let mut out: Vec<T> = arr.to_vec();
out.shuffle(&mut rng);
out
}
#[derive(Debug)]
pub struct Lazy<T, F = fn() -> T> {
cell: once_cell::sync::Lazy<T, F>,
}
impl<T, F: FnOnce() -> T> Lazy<T, F> {
pub fn new(init: F) -> Self {
Self {
cell: once_cell::sync::Lazy::new(init),
}
}
pub fn get(&self) -> &T {
Deref::deref(&self.cell)
}
}
unsafe impl<T, F: FnOnce() -> T + Send + Sync> Send for Lazy<T, F> {}
unsafe impl<T, F: FnOnce() -> T + Send + Sync> Sync for Lazy<T, F> {}
pub fn memoize<K, R>(f: impl Fn(K) -> R) -> impl Fn(K) -> R
where
K: Hash + Eq + Clone,
R: Clone,
{
let cache = std::cell::RefCell::new(HashMap::<K, R>::new());
move |arg: K| {
let arg = arg.clone();
if let Some(result) = cache.borrow().get(&arg) {
return result.clone();
}
let result = f(arg.clone());
cache.borrow_mut().insert(arg, result.clone());
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pipe() {
let result = pipe(1, vec![|x| x + 1, |x| x * 2]);
assert_eq!(result, 4);
}
#[test]
fn test_compose() {
let f = compose(|x: i32| x + 1, |x: i32| x * 2);
assert_eq!(f(3), 7);
}
#[test]
fn test_tap() {
let mut seen = false;
let _ = tap(42, |v| seen = *v == 42);
assert!(seen);
}
#[test]
fn test_apply() {
let result = apply(5, |x| x * 2);
assert_eq!(result, 10);
}
#[test]
fn test_or_default() {
assert_eq!(or_default(Some(42)), 42);
assert_eq!(or_default(None::<i32>), 0);
}
#[test]
fn test_retry_success() {
let result = retry(3, || Ok::<i32, i32>(42));
assert_eq!(result.unwrap(), 42);
}
#[test]
fn test_retry_eventual() {
let mut attempts = 0;
let result = retry(3, &mut || {
attempts += 1;
if attempts < 3 {
Err(1)
} else {
Ok(42)
}
});
assert_eq!(result.unwrap(), 42);
assert_eq!(attempts, 3);
}
#[test]
fn test_retry_failure() {
let result = retry(2, || Err::<i32, i32>(42));
assert!(result.is_err());
}
#[test]
fn test_lazy() {
let lazy: Lazy<i32, fn() -> i32> = Lazy::new(|| 42);
assert_eq!(*lazy.get(), 42);
assert_eq!(*lazy.get(), 42);
}
#[test]
fn test_memoize() {
let count_ptr = std::cell::Cell::new(0);
let memoized = memoize(move |x: i32| {
count_ptr.set(count_ptr.get() + 1);
x * 2
});
assert_eq!(memoized(5), 10);
assert_eq!(memoized(5), 10);
let _ = count_ptr;
}
}