Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@ jobs:
- name: Build
run: cargo build --verbose

- name: Run tests
- name: Run tests (not-send-futures)
run: cargo test --features not-send-futures --verbose

- name: Run tests (default)
run: cargo test --verbose
111 changes: 111 additions & 0 deletions src/decider.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "not-send-futures")]
use std::rc::Rc;
#[cfg(not(feature = "not-send-futures"))]
use std::sync::Arc;

use crate::{
Expand Down Expand Up @@ -165,6 +168,7 @@ pub struct Decider<'a, C: 'a, S: 'a, E: 'a, Error: 'a = ()> {
impl<'a, C, S, E, Error> Decider<'a, C, S, E, Error> {
/// Maps the Decider over the S/State type parameter.
/// Creates a new instance of [Decider]`<C, S2, E, Error>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_state<S2, F1, F2>(self, f1: F1, f2: F2) -> Decider<'a, C, S2, E, Error>
where
F1: Fn(&S2) -> S + Send + Sync + 'a,
Expand Down Expand Up @@ -198,8 +202,44 @@ impl<'a, C, S, E, Error> Decider<'a, C, S, E, Error> {
}
}

/// Maps the Decider over the S/State type parameter.
/// Creates a new instance of [Decider]`<C, S2, E, Error>`.
#[cfg(feature = "not-send-futures")]
pub fn map_state<S2, F1, F2>(self, f1: F1, f2: F2) -> Decider<'a, C, S2, E, Error>
where
F1: Fn(&S2) -> S + 'a,
F2: Fn(&S) -> S2 + 'a,
{
let f1 = Rc::new(f1);
let f2 = Rc::new(f2);

let new_decide = {
let f1 = Rc::clone(&f1);
Box::new(move |c: &C, s2: &S2| {
let s = f1(s2);
(self.decide)(c, &s)
})
};

let new_evolve = {
let f2 = Rc::clone(&f2);
Box::new(move |s2: &S2, e: &E| {
let s = f1(s2);
f2(&(self.evolve)(&s, e))
})
};

let new_initial_state = { Box::new(move || f2(&(self.initial_state)())) };

Decider {
decide: new_decide,
evolve: new_evolve,
initial_state: new_initial_state,
}
}
/// Maps the Decider over the E/Event type parameter.
/// Creates a new instance of [Decider]`<C, S, E2, Error>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_event<E2, F1, F2>(self, f1: F1, f2: F2) -> Decider<'a, C, S, E2, Error>
where
F1: Fn(&E2) -> E + Send + Sync + 'a,
Expand All @@ -223,8 +263,35 @@ impl<'a, C, S, E, Error> Decider<'a, C, S, E, Error> {
}
}

/// Maps the Decider over the E/Event type parameter.
/// Creates a new instance of [Decider]`<C, S, E2, Error>`.
#[cfg(feature = "not-send-futures")]
pub fn map_event<E2, F1, F2>(self, f1: F1, f2: F2) -> Decider<'a, C, S, E2, Error>
where
F1: Fn(&E2) -> E + 'a,
F2: Fn(&E) -> E2 + 'a,
{
let new_decide = Box::new(move |c: &C, s: &S| {
(self.decide)(c, s).map(|result| result.into_iter().map(|e: E| f2(&e)).collect())
});

let new_evolve = Box::new(move |s: &S, e2: &E2| {
let e = f1(e2);
(self.evolve)(s, &e)
});

let new_initial_state = Box::new(move || (self.initial_state)());

Decider {
decide: new_decide,
evolve: new_evolve,
initial_state: new_initial_state,
}
}

/// Maps the Decider over the C/Command type parameter.
/// Creates a new instance of [Decider]`<C2, S, E, Error>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_command<C2, F>(self, f: F) -> Decider<'a, C2, S, E, Error>
where
F: Fn(&C2) -> C + Send + Sync + 'a,
Expand All @@ -245,8 +312,32 @@ impl<'a, C, S, E, Error> Decider<'a, C, S, E, Error> {
}
}

/// Maps the Decider over the C/Command type parameter.
/// Creates a new instance of [Decider]`<C2, S, E, Error>`.
#[cfg(feature = "not-send-futures")]
pub fn map_command<C2, F>(self, f: F) -> Decider<'a, C2, S, E, Error>
where
F: Fn(&C2) -> C + 'a,
{
let new_decide = Box::new(move |c2: &C2, s: &S| {
let c = f(c2);
(self.decide)(&c, s)
});

let new_evolve = Box::new(move |s: &S, e: &E| (self.evolve)(s, e));

let new_initial_state = Box::new(move || (self.initial_state)());

Decider {
decide: new_decide,
evolve: new_evolve,
initial_state: new_initial_state,
}
}

