commit 29162efd49e965e5e9f38e3d10d338e2b0100e89
parent 121a32e8f9e2359a129807b4d6b26f2f907d8fe3
Author: marisa <mokou@posteo.de>
Date: Thu, 31 Oct 2019 17:05:19 +0100
Document Ware
Diffstat:
A | ware/README.md | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
M | ware/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))
}