use super::packet::{DataPacket, DataType};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AggregationError {
#[error("Failed to deserialize payload: {0}")]
DeserializationError(#[from] serde_json::Error),
#[error("Aggregation operation failed: {0}")]
AggregationFailed(String),
#[error("Expected {expected} packet type, got {actual:?}")]
InvalidPacketType { expected: String, actual: DataType },
#[error("Cannot aggregate empty packet list")]
EmptyInput,
}
pub trait Aggregator: Send + Sync {
fn aggregate_telemetry(
&self,
group_id: &str,
leader_id: &str,
telemetry_packets: Vec<DataPacket>,
) -> Result<DataPacket, AggregationError>;
fn extract_summary_bytes(&self, packet: &DataPacket) -> Result<Vec<u8>, AggregationError> {
if packet.data_type != DataType::AggregatedTelemetry {
return Err(AggregationError::InvalidPacketType {
expected: "AggregatedTelemetry".to_string(),
actual: packet.data_type,
});
}
Ok(packet.payload.clone())
}
}
pub struct NoOpAggregator;
impl Aggregator for NoOpAggregator {
fn aggregate_telemetry(
&self,
_group_id: &str,
_leader_id: &str,
_telemetry_packets: Vec<DataPacket>,
) -> Result<DataPacket, AggregationError> {
Err(AggregationError::AggregationFailed(
"No aggregator configured".to_string(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aggregation_error_display() {
let err = AggregationError::EmptyInput;
assert_eq!(err.to_string(), "Cannot aggregate empty packet list");
let err = AggregationError::AggregationFailed("test".to_string());
assert_eq!(err.to_string(), "Aggregation operation failed: test");
}
#[test]
fn test_aggregation_error_invalid_type() {
let err = AggregationError::InvalidPacketType {
expected: "Telemetry".to_string(),
actual: DataType::Command,
};
assert!(err.to_string().contains("Expected Telemetry"));
}
#[test]
fn test_aggregation_error_from_serde() {
let serde_err = serde_json::from_str::<serde_json::Value>("not json").unwrap_err();
let err: AggregationError = serde_err.into();
assert!(err.to_string().contains("deserialize"));
}
#[test]
fn test_noop_aggregator_returns_error() {
let agg = NoOpAggregator;
let result = agg.aggregate_telemetry("group", "leader", vec![]);
assert!(result.is_err());
if let Err(AggregationError::AggregationFailed(msg)) = result {
assert!(msg.contains("No aggregator configured"));
}
}
#[test]
fn test_extract_summary_bytes_valid() {
let agg = NoOpAggregator;
let mut packet = DataPacket::telemetry("src", vec![1, 2, 3]);
packet.data_type = DataType::AggregatedTelemetry;
let result = agg.extract_summary_bytes(&packet);
assert_eq!(result.unwrap(), vec![1, 2, 3]);
}
#[test]
fn test_extract_summary_bytes_wrong_type() {
let agg = NoOpAggregator;
let packet = DataPacket::command("src", "dst", vec![]);
let result = agg.extract_summary_bytes(&packet);
assert!(result.is_err());
if let Err(AggregationError::InvalidPacketType { expected, actual }) = result {
assert_eq!(expected, "AggregatedTelemetry");
assert_eq!(actual, DataType::Command);
}
}
}