/// Maps the Decider over the Error type parameter.
/// Creates a new instance of [Decider]`<C, S, E, Error2>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_error<Error2, F>(self, f: F) -> Decider<'a, C, S, E, Error2>
where
F: Fn(&Error) -> Error2 + Send + Sync + 'a,
Expand All @@ -264,6 +355,26 @@ impl<'a, C, S, E, Error> Decider<'a, C, S, E, Error> {
}
}

/// Maps the Decider over the Error type parameter.
/// Creates a new instance of [Decider]`<C, S, E, Error2>`.
#[cfg(feature = "not-send-futures")]
pub fn map_error<Error2, F>(self, f: F) -> Decider<'a, C, S, E, Error2>
where
F: Fn(&Error) -> Error2 + 'a,
{
let new_decide = Box::new(move |c: &C, s: &S| (self.decide)(c, s).map_err(|e| f(&e)));

let new_evolve = Box::new(move |s: &S, e: &E| (self.evolve)(s, e));

let new_initial_state = Box::new(move || (self.initial_state)());

Decider {
decide: new_decide,
evolve: new_evolve,
initial_state: new_initial_state,
}
}

/// Combines two deciders into one bigger decider
/// Creates a new instance of a Decider by combining two deciders of type `C`, `S`, `E` and `C2`, `S2`, `E2` into a new decider of type `Sum<C, C2>`, `(S, S2)`, `Sum<E, E2>`
#[allow(clippy::type_complexity)]
Expand Down
17 changes: 17 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,15 +328,32 @@ pub mod specification;
pub mod view;

/// The [DecideFunction] function is used to decide which events to produce based on the command and the current state.
#[cfg(not(feature = "not-send-futures"))]
pub type DecideFunction<'a, C, S, E, Error> =
Box<dyn Fn(&C, &S) -> Result<Vec<E>, Error> + 'a + Send + Sync>;
/// The [EvolveFunction] function is used to evolve the state based on the current state and the event.
#[cfg(not(feature = "not-send-futures"))]
pub type EvolveFunction<'a, S, E> = Box<dyn Fn(&S, &E) -> S + 'a + Send + Sync>;
/// The [InitialStateFunction] function is used to produce the initial state.
#[cfg(not(feature = "not-send-futures"))]
pub type InitialStateFunction<'a, S> = Box<dyn Fn() -> S + 'a + Send + Sync>;
/// The [ReactFunction] function is used to decide what actions/A to execute next based on the action result/AR.
#[cfg(not(feature = "not-send-futures"))]
pub type ReactFunction<'a, AR, A> = Box<dyn Fn(&AR) -> Vec<A> + 'a + Send + Sync>;

/// The [DecideFunction] function is used to decide which events to produce based on the command and the current state.
#[cfg(feature = "not-send-futures")]
pub type DecideFunction<'a, C, S, E, Error> = Box<dyn Fn(&C, &S) -> Result<Vec<E>, Error> + 'a>;
/// The [EvolveFunction] function is used to evolve the state based on the current state and the event.
#[cfg(feature = "not-send-futures")]
pub type EvolveFunction<'a, S, E> = Box<dyn Fn(&S, &E) -> S + 'a>;
/// The [InitialStateFunction] function is used to produce the initial state.
#[cfg(feature = "not-send-futures")]
pub type InitialStateFunction<'a, S> = Box<dyn Fn() -> S + 'a>;
/// The [ReactFunction] function is used to decide what actions/A to execute next based on the action result/AR.
#[cfg(feature = "not-send-futures")]
pub type ReactFunction<'a, AR, A> = Box<dyn Fn(&AR) -> Vec<A> + 'a>;

