use serde::{Deserialize, Serialize};
use crate::validation::{
ProtocolLimits, Validate, ValidationError, check_count, check_finite, check_opt_string,
};
use super::BatchMetadata;
use super::de::{
de_opt_session_id, de_spikes, de_strength, prefix_path, validate_optional_metadata,
};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[serde(try_from = "SpikeBatchWire")]
pub struct SpikeBatch {
pub session_id: Option<String>,
pub batch_id: u64,
pub timestamp: u64,
pub spikes: Vec<SpikeEvent>,
pub metadata: Option<BatchMetadata>,
}
#[derive(Deserialize)]
struct SpikeBatchWire {
#[serde(default, deserialize_with = "de_opt_session_id")]
session_id: Option<String>,
batch_id: u64,
timestamp: u64,
#[serde(deserialize_with = "de_spikes")]
spikes: Vec<SpikeEvent>,
metadata: Option<BatchMetadata>,
}
impl TryFrom<SpikeBatchWire> for SpikeBatch {
type Error = ValidationError;
fn try_from(wire: SpikeBatchWire) -> Result<Self, Self::Error> {
let batch = SpikeBatch {
session_id: wire.session_id,
batch_id: wire.batch_id,
timestamp: wire.timestamp,
spikes: wire.spikes,
metadata: wire.metadata,
};
batch.validate()?;
Ok(batch)
}
}
impl Validate for SpikeBatch {
fn validate_with(&self, limits: ProtocolLimits) -> Result<(), ValidationError> {
check_opt_string("session_id", self.session_id.as_deref(), limits)?;
check_count("spikes", self.spikes.len(), limits.max_spike_events)?;
for (index, spike) in self.spikes.iter().enumerate() {
spike
.validate_with(limits)
.map_err(|err| prefix_path(err, &format!("spikes[{index}]")))?;
}
validate_optional_metadata(&self.metadata, limits, self.spikes.len())
}
}
pub type IpcSpikeBatch = SpikeBatch;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[serde(try_from = "SpikeEventWire")]
pub struct SpikeEvent {
pub channel: u16,
pub time: u32,
pub strength: f32,
}
#[derive(Deserialize)]
struct SpikeEventWire {
channel: u16,
time: u32,
#[serde(deserialize_with = "de_strength")]
strength: f32,
}
impl TryFrom<SpikeEventWire> for SpikeEvent {
type Error = ValidationError;
fn try_from(wire: SpikeEventWire) -> Result<Self, Self::Error> {
let event = SpikeEvent {
channel: wire.channel,
time: wire.time,
strength: wire.strength,
};
event.validate()?;
Ok(event)
}
}
impl Validate for SpikeEvent {
fn validate_with(&self, _limits: ProtocolLimits) -> Result<(), ValidationError> {
check_finite("strength", self.strength)
}
}