Skip to main content

architecturekit/
decider.rs

1use std::{any, fmt, future::Future, pin::Pin};
2
3use crate::State;
4
5#[cfg(test)]
6mod tests;
7
8type Decision<S, D> = Result<Vec<<S as State>::Event>, D>;
9type Decide<C, S, D> =
10    dyn Fn(C, S) -> Pin<Box<dyn Future<Output = Decision<S, D>> + Send>> + Send + Sync;
11
12/// A decision on a command, given the state of its subject: the events to
13/// write, or an error if the command is refused.
14///
15/// Create one from a function that receives the command and the state, and
16/// returns the events in a future, so that it can load what else it needs to
17/// decide, such as the state of another subject:
18///
19/// ```
20/// # use architecturekit::{Event, Events, State};
21/// # use serde::{Deserialize, Serialize};
22/// # #[derive(Serialize, Deserialize, Event)]
23/// # #[event(type = "io.eventsourcingdb.library.book-borrowed")]
24/// # struct BookBorrowed { reader: String }
25/// # #[derive(Events)]
26/// # enum BookEvent { Borrowed(BookBorrowed) }
27/// # #[derive(Clone, Default)]
28/// # struct Book { is_borrowed: bool }
29/// # impl State for Book {
30/// #     type Event = BookEvent;
31/// #     fn evolve(self, event: &BookEvent) -> Self { Book { is_borrowed: true } }
32/// # }
33/// # #[derive(Clone)]
34/// # struct BorrowBook { book_id: String, reader: String }
35/// use architecturekit::{Decider, DomainError};
36///
37/// let borrow_book = Decider::new(|cmd: BorrowBook, book: Book| async move {
38///     if book.is_borrowed {
39///         return Err(DomainError::new(format!("book {} is already borrowed", cmd.book_id)));
40///     }
41///
42///     Ok(vec![BookEvent::Borrowed(BookBorrowed { reader: cmd.reader })])
43/// });
44/// ```
45///
46/// The types of the command, the state and the error come from the function,
47/// so that none of them has to be named. A decider that never refuses names
48/// its error type, as in `Decider::<_, _, DomainError>::new(…)`.
49///
50/// The function takes the command and the state rather than references to
51/// them, so that its future can be sent across threads, which a web server
52/// needs.
53pub struct Decider<C, S: State, D> {
54    decide: Box<Decide<C, S, D>>,
55}
56
57impl<C, S: State, D> Decider<C, S, D> {
58    /// Creates a decider from a function that decides on a command, given the
59    /// state of its subject.
60    #[must_use]
61    pub fn new<F, Fut>(decide: F) -> Self
62    where
63        F: Fn(C, S) -> Fut + Send + Sync + 'static,
64        Fut: Future<Output = Decision<S, D>> + Send + 'static,
65    {
66        Self {
67            decide: Box::new(move |command, state| Box::pin(decide(command, state))),
68        }
69    }
70
71    /// Decides on a command, given the state of its subject, with the function
72    /// the decider was created with, and returns what that function returns.
73    ///
74    /// # Errors
75    ///
76    /// Fails with the error of the function, if it refuses the command.
77    pub async fn decide(&self, command: C, state: S) -> Decision<S, D> {
78        (self.decide)(command, state).await
79    }
80}
81
82impl<C, S: State, D> fmt::Debug for Decider<C, S, D> {
83    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
84        formatter
85            .debug_struct("Decider")
86            .field("command", &any::type_name::<C>())
87            .field("state", &any::type_name::<S>())
88            .field("error", &any::type_name::<D>())
89            .finish_non_exhaustive()
90    }
91}