/// Generic Combined/Sum Enum of two variants
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum Sum<A, B> {
Expand Down
32 changes: 32 additions & 0 deletions src/saga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub struct Saga<'a, AR: 'a, A: 'a> {
impl<'a, AR, A> Saga<'a, AR, A> {
/// Maps the Saga over the A/Action type parameter.
/// Creates a new instance of [Saga]`<AR, A2>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_action<A2, F>(self, f: F) -> Saga<'a, AR, A2>
where
F: Fn(&A) -> A2 + Send + Sync + 'a,
Expand All @@ -101,8 +102,24 @@ impl<'a, AR, A> Saga<'a, AR, A> {
Saga { react: new_react }
}

/// Maps the Saga over the A/Action type parameter.
/// Creates a new instance of [Saga]`<AR, A2>`.
#[cfg(feature = "not-send-futures")]
pub fn map_action<A2, F>(self, f: F) -> Saga<'a, AR, A2>
where
F: Fn(&A) -> A2 + 'a,
{
let new_react = Box::new(move |ar: &AR| {
let a = (self.react)(ar);
a.into_iter().map(|a: A| f(&a)).collect()
});

Saga { react: new_react }
}

/// Maps the Saga over the AR/ActionResult type parameter.
/// Creates a new instance of [Saga]`<AR2, A>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_action_result<AR2, F>(self, f: F) -> Saga<'a, AR2, A>
where
F: Fn(&AR2) -> AR + Send + Sync + 'a,
Expand All @@ -115,6 +132,21 @@ impl<'a, AR, A> Saga<'a, AR, A> {
Saga { react: new_react }
}

/// Maps the Saga over the AR/ActionResult type parameter.
/// Creates a new instance of [Saga]`<AR2, A>`.
#[cfg(feature = "not-send-futures")]
pub fn map_action_result<AR2, F>(self, f: F) -> Saga<'a, AR2, A>
where
F: Fn(&AR2) -> AR + 'a,
{
let new_react = Box::new(move |ar2: &AR2| {
let ar = f(ar2);
(self.react)(&ar)
});

Saga { react: new_react }
}

/// Combines two sagas into one.
/// Creates a new instance of a Saga by combining two sagas of type `AR`, `A` and `AR2`, `A2` into a new saga of type `Sum<AR, AR2>`, `Sum<A2, A>`
#[deprecated(
Expand Down
52 changes: 52 additions & 0 deletions src/view.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "not-send-futures")]
use std::rc::Rc;
#[cfg(not(feature = "not-send-futures"))]
use std::sync::Arc;

use crate::{EvolveFunction, InitialStateFunction, Sum, View3, View4, View5, View6};
Expand Down Expand Up @@ -90,6 +93,7 @@ pub struct View<'a, S: 'a, E: 'a> {
impl<'a, S, E> View<'a, S, E> {
/// Maps the View over the S/State type parameter.
/// Creates a new instance of [View]`<S2, E>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_state<S2, F1, F2>(self, f1: F1, f2: F2) -> View<'a, S2, E>
where
F1: Fn(&S2) -> S + Send + Sync + 'a,
Expand All @@ -114,8 +118,36 @@ impl<'a, S, E> View<'a, S, E> {
}
}

/// Maps the View over the S/State type parameter.
/// Creates a new instance of [View]`<S2, E>`.
#[cfg(feature = "not-send-futures")]
pub fn map_state<S2, F1, F2>(self, f1: F1, f2: F2) -> View<'a, S2, E>
where
F1: Fn(&S2) -> S + 'a,
F2: Fn(&S) -> S2 + 'a,
{
let f1 = Rc::new(f1);
let f2 = Rc::new(f2);

let new_evolve = {
let f2 = Rc::clone(&f2);
Box::new(move |s2: &S2, e: &E| {
let s = f1(s2);
f2(&(self.evolve)(&s, e))
})
};

let new_initial_state = { Box::new(move || f2(&(self.initial_state)())) };

View {
evolve: new_evolve,
initial_state: new_initial_state,
}
}

/// Maps the View over the E/Event type parameter.
/// Creates a new instance of [View]`<S, E2>`.
#[cfg(not(feature = "not-send-futures"))]
pub fn map_event<E2, F>(self, f: F) -> View<'a, S, E2>
where
F: Fn(&E2) -> E + Send + Sync + 'a,
Expand All @@ -133,6 +165,26 @@ impl<'a, S, E> View<'a, S, E> {
}
}

/// Maps the View over the E/Event type parameter.
/// Creates a new instance of [View]`<S, E2>`.
#[cfg(feature = "not-send-futures")]
pub fn map_event<E2, F>(self, f: F) -> View<'a, S, E2>
where
F: Fn(&E2) -> E + 'a,
{
let new_evolve = Box::new(move |s: &S, e2: &E2| {
let e = f(e2);
(self.evolve)(s, &e)
});

let new_initial_state = Box::new(move || (self.initial_state)());

View {
evolve: new_evolve,
initial_state: new_initial_state,
}
}

/// Combines two views into one.
/// Creates a new instance of a View by combining two views of type `S`, `E` and `S2`, `E2` into a new view of type `(S, S2)`, `Sum<E, E2>`
/// Combines two views that operate on different event types (`E`` and `E2``) into a new view operating on `Sum<E, E2>`
Expand Down
2 changes: 2 additions & 0 deletions tests/aggregate_combined_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(feature = "not-send-futures"))]

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::thread;
Expand Down
2 changes: 2 additions & 0 deletions tests/aggregate_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(feature = "not-send-futures"))]

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};

Expand Down
2 changes: 2 additions & 0 deletions tests/materialized_view_merged_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(feature = "not-send-futures"))]

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
Expand Down
2 changes: 2 additions & 0 deletions tests/materialized_view_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(feature = "not-send-futures"))]

use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::thread;
Expand Down
2 changes: 2 additions & 0 deletions tests/saga_manager_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(feature = "not-send-futures"))]

use std::sync::Arc;
use std::thread;

Expand Down
Loading