use std::any::Any;
pub trait Stream<A>
where
A: Arguments,
{
type Output: 'static;
fn search(&mut self, args: A::Search<'_>) -> anyhow::Result<Self::Output>;
fn insert(&mut self, args: A::Insert<'_>) -> anyhow::Result<Self::Output>;
fn replace(&mut self, args: A::Replace<'_>) -> anyhow::Result<Self::Output>;
fn delete(&mut self, args: A::Delete<'_>) -> anyhow::Result<Self::Output>;
fn maintain(&mut self, args: A::Maintain<'_>) -> anyhow::Result<Self::Output>;
fn needs_maintenance(&mut self) -> bool;
}
pub trait Arguments: 'static {
type Search<'a>;
type Insert<'a>;
type Replace<'a>;
type Delete<'a>;
type Maintain<'a>;
}
pub trait Executor {
type Args: Arguments;
fn run_with<S, F, O>(&mut self, stream: &mut S, collect: F) -> anyhow::Result<()>
where
S: Stream<Self::Args, Output = O>,
O: 'static,
F: FnMut(O) -> anyhow::Result<()>;
fn run<S>(&mut self, stream: &mut S) -> anyhow::Result<Vec<S::Output>>
where
S: Stream<Self::Args>,
{
let mut outputs = Vec::new();
self.run_with(stream, |output| {
outputs.push(output);
Ok(())
})?;
Ok(outputs)
}
}
#[derive(Debug)]
pub struct AnyStream<'a, T>(&'a mut T);
impl<'a, T> AnyStream<'a, T> {
pub fn new(stream: &'a mut T) -> Self {
Self(stream)
}
}
fn boxed<T>(x: T) -> Box<dyn Any>
where
T: Any,
{
Box::new(x)
}
impl<A, T> Stream<A> for AnyStream<'_, T>
where
A: Arguments,
T: Stream<A>,
{
type Output = Box<dyn Any>;
fn search(&mut self, args: A::Search<'_>) -> anyhow::Result<Self::Output> {
self.0.search(args).map(boxed)
}
fn insert(&mut self, args: A::Insert<'_>) -> anyhow::Result<Self::Output> {
self.0.insert(args).map(boxed)
}
fn replace(&mut self, args: A::Replace<'_>) -> anyhow::Result<Self::Output> {
self.0.replace(args).map(boxed)
}
fn delete(&mut self, args: A::Delete<'_>) -> anyhow::Result<Self::Output> {
self.0.delete(args).map(boxed)
}
fn maintain(&mut self, args: A::Maintain<'_>) -> anyhow::Result<Self::Output> {
self.0.maintain(args).map(boxed)
}
fn needs_maintenance(&mut self) -> bool {
self.0.needs_maintenance()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Search;
struct Insert;
struct Replace;
struct Delete;
struct Maintain;
#[derive(Debug, PartialEq)]
enum Op {
Search,
Insert,
Replace,
Delete,
Maintain,
}
struct TestArgs;
impl Arguments for TestArgs {
type Search<'a> = Search;
type Insert<'a> = Insert;
type Replace<'a> = Replace;
type Delete<'a> = Delete;
type Maintain<'a> = Maintain;
}
struct TestStream {
needs_maintenance: bool,
}
impl TestStream {
fn new(needs_maintenance: bool) -> Self {
Self { needs_maintenance }
}
}
impl Stream<TestArgs> for TestStream {
type Output = Op;
fn search(&mut self, _args: Search) -> anyhow::Result<Self::Output> {
Ok(Op::Search)
}
fn insert(&mut self, _args: Insert) -> anyhow::Result<Self::Output> {
Ok(Op::Insert)
}
fn replace(&mut self, _args: Replace) -> anyhow::Result<Self::Output> {
Ok(Op::Replace)
}
fn delete(&mut self, _args: Delete) -> anyhow::Result<Self::Output> {
Ok(Op::Delete)
}
fn maintain(&mut self, _args: Maintain) -> anyhow::Result<Self::Output> {
Ok(Op::Maintain)
}
fn needs_maintenance(&mut self) -> bool {
self.needs_maintenance
}
}
struct TestExecutor;
impl Executor for TestExecutor {
type Args = TestArgs;
fn run_with<S, F, O>(&mut self, stream: &mut S, mut collect: F) -> anyhow::Result<()>
where
S: Stream<Self::Args, Output = O>,
O: 'static,
F: FnMut(O) -> anyhow::Result<()>,
{
collect(stream.search(Search)?)?;
collect(stream.insert(Insert)?)?;
collect(stream.replace(Replace)?)?;
collect(stream.delete(Delete)?)?;
collect(stream.maintain(Maintain)?)?;
Ok(())
}
}
#[test]
fn test_executor_run() -> anyhow::Result<()> {
let mut stream = TestStream::new(false);
let mut executor = TestExecutor;
let outputs = executor.run(&mut stream)?;
assert_eq!(outputs.len(), 5);
assert!(matches!(outputs[0], Op::Search));
assert!(matches!(outputs[1], Op::Insert));
assert!(matches!(outputs[2], Op::Replace));
assert!(matches!(outputs[3], Op::Delete));
assert!(matches!(outputs[4], Op::Maintain));
Ok(())
}
#[test]
fn test_any_stream() {
let mut stream = TestStream::new(false);
let mut any_stream = AnyStream::new(&mut stream);
assert!(
!any_stream.needs_maintenance(),
"AnyStream should forward `needs_maintenance`"
);
assert_eq!(
any_stream
.search(Search)
.unwrap()
.downcast_ref::<Op>()
.unwrap(),
&Op::Search
);
assert_eq!(
any_stream
.insert(Insert)
.unwrap()
.downcast_ref::<Op>()
.unwrap(),
&Op::Insert
);
assert_eq!(
any_stream
.replace(Replace)
.unwrap()
.downcast_ref::<Op>()
.unwrap(),
&Op::Replace
);
assert_eq!(
any_stream
.delete(Delete)
.unwrap()
.downcast_ref::<Op>()
.unwrap(),
&Op::Delete
);
assert_eq!(
any_stream
.maintain(Maintain)
.unwrap()
.downcast_ref::<Op>()
.unwrap(),
&Op::Maintain
);
let mut stream = TestStream::new(true);
let mut any_stream = AnyStream::new(&mut stream);
assert!(
any_stream.needs_maintenance(),
"AnyStream should forward `needs_maintenance`"
);
}
}