use core::any::TypeId;
use serde::{Deserialize, Serialize};
use std::string::String;
use crate::graph::RecordOrigin;
use crate::record_id::{RecordId, RecordKey};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecordMetadata {
pub record_id: u32,
pub record_key: String,
pub name: String,
pub type_id: String,
pub origin: RecordOrigin,
pub buffer_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub buffer_capacity: Option<usize>,
pub producer_count: usize,
pub consumer_count: usize,
pub writable: bool,
pub created_at: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_update: Option<String>,
pub outbound_connector_count: usize,
#[cfg(feature = "metrics")]
#[serde(skip_serializing_if = "Option::is_none")]
pub produced_count: Option<u64>,
#[cfg(feature = "metrics")]
#[serde(skip_serializing_if = "Option::is_none")]
pub consumed_count: Option<u64>,
#[cfg(feature = "metrics")]
#[serde(skip_serializing_if = "Option::is_none")]
pub dropped_count: Option<u64>,
#[cfg(feature = "metrics")]
#[serde(skip_serializing_if = "Option::is_none")]
pub occupancy: Option<(usize, usize)>,
}
impl RecordMetadata {
#[allow(clippy::too_many_arguments)]
pub fn new<K: RecordKey>(
record_id: RecordId,
record_key: K,
type_id: TypeId,
name: String,
origin: RecordOrigin,
buffer_type: String,
buffer_capacity: Option<usize>,
producer_count: usize,
consumer_count: usize,
writable: bool,
created_at: String,
outbound_connector_count: usize,
) -> Self {
Self {
record_id: record_id.raw(),
record_key: record_key.as_str().to_string(),
name,
type_id: format!("{:?}", type_id),
origin,
buffer_type,
buffer_capacity,
producer_count,
consumer_count,
writable,
created_at,
last_update: None,
outbound_connector_count,
#[cfg(feature = "metrics")]
produced_count: None,
#[cfg(feature = "metrics")]
consumed_count: None,
#[cfg(feature = "metrics")]
dropped_count: None,
#[cfg(feature = "metrics")]
occupancy: None,
}
}
pub fn with_last_update(mut self, timestamp: String) -> Self {
self.last_update = Some(timestamp);
self
}
pub fn with_last_update_opt(mut self, timestamp: Option<String>) -> Self {
self.last_update = timestamp;
self
}
#[cfg(feature = "metrics")]
pub fn with_buffer_metrics(mut self, snapshot: crate::buffer::BufferMetricsSnapshot) -> Self {
self.produced_count = Some(snapshot.produced_count);
self.consumed_count = Some(snapshot.consumed_count);
self.dropped_count = Some(snapshot.dropped_count);
if snapshot.occupancy.1 > 0 {
self.occupancy = Some(snapshot.occupancy);
}
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::record_id::StringKey;
#[test]
fn test_record_metadata_creation() {
let type_id = TypeId::of::<i32>();
let metadata = RecordMetadata::new(
RecordId::new(0),
StringKey::new("test.record"),
type_id,
"i32".to_string(),
RecordOrigin::Source,
"spmc_ring".to_string(),
Some(100),
1,
2,
false,
"2025-10-31T10:00:00.000Z".to_string(),
0,
);
assert_eq!(metadata.record_id, 0);
assert_eq!(metadata.record_key, "test.record");
assert_eq!(metadata.name, "i32");
assert!(matches!(metadata.origin, RecordOrigin::Source));
assert_eq!(metadata.buffer_type, "spmc_ring");
assert_eq!(metadata.buffer_capacity, Some(100));
assert_eq!(metadata.producer_count, 1);
assert_eq!(metadata.consumer_count, 2);
assert_eq!(metadata.outbound_connector_count, 0);
assert!(!metadata.writable);
}
#[test]
fn test_record_metadata_serialization() {
let type_id = TypeId::of::<String>();
let metadata = RecordMetadata::new(
RecordId::new(1),
StringKey::new("app.config"),
type_id,
"String".to_string(),
RecordOrigin::Passive,
"single_latest".to_string(),
None,
1,
1,
true,
"2025-10-31T10:00:00.000Z".to_string(),
2,
)
.with_last_update("2025-10-31T12:00:00.000Z".to_string());
let json = serde_json::to_string(&metadata).unwrap();
assert!(json.contains("\"record_id\":1"));
assert!(json.contains("\"record_key\":\"app.config\""));
assert!(json.contains("\"name\":\"String\""));
assert!(json.contains("\"origin\":\"passive\""));
assert!(json.contains("\"buffer_type\":\"single_latest\""));
assert!(json.contains("\"writable\":true"));
assert!(json.contains("\"outbound_connector_count\":2"));
}
}