shtola

ssg in rust
git clone https://tilde.team/~marisa/repo/shtola.git
Log | Files | Refs | LICENSE

commit 29162efd49e965e5e9f38e3d10d338e2b0100e89
parent 121a32e8f9e2359a129807b4d6b26f2f907d8fe3
Author: marisa <mokou@posteo.de>
Date:   Thu, 31 Oct 2019 17:05:19 +0100

Document Ware

Diffstat:
Aware/README.md | 46++++++++++++++++++++++++++++++++++++++++++++++
Mware/src/lib.rs | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 135 insertions(+), 0 deletions(-)

diff --git a/ware/README.md b/ware/README.md @@ -0,0 +1,46 @@ +# Ware + +Immutable middleware chains. + +Ware allows you to create middleware chains that pass through and modify a value +as they go along. You can imagine a middleware chain as something like this: + +``` rust +let initial_value = 1; + +fn middleware_1(value: i32) -> i32 { + value + 1 +} + +fn middleware_2(value: i32) -> i32 { + value * 5 +} + +let result = middleware_2(middleware_1(initial_value)); +assert_eq!(result, 10); +``` + +Ware abstracts over this concept like such: + +``` rust +use ware::Ware; + +let mut middleware_chain: Ware<i32> = Ware::new(); + +middleware_chain.wrap(Box::new(|num| num + 1)); +middleware_chain.wrap(Box::new(|num| num * 5)); + +let result = middleware_chain.run(1); +assert_eq!(result, 10); +``` + +Ware provides a single-argument struct (e.g. `Ware<i32>`) and a dual-argument +struct (e.g. `Ware2<i32, String>`). + +Functions that get registered as middleware cannot directly modify their +variables, as they have to by of the `Fn` trait. I would +recommend using immutable data structures that are efficient when duplicating values. + +## Documentation + +The documentation is available at https://docs.rs/ware. diff --git a/ware/src/lib.rs b/ware/src/lib.rs @@ -1,36 +1,125 @@ +//! Ware provides immutable middleware abstractions. Basically, it means that +//! you can pass one variable through a series of functions that all have the +//! ability to modify this variable, therefore sending this modified version of +//! it further down the chain. +//! +//! Ware is used like this: +//! +//! ``` +//! use ware::Ware; +//! +//! fn main() { +//! let mut chain: Ware<i32> = Ware::new(); +//! chain.wrap(Box::new(|num| num * 10)); +//! chain.wrap(Box::new(|num| num - 2)); +//! let result = chain.run(5); +//! assert_eq!(result, 48); +//! } +//! ``` +//! +//! Ware also provides a version of itself that can pass through two variables +//! at once, since Rust doesn't support variadic functions (functions that can +//! have multiple numbers of arguments). In middleware functions for these, +//! you return a 2-tuple instead of a single value: +//! +//! ``` +//! use ware::Ware2; +//! +//! fn main() { +//! let mut chain: Ware2<i32, i32> = Ware2::new(); +//! chain.wrap(Box::new(|num1, num2| (num1 - 4, num2 + 3))); +//! let (res1, res2) = chain.run(10, 10); +//! assert_eq!(res1, 6); +//! assert_eq!(res2, 13); +//! } +//! ``` + +/// A middleware chain that can pass through one argument. pub struct Ware<R> { + /// The internal list of middleware functions. pub fns: Vec<Box<dyn Fn(R) -> R>>, } impl<R> Ware<R> { + /// Create a new middleware chain with a given type. + /// + /// # Example + /// ``` + /// use ware::Ware; + /// let mut chain: Ware<String> = Ware::new(); + /// ``` pub fn new() -> Ware<R> { let vec: Vec<Box<dyn Fn(R) -> R>> = Vec::new(); Ware { fns: vec } } + /// Add a new middleware function to the internal function list. This function + /// must be of the `Fn` trait, take the specified type and return the same + /// specified type. It also has to be boxed for memory safety reasons. + /// + /// # Example + /// ``` + /// use ware::Ware; + /// let mut chain: Ware<String> = Ware::new(); + /// chain.wrap(Box::new(|st| { + /// let mut s = st.clone(); + /// s.push('a'); + /// s + /// })) + /// ``` pub fn wrap(&mut self, func: Box<dyn Fn(R) -> R>) { self.fns.push(func); } + /// Run the registered middleware functions with the given value to pass + /// through. Returns whatever the last registered middleware function + /// returns. pub fn run(&self, arg: R) -> R { self.fns.iter().fold(arg, |acc, func| func(acc)) } } +/// A middleware chain that can pass through two arguments. pub struct Ware2<R, S> { + /// The internal list of middleware functions. pub fns: Vec<Box<dyn Fn(R, S) -> (R, S)>>, } impl<R, S> Ware2<R, S> { + /// Create a new middleware chain with the two given types. + /// + /// # Example + /// ``` + /// use ware::Ware2; + /// let mut chain: Ware2<String, i32> = Ware2::new(); + /// ``` pub fn new() -> Ware2<R, S> { let vec: Vec<Box<dyn Fn(R, S) -> (R, S)>> = Vec::new(); Ware2 { fns: vec } } + /// Add a new middleware function to the internal function list. This function + /// must be of the `Fn` trait, take the specified types in order and return + /// a tuple of the same specified types. It also has to be boxed for memory + /// safety reasons. + /// + /// # Example + /// ``` + /// use ware::Ware2; + /// let mut chain: Ware2<String, i32> = Ware2::new(); + /// chain.wrap(Box::new(|st, num| { + /// let mut s = st.clone(); + /// s.push('a'); + /// (s, num + 1) + /// })) + /// ``` pub fn wrap(&mut self, func: Box<dyn Fn(R, S) -> (R, S)>) { self.fns.push(func); } + /// Run the registered middleware functions with the given value to pass + /// through. Returns whatever the last registered middleware function + /// returns. pub fn run (&self, arg1: R, arg2: S) -> (R, S) { self.fns.iter().fold((arg1, arg2), |acc, func| func(acc.0, acc.1)) }