use crate::GtpMessage;
use gbp::CodecError;
use gbp_core::{GbpFlags, MemberId, StreamType};
use gbp_node::{GroupNode, NodeError, OutboundFrame, Sealer};
use std::collections::HashSet;
#[derive(Debug, thiserror::Error)]
pub enum GtpError {
#[error("decode: {0}")]
Decode(#[from] CodecError),
#[error("duplicate (sender={sender_id}, mid=0x{message_id:X})")]
Duplicate {
sender_id: MemberId,
message_id: u64,
},
#[error("node: {0}")]
Node(#[from] NodeError),
}
#[derive(Debug)]
pub enum GtpAccept {
New(GtpMessage),
Duplicate(GtpMessage),
}
#[derive(Default)]
pub struct GtpClient {
seen: HashSet<(MemberId, u64)>,
current_epoch: Option<u64>,
}
impl GtpClient {
pub fn new() -> Self {
Self::default()
}
pub fn send<S: Sealer>(
&mut self,
node: &mut GroupNode,
seal: &mut S,
target: MemberId,
message_id: u64,
text: &str,
) -> Result<OutboundFrame, GtpError> {
self.sync_epoch(node.current_epoch);
let msg = GtpMessage::plain(node.member_id, message_id, text);
let stream_id = node.member_stream_id(1);
let of = node.send_payload(
seal,
target,
StreamType::Text,
stream_id,
GbpFlags::ordered_reliable_ack(),
&msg.to_cbor(),
)?;
Ok(of)
}
pub fn accept(&mut self, plaintext: &[u8], current_epoch: u64) -> Result<GtpAccept, GtpError> {
self.sync_epoch(current_epoch);
let m = GtpMessage::from_cbor(plaintext)?;
let key = (m.sender_id, m.message_id);
if !self.seen.insert(key) {
return Ok(GtpAccept::Duplicate(m));
}
Ok(GtpAccept::New(m))
}
pub fn sync_epoch(&mut self, epoch: u64) {
if Some(epoch) != self.current_epoch {
self.seen.clear();
self.current_epoch = Some(epoch);
}
}
pub fn reset(&mut self) {
self.seen.clear();
self.current_epoch = None;
}
}