use core::marker::PhantomData;
pub trait FastMonoid: Clone {
fn empty() -> Self;
fn combine(self, other: Self) -> Self;
}
impl FastMonoid for alloc::string::String {
#[inline(always)]
fn empty() -> Self {
alloc::string::String::new()
}
#[inline(always)]
fn combine(mut self, other: Self) -> Self {
self.push_str(&other);
self
}
}
impl<T: Clone> FastMonoid for alloc::vec::Vec<T> {
#[inline(always)]
fn empty() -> Self {
alloc::vec::Vec::new()
}
#[inline(always)]
fn combine(mut self, other: Self) -> Self {
self.extend(other);
self
}
}
pub trait WriterOp<W: FastMonoid> {
type Output;
fn run_writer(self) -> (Self::Output, W);
}
pub struct PureWriter<W, A>(pub A, PhantomData<W>);
impl<W: FastMonoid, A> WriterOp<W> for PureWriter<W, A> {
type Output = A;
#[inline(always)]
fn run_writer(self) -> (A, W) {
(self.0, W::empty())
}
}
pub struct TellWriter<W>(pub W);
impl<W: FastMonoid> WriterOp<W> for TellWriter<W> {
type Output = ();
#[inline(always)]
fn run_writer(self) -> ((), W) {
((), self.0)
}
}
pub struct MapWriter<Op, F>(pub Op, pub F);
impl<W: FastMonoid, Op: WriterOp<W>, B, F: FnOnce(Op::Output) -> B> WriterOp<W>
for MapWriter<Op, F>
{
type Output = B;
#[inline(always)]
fn run_writer(self) -> (B, W) {
let (a, w) = self.0.run_writer();
((self.1)(a), w)
}
}
pub struct AndThenWriter<Op1, F>(pub Op1, pub F);
impl<W: FastMonoid, Op1: WriterOp<W>, Op2: WriterOp<W>, F: FnOnce(Op1::Output) -> Op2> WriterOp<W>
for AndThenWriter<Op1, F>
{
type Output = Op2::Output;
#[inline(always)]
fn run_writer(self) -> (Op2::Output, W) {
let (a, w1) = self.0.run_writer();
let (b, w2) = (self.1)(a).run_writer();
(b, w1.combine(w2))
}
}
pub struct ThenWriter<Op1, Op2>(pub Op1, pub Op2);
impl<W: FastMonoid, Op1: WriterOp<W>, Op2: WriterOp<W>> WriterOp<W> for ThenWriter<Op1, Op2> {
type Output = Op2::Output;
#[inline(always)]
fn run_writer(self) -> (Op2::Output, W) {
let (_, w1) = self.0.run_writer();
let (b, w2) = self.1.run_writer();
(b, w1.combine(w2))
}
}
pub struct ListenWriter<Op>(pub Op);
impl<W: FastMonoid, Op: WriterOp<W>> WriterOp<W> for ListenWriter<Op> {
type Output = (Op::Output, W);
#[inline(always)]
fn run_writer(self) -> ((Op::Output, W), W) {
let (a, w) = self.0.run_writer();
((a, w.clone()), w)
}
}
pub struct CensorWriter<Op, F>(pub Op, pub F);
impl<W: FastMonoid, Op: WriterOp<W>, F: FnOnce(W) -> W> WriterOp<W> for CensorWriter<Op, F> {
type Output = Op::Output;
#[inline(always)]
fn run_writer(self) -> (Op::Output, W) {
let (a, w) = self.0.run_writer();
(a, (self.1)(w))
}
}
pub trait WriterOpExt<W: FastMonoid>: WriterOp<W> + Sized {
#[inline(always)]
fn map_writer<B, F: FnOnce(Self::Output) -> B>(self, f: F) -> MapWriter<Self, F> {
MapWriter(self, f)
}
#[inline(always)]
fn and_then_writer<Op2: WriterOp<W>, F: FnOnce(Self::Output) -> Op2>(
self,
f: F,
) -> AndThenWriter<Self, F> {
AndThenWriter(self, f)
}
#[inline(always)]
fn then_writer<Op2: WriterOp<W>>(self, next: Op2) -> ThenWriter<Self, Op2> {
ThenWriter(self, next)
}
#[inline(always)]
fn listen_writer(self) -> ListenWriter<Self> {
ListenWriter(self)
}
#[inline(always)]
fn censor_writer<F: FnOnce(W) -> W>(self, f: F) -> CensorWriter<Self, F> {
CensorWriter(self, f)
}
}
impl<W: FastMonoid, Op: WriterOp<W>> WriterOpExt<W> for Op {}
#[inline(always)]
pub fn pure_writer<W: FastMonoid, A>(a: A) -> PureWriter<W, A> {
PureWriter(a, PhantomData)
}
#[inline(always)]
pub fn tell_writer<W: FastMonoid>(w: W) -> TellWriter<W> {
TellWriter(w)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
#[test]
fn test_writer_op_pure() {
let op = pure_writer::<String, _>(42);
let (result, log) = op.run_writer();
assert_eq!(result, 42);
assert_eq!(log, "");
}
#[test]
fn test_writer_op_tell() {
let op = tell_writer("Hello".to_string());
let ((), log) = op.run_writer();
assert_eq!(log, "Hello");
}
#[test]
fn test_writer_op_map() {
let op = pure_writer::<String, _>(21).map_writer(|x| x * 2);
let (result, log) = op.run_writer();
assert_eq!(result, 42);
assert_eq!(log, "");
}
#[test]
fn test_writer_op_and_then() {
let op = tell_writer("Hello, ".to_string())
.and_then_writer(|()| tell_writer("World!".to_string()));
let ((), log) = op.run_writer();
assert_eq!(log, "Hello, World!");
}
#[test]
fn test_writer_op_then() {
let op = tell_writer("Hello, ".to_string()).then_writer(tell_writer("World!".to_string()));
let ((), log) = op.run_writer();
assert_eq!(log, "Hello, World!");
}
#[test]
fn test_writer_op_listen() {
let op = tell_writer("Log".to_string())
.and_then_writer(|()| pure_writer::<String, _>(42))
.listen_writer();
let ((result, inner_log), log) = op.run_writer();
assert_eq!(result, 42);
assert_eq!(inner_log, "Log");
assert_eq!(log, "Log");
}
#[test]
fn test_writer_op_censor() {
let op = tell_writer("hello".to_string()).censor_writer(|s| s.to_uppercase());
let ((), log) = op.run_writer();
assert_eq!(log, "HELLO");
}
#[test]
fn test_writer_op_vec() {
let op = tell_writer(vec![1, 2]).and_then_writer(|()| tell_writer(vec![3, 4]));
let ((), log): ((), Vec<i32>) = op.run_writer();
assert_eq!(log, vec![1, 2, 3, 4]);
}
#[test]
fn test_writer_op_long_chain() {
let op = tell_writer(vec![1i32])
.then_writer(tell_writer(vec![2]))
.then_writer(tell_writer(vec![3]))
.then_writer(tell_writer(vec![4]))
.then_writer(tell_writer(vec![5]))
.then_writer(tell_writer(vec![6]))
.then_writer(tell_writer(vec![7]))
.then_writer(tell_writer(vec![8]))
.then_writer(tell_writer(vec![9]))
.then_writer(tell_writer(vec![10]));
let ((), log): ((), Vec<i32>) = op.run_writer();
assert_eq!(log, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
}
}