pallas_machines/
lib.rs

1mod codec;
2mod payloads;
3pub mod primitives;
4
5use log::{debug, trace};
6use pallas_multiplexer::{Channel, Payload};
7use std::borrow::Borrow;
8use std::fmt::{Debug, Display};
9use std::sync::mpsc::Sender;
10
11pub use payloads::*;
12
13#[derive(Debug)]
14pub enum MachineError<State, Msg>
15where
16    State: Debug,
17    Msg: Debug,
18{
19    InvalidMsgForState(State, Msg),
20}
21
22impl<S, M> Display for MachineError<S, M>
23where
24    S: Debug,
25    M: Debug,
26{
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            MachineError::InvalidMsgForState(msg, state) => {
30                write!(
31                    f,
32                    "received invalid message ({:?}) for current state ({:?})",
33                    msg, state
34                )
35            }
36        }
37    }
38}
39
40impl<S, M> std::error::Error for MachineError<S, M>
41where
42    S: Debug,
43    M: Debug,
44{
45}
46
47#[derive(Debug)]
48pub enum CodecError {
49    BadLabel(u16),
50    UnexpectedCbor(&'static str),
51}
52
53impl std::error::Error for CodecError {}
54
55impl Display for CodecError {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        match self {
58            CodecError::BadLabel(label) => {
59                write!(f, "unknown message label: {}", label)
60            }
61            CodecError::UnexpectedCbor(msg) => {
62                write!(f, "unexpected cbor: {}", msg)
63            }
64        }
65    }
66}
67
68pub trait MachineOutput {
69    fn send_msg(&self, data: &impl EncodePayload) -> Result<(), Box<dyn std::error::Error>>;
70}
71
72impl MachineOutput for Sender<Payload> {
73    fn send_msg(&self, data: &impl EncodePayload) -> Result<(), Box<dyn std::error::Error>> {
74        let payload = to_payload(data.borrow())?;
75        self.send(payload)?;
76
77        Ok(())
78    }
79}
80
81pub type Transition<T> = Result<T, Box<dyn std::error::Error>>;
82
83pub trait Agent: Sized {
84    type Message: DecodePayload + Debug;
85
86    fn is_done(&self) -> bool;
87    fn has_agency(&self) -> bool;
88    fn send_next(self, tx: &impl MachineOutput) -> Transition<Self>;
89    fn receive_next(self, msg: Self::Message) -> Transition<Self>;
90}
91
92pub fn run_agent<T: Agent + Debug>(
93    agent: T,
94    channel: &mut Channel,
95) -> Result<T, Box<dyn std::error::Error>> {
96    let Channel(tx, rx) = channel;
97
98    let mut input = PayloadDeconstructor {
99        rx,
100        remaining: Vec::new(),
101    };
102
103    let mut agent = agent;
104
105    while !agent.is_done() {
106        debug!("evaluating agent {:?}", agent);
107
108        match agent.has_agency() {
109            true => {
110                agent = agent.send_next(tx)?;
111            }
112            false => {
113                let msg = input.consume_next_message::<T::Message>()?;
114                trace!("procesing inbound msg: {:?}", msg);
115                agent = agent.receive_next(msg)?;
116            }
117        }
118    }
119
120    Ok(agent)
121}