use core::time::Duration;
use buggy::Bug;
use heapless::Vec;
use postcard::Error as PostcardError;
use serde::{Deserialize, Serialize};
use crate::{
Address, MaxCut, Prior,
command::{CmdId, Command, Priority},
storage::{GraphId, MAX_COMMAND_LENGTH, StorageError},
};
mod requester;
mod responder;
mod wire;
use requester::SyncRequestMessage;
pub use requester::SyncRequester;
use responder::SyncResponseMessage;
pub use responder::{PeerCache, SyncResponder};
use wire::{SubscribeResult, SyncHelloType, SyncType};
pub const PEER_HEAD_MAX: usize = 10;
#[cfg(feature = "low-mem-usage")]
const COMMAND_SAMPLE_MAX: usize = 20;
#[cfg(not(feature = "low-mem-usage"))]
const COMMAND_SAMPLE_MAX: usize = 100;
#[cfg(feature = "low-mem-usage")]
const REQUEST_MISSING_MAX: usize = 1;
#[cfg(not(feature = "low-mem-usage"))]
const REQUEST_MISSING_MAX: usize = 100;
#[cfg(feature = "low-mem-usage")]
pub const COMMAND_RESPONSE_MAX: usize = 5;
#[cfg(not(feature = "low-mem-usage"))]
pub const COMMAND_RESPONSE_MAX: usize = 100;
#[cfg(feature = "low-mem-usage")]
const SEGMENT_BUFFER_MAX: usize = 10;
#[cfg(not(feature = "low-mem-usage"))]
const SEGMENT_BUFFER_MAX: usize = 100;
pub const MAX_SYNC_MESSAGE_SIZE: usize = 1024 + MAX_COMMAND_LENGTH * COMMAND_RESPONSE_MAX;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SyncError {
#[error("sync session ID does not match")]
SessionMismatch,
#[error("missing sync response")]
MissingSyncResponse,
#[error("syncer state not valid for this message")]
SessionState,
#[error("syncer not ready for operation")]
NotReady,
#[error("too many commands sent")]
CommandOverflow,
#[error("storage error: {0}")]
Storage(#[from] StorageError),
#[error("serialize error: {0}")]
Serialize(#[from] PostcardError),
#[error(transparent)]
Bug(#[from] Bug),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SyncCommand<'a> {
priority: Priority,
id: CmdId,
parent: Prior<Address>,
policy: Option<&'a [u8]>,
data: &'a [u8],
max_cut: MaxCut,
}
impl<'a> Command for SyncCommand<'a> {
fn priority(&self) -> Priority {
self.priority.clone()
}
fn id(&self) -> CmdId {
self.id
}
fn parent(&self) -> Prior<Address> {
self.parent
}
fn policy(&self) -> Option<&'a [u8]> {
self.policy
}
fn bytes(&self) -> &'a [u8] {
self.data
}
fn max_cut(&self) -> Result<MaxCut, Bug> {
Ok(self.max_cut)
}
}
#[allow(clippy::large_enum_variant)]
pub enum SyncIncoming<'a> {
Poll(PollIncoming),
Subscribe(SubscribeIncoming),
Unsubscribe(UnsubscribeIncoming),
Push(PushIncoming<'a>),
Hello(SyncHello),
}
impl<'a> SyncIncoming<'a> {
pub fn decode(data: &'a [u8]) -> Result<Self, SyncError> {
let (sync_type, remaining) = postcard::take_from_bytes::<SyncType>(data)?;
Ok(match sync_type {
SyncType::Poll { request } => Self::Poll(PollIncoming {
session_id: request.session_id(),
message: request,
}),
SyncType::Subscribe {
remain_open,
max_bytes,
commands,
graph_id,
} => Self::Subscribe(SubscribeIncoming {
graph_id,
remain_open,
max_bytes,
heads: SyncHeads { inner: commands },
}),
SyncType::Unsubscribe { graph_id } => {
Self::Unsubscribe(UnsubscribeIncoming { graph_id })
}
SyncType::Push { message, graph_id } => Self::Push(PushIncoming {
graph_id,
session_id: message.session_id(),
message,
command_data: remaining,
}),
SyncType::Hello(hello) => Self::Hello(hello.into()),
})
}
}
pub struct SyncHeads {
inner: Vec<Address, COMMAND_SAMPLE_MAX>,
}
impl SyncHeads {
pub fn as_slice(&self) -> &[Address] {
&self.inner
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = Address> + ExactSizeIterator + '_ {
self.inner.iter().copied()
}
}
#[derive(Debug)]
pub enum SyncHello {
Subscribe(HelloSubscribe),
Unsubscribe(HelloUnsubscribe),
Hello(HelloNotification),
}
impl From<SyncHelloType> for SyncHello {
fn from(t: SyncHelloType) -> Self {
match t {
SyncHelloType::Subscribe {
graph_id,
graph_change_delay,
duration,
schedule_delay,
} => Self::Subscribe(HelloSubscribe {
graph_id,
graph_change_delay,
duration,
schedule_delay,
}),
SyncHelloType::Unsubscribe { graph_id } => {
Self::Unsubscribe(HelloUnsubscribe { graph_id })
}
SyncHelloType::Hello { graph_id, head } => {
Self::Hello(HelloNotification { graph_id, head })
}
}
}
}
#[derive(Debug)]
pub struct HelloSubscribe {
graph_id: GraphId,
graph_change_delay: Duration,
duration: Duration,
schedule_delay: Duration,
}
impl HelloSubscribe {
pub fn graph_id(&self) -> GraphId {
self.graph_id
}
pub fn graph_change_delay(&self) -> Duration {
self.graph_change_delay
}
pub fn duration(&self) -> Duration {
self.duration
}
pub fn schedule_delay(&self) -> Duration {
self.schedule_delay
}
}
#[derive(Debug)]
pub struct HelloUnsubscribe {
graph_id: GraphId,
}
impl HelloUnsubscribe {
pub fn graph_id(&self) -> GraphId {
self.graph_id
}
}
#[derive(Debug)]
pub struct HelloNotification {
graph_id: GraphId,
head: Address,
}
impl HelloNotification {
pub fn graph_id(&self) -> GraphId {
self.graph_id
}
pub fn head(&self) -> Address {
self.head
}
}
pub struct PollIncoming {
session_id: u128,
pub(crate) message: SyncRequestMessage,
}
impl PollIncoming {
pub fn session_id(&self) -> u128 {
self.session_id
}
}
pub struct SubscribeIncoming {
graph_id: GraphId,
remain_open: u64,
max_bytes: u64,
heads: SyncHeads,
}
impl SubscribeIncoming {
pub fn graph_id(&self) -> GraphId {
self.graph_id
}
pub fn remain_open(&self) -> Duration {
Duration::from_secs(self.remain_open)
}
pub fn max_bytes(&self) -> u64 {
self.max_bytes
}
pub fn heads(&self) -> &SyncHeads {
&self.heads
}
}
pub struct UnsubscribeIncoming {
graph_id: GraphId,
}
impl UnsubscribeIncoming {
pub fn graph_id(&self) -> GraphId {
self.graph_id
}
}
pub struct PushIncoming<'a> {
graph_id: GraphId,
session_id: u128,
pub(crate) message: SyncResponseMessage,
pub(crate) command_data: &'a [u8],
}
impl PushIncoming<'_> {
pub fn graph_id(&self) -> GraphId {
self.graph_id
}
pub fn session_id(&self) -> u128 {
self.session_id
}
}
#[derive(Debug)]
pub enum SubscribeResponse {
Success,
TooManySubscriptions,
}
impl SubscribeResponse {
pub fn encode_to(self, target: &mut [u8]) -> Result<usize, SyncError> {
let inner = match self {
Self::Success => SubscribeResult::Success,
Self::TooManySubscriptions => SubscribeResult::TooManySubscriptions,
};
Ok(postcard::to_slice(&inner, target)?.len())
}
pub fn decode(data: &[u8]) -> Result<Self, SyncError> {
let inner: SubscribeResult = postcard::from_bytes(data)?;
Ok(match inner {
SubscribeResult::Success => Self::Success,
SubscribeResult::TooManySubscriptions => Self::TooManySubscriptions,
})
}
}