quint-connect 0.1.2

A model-based testing framework that connects Quint specifications with Rust applications
Documentation
use serde::Deserialize;

#[derive(Eq, PartialEq, Deserialize, Default, Copy, Clone, Debug)]
#[serde(tag = "tag")]
pub enum Stage {
    #[default]
    Working,
    Prepared,
    Committed,
    Aborted,
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Debug)]
pub enum Message {
    Prepare,
    Prepared,
    Commit,
    Abort,
}

pub enum Node {
    Coordinator(Coordinator),
    Participant(Participant),
}

impl Node {
    pub fn stage(&self) -> Stage {
        match self {
            Node::Coordinator(coord) => coord.stage,
            Node::Participant(part) => part.stage,
        }
    }

    pub fn timeout(&mut self) -> Option<Message> {
        match self {
            Node::Coordinator(coord) => coord.timeout(),
            Node::Participant(part) => part.timeout(),
        }
    }

    pub fn receive(&mut self, msg: Message) -> Option<Message> {
        match self {
            Node::Coordinator(coord) => coord.receive(msg),
            Node::Participant(part) => part.receive(msg),
        }
    }
}

pub struct Coordinator {
    stage: Stage,
    quorum: usize,
    prepared: usize,
}

impl Coordinator {
    pub fn new(quorum: usize) -> Self {
        Self {
            stage: Stage::Working,
            prepared: 0,
            quorum,
        }
    }

    pub fn start(&self) -> Message {
        Message::Prepare
    }

    pub fn timeout(&mut self) -> Option<Message> {
        match self.stage {
            Stage::Working => {
                self.stage = Stage::Aborted;
                Some(Message::Abort)
            }
            _ => None,
        }
    }

    pub fn receive(&mut self, msg: Message) -> Option<Message> {
        match (self.stage, msg) {
            (Stage::Working, Message::Prepared) => {
                self.prepared += 1;
                if self.prepared == self.quorum {
                    self.stage = Stage::Committed;
                    return Some(Message::Commit);
                }
                None
            }
            _ => None,
        }
    }
}

#[derive(Default)]
pub struct Participant {
    stage: Stage,
}

impl Participant {
    pub fn timeout(&mut self) -> Option<Message> {
        match self.stage {
            Stage::Working => {
                self.stage = Stage::Aborted;
                None
            }
            _ => None,
        }
    }

    pub fn receive(&mut self, msg: Message) -> Option<Message> {
        match (self.stage, msg) {
            (Stage::Working, Message::Prepare) => {
                self.stage = Stage::Prepared;
                Some(Message::Prepared)
            }
            (_, Message::Abort) => {
                self.stage = Stage::Aborted;
                None
            }
            (_, Message::Commit) => {
                self.stage = Stage::Committed;
                None
            }
            _ => None,
        }
    }
}