corpus_ipc/models/
metadata.rs1use serde::{Deserialize, Serialize};
4
5use crate::validation::{
6 ProtocolLimits, Validate, ValidationError, check_count, check_opt_string, check_string,
7};
8
9use super::de::{de_metadata_custom, de_source};
10
11#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
13#[serde(try_from = "BatchMetadataWire")]
14pub struct BatchMetadata {
15 pub processing_latency_ns: Option<u64>,
17 pub source: Option<String>,
19 pub custom: std::collections::HashMap<String, String>,
21}
22
23#[derive(Deserialize)]
24struct BatchMetadataWire {
25 processing_latency_ns: Option<u64>,
26 #[serde(default, deserialize_with = "de_source")]
27 source: Option<String>,
28 #[serde(default, deserialize_with = "de_metadata_custom")]
29 custom: std::collections::HashMap<String, String>,
30}
31
32impl TryFrom<BatchMetadataWire> for BatchMetadata {
33 type Error = ValidationError;
34
35 fn try_from(wire: BatchMetadataWire) -> Result<Self, Self::Error> {
36 let metadata = BatchMetadata {
37 processing_latency_ns: wire.processing_latency_ns,
38 source: wire.source,
39 custom: wire.custom,
40 };
41 metadata.validate()?;
42 Ok(metadata)
43 }
44}
45
46impl Validate for BatchMetadata {
47 fn validate_with(&self, limits: ProtocolLimits) -> Result<(), ValidationError> {
48 check_opt_string("source", self.source.as_deref(), limits)?;
49 check_count("custom", self.custom.len(), limits.max_metadata_entries)?;
50 for (key, value) in &self.custom {
51 if key.is_empty() {
52 return Err(ValidationError::nested_metadata("custom.<empty>"));
53 }
54 check_string("custom.<key>", key, limits)?;
55 check_string("custom.<value>", value, limits)?;
56 }
57 Ok(())
58 }
59}