use super::name::{ChannelId, ChannelName};
use crate::adapter::net::stream::Reliability;
use crate::error::AdapterError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OnFailure {
#[default]
BestEffort,
FailFast,
Collect,
}
#[derive(Debug, Clone)]
pub struct PublishConfig {
pub reliability: Reliability,
pub on_failure: OnFailure,
pub max_inflight: usize,
}
impl Default for PublishConfig {
fn default() -> Self {
Self {
reliability: Reliability::FireAndForget,
on_failure: OnFailure::BestEffort,
max_inflight: 32,
}
}
}
impl PublishConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_reliability(mut self, reliability: Reliability) -> Self {
self.reliability = reliability;
self
}
pub fn with_on_failure(mut self, on_failure: OnFailure) -> Self {
self.on_failure = on_failure;
self
}
pub fn with_max_inflight(mut self, n: usize) -> Self {
self.max_inflight = n.max(1);
self
}
}
#[derive(Debug, Clone)]
pub struct ChannelPublisher {
channel: ChannelId,
config: PublishConfig,
}
impl ChannelPublisher {
pub fn new(channel: ChannelName, config: PublishConfig) -> Self {
Self {
channel: ChannelId::new(channel),
config,
}
}
pub fn channel(&self) -> &ChannelId {
&self.channel
}
pub fn config(&self) -> &PublishConfig {
&self.config
}
}
#[derive(Debug)]
pub struct PublishReport {
pub attempted: usize,
pub delivered: usize,
pub errors: Vec<(u64, AdapterError)>,
}
impl PublishReport {
pub fn all_delivered(&self) -> bool {
self.delivered == self.attempted && self.errors.is_empty()
}
pub fn is_empty(&self) -> bool {
self.attempted == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_defaults() {
let c = PublishConfig::default();
assert_eq!(c.reliability, Reliability::FireAndForget);
assert_eq!(c.on_failure, OnFailure::BestEffort);
assert_eq!(c.max_inflight, 32);
}
#[test]
fn test_config_builder() {
let c = PublishConfig::new()
.with_reliability(Reliability::Reliable)
.with_on_failure(OnFailure::FailFast)
.with_max_inflight(8);
assert_eq!(c.reliability, Reliability::Reliable);
assert_eq!(c.on_failure, OnFailure::FailFast);
assert_eq!(c.max_inflight, 8);
}
#[test]
fn test_max_inflight_clamp() {
let c = PublishConfig::new().with_max_inflight(0);
assert_eq!(c.max_inflight, 1);
}
#[test]
fn test_publisher_new() {
let name = ChannelName::new("sensors/lidar").unwrap();
let p = ChannelPublisher::new(name.clone(), PublishConfig::default());
assert_eq!(p.channel().name().as_str(), "sensors/lidar");
assert_eq!(p.config().reliability, Reliability::FireAndForget);
}
#[test]
fn test_report_helpers() {
let empty = PublishReport {
attempted: 0,
delivered: 0,
errors: vec![],
};
assert!(empty.is_empty());
assert!(
empty.all_delivered(),
"empty roster must be vacuously all_delivered"
);
let full = PublishReport {
attempted: 3,
delivered: 3,
errors: vec![],
};
assert!(!full.is_empty());
assert!(full.all_delivered());
let partial = PublishReport {
attempted: 3,
delivered: 2,
errors: vec![(42, AdapterError::Connection("boom".into()))],
};
assert!(!partial.all_delivered());
}
}