use bigerror::{error_stack::Report, ConversionError, ThinContext};
use tokio::time::Instant;
use super::{Kind, Rex, State};
use crate::{
ingress::{Ingress, StateRouter},
notification::{GetTopic, RexMessage},
timeout::{NoRetain, Timeout, TimeoutInput, TimeoutMessage},
RexError, StateId,
};
pub trait TestDefault {
fn test_default() -> Self;
}
#[macro_export]
macro_rules! node_state {
($( $name: ident ),*) => {
$(
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum $name {
New,
Awaiting,
Completed,
Failed,
}
)*
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum NodeKind {
$( $name, )*
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NodeState {
$( $name($name), )*
}
impl State for NodeState {
type Input = ();
}
impl AsRef<NodeKind> for NodeState {
fn as_ref(&self) -> &NodeKind {
match self {
$( Self::$name(_) => &NodeKind::$name, )*
}
}
}
impl Kind for NodeKind {
type State = NodeState;
type Input = ();
fn new_state(&self) -> Self::State {
match self {
$( NodeKind::$name => NodeState::$name($name::New), )*
}
}
fn failed_state(&self) -> Self::State {
match self {
$( NodeKind::$name => NodeState::$name($name::Failed), )*
}
}
fn completed_state(&self) -> Self::State {
match self {
$( NodeKind::$name => NodeState::$name($name::Completed), )*
}
}
}
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
#[allow(dead_code)]
pub enum TestTopic {
Timeout,
Ingress,
Other,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum TestMsg {
TimeoutInput(TimeoutInput<TestKind>),
Ingress(OutPacket),
Other,
}
impl RexMessage for TestMsg {
type Topic = TestTopic;
}
#[derive(Copy, Clone, Debug, derive_more::Display)]
pub struct Hold<T>(pub(crate) T);
impl TimeoutMessage<TestKind> for TestMsg {
type Item = NoRetain;
}
impl GetTopic<TestTopic> for TestMsg {
fn get_topic(&self) -> TestTopic {
match self {
Self::TimeoutInput(_) => TestTopic::Timeout,
Self::Ingress(_) => TestTopic::Ingress,
Self::Other => TestTopic::Other,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(dead_code)]
pub enum TestState {
New,
Awaiting,
Completed,
Failed,
}
impl State for TestState {
type Input = TestInput;
}
impl AsRef<TestKind> for TestState {
fn as_ref(&self) -> &TestKind {
&TestKind
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct TestKind;
impl Kind for TestKind {
type State = TestState;
type Input = TestInput;
fn new_state(&self) -> Self::State {
TestState::New
}
fn failed_state(&self) -> Self::State {
TestState::Failed
}
fn completed_state(&self) -> Self::State {
TestState::Completed
}
}
impl Ingress for TestKind {
type In = InPacket;
type Out = OutPacket;
}
impl TryFrom<InPacket> for TestInput {
type Error = Report<ConversionError>;
fn try_from(packet: InPacket) -> Result<Self, Self::Error> {
Ok(Self::Packet(packet))
}
}
impl Timeout for TestKind {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TestInput {
Timeout(Instant),
Packet(InPacket),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OutPacket(pub Vec<u8>);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InPacket(pub Vec<u8>);
impl Rex for TestKind {
type Message = TestMsg;
fn state_input(&self, _state: <Self as Kind>::State) -> Option<Self::Input> {
unimplemented!()
}
fn timeout_input(&self, instant: tokio::time::Instant) -> Option<Self::Input> {
Some(TestInput::Timeout(instant))
}
}
pub struct TestStateRouter;
impl StateRouter<TestKind> for TestStateRouter {
type Inbound = InPacket;
fn get_id(&self, input: &Self::Inbound) -> Result<Option<StateId<TestKind>>, Report<RexError>> {
let packet = &input.0;
match packet {
_ if packet.starts_with(b"unsupported") => Err(RexError::attach("wrong packet type")),
_ if packet.starts_with(b"unknown") => Ok(None),
_ if packet.starts_with(b"new_state") => Ok(Some(StateId::new_with_u128(TestKind, 1))),
_ => unimplemented!(),
}
}
fn get_kind(&self) -> TestKind {
TestKind
}
}
impl<'a> TryFrom<&'a InPacket> for TestKind {
type Error = Report<ConversionError>;
fn try_from(_value: &'a InPacket) -> Result<Self, Self::Error> {
Ok(Self)
}
}
impl TryInto<OutPacket> for TestMsg {
type Error = Report<ConversionError>;
fn try_into(self) -> Result<OutPacket, Self::Error> {
if let Self::Ingress(packet) = self {
return Ok(packet);
}
Err(ConversionError::attach_dbg(self))
}
}
impl TryInto<TimeoutInput<TestKind>> for TestMsg {
type Error = Report<ConversionError>;
fn try_into(self) -> Result<TimeoutInput<TestKind>, Self::Error> {
if let Self::TimeoutInput(timeout) = self {
return Ok(timeout);
}
Err(ConversionError::attach_dbg(self))
}
}
impl From<OutPacket> for TestMsg {
fn from(val: OutPacket) -> Self {
Self::Ingress(val)
}
}
impl From<TimeoutInput<TestKind>> for TestMsg {
fn from(value: TimeoutInput<TestKind>) -> Self {
Self::TimeoutInput(value)
}
}