lnmp_envelope/
envelope.rs1use lnmp_core::LnmpRecord;
4
5use crate::metadata::EnvelopeMetadata;
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub struct LnmpEnvelope {
34 pub record: LnmpRecord,
36
37 pub metadata: EnvelopeMetadata,
39}
40
41impl LnmpEnvelope {
42 pub fn new(record: LnmpRecord) -> Self {
44 Self {
45 record,
46 metadata: EnvelopeMetadata::new(),
47 }
48 }
49
50 pub fn with_metadata(record: LnmpRecord, metadata: EnvelopeMetadata) -> Self {
52 Self { record, metadata }
53 }
54
55 pub fn has_metadata(&self) -> bool {
57 !self.metadata.is_empty()
58 }
59
60 pub fn validate(&self) -> crate::Result<()> {
62 self.metadata.validate()?;
63 Ok(())
64 }
65}
66
67pub struct EnvelopeBuilder {
86 record: LnmpRecord,
87 metadata: EnvelopeMetadata,
88}
89
90impl EnvelopeBuilder {
91 pub fn new(record: LnmpRecord) -> Self {
93 Self {
94 record,
95 metadata: EnvelopeMetadata::new(),
96 }
97 }
98
99 pub fn timestamp(mut self, ts: u64) -> Self {
101 self.metadata.timestamp = Some(ts);
102 self
103 }
104
105 pub fn source(mut self, src: impl Into<String>) -> Self {
107 self.metadata.source = Some(src.into());
108 self
109 }
110
111 pub fn trace_id(mut self, id: impl Into<String>) -> Self {
113 self.metadata.trace_id = Some(id.into());
114 self
115 }
116
117 pub fn sequence(mut self, seq: u64) -> Self {
119 self.metadata.sequence = Some(seq);
120 self
121 }
122
123 pub fn label(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
125 self.metadata.labels.insert(key.into(), value.into());
126 self
127 }
128
129 pub fn build(self) -> LnmpEnvelope {
131 LnmpEnvelope::with_metadata(self.record, self.metadata)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use lnmp_core::{LnmpField, LnmpValue};
139
140 fn sample_record() -> LnmpRecord {
141 let mut record = LnmpRecord::new();
142 record.add_field(LnmpField {
143 fid: 12,
144 value: LnmpValue::Int(14532),
145 });
146 record
147 }
148
149 #[test]
150 fn test_new_envelope_has_no_metadata() {
151 let envelope = LnmpEnvelope::new(sample_record());
152 assert!(!envelope.has_metadata());
153 }
154
155 #[test]
156 fn test_builder_basic() {
157 let envelope = EnvelopeBuilder::new(sample_record())
158 .timestamp(1732373147000)
159 .source("test-service")
160 .build();
161
162 assert!(envelope.has_metadata());
163 assert_eq!(envelope.metadata.timestamp, Some(1732373147000));
164 assert_eq!(envelope.metadata.source, Some("test-service".to_string()));
165 }
166
167 #[test]
168 fn test_builder_all_fields() {
169 let envelope = EnvelopeBuilder::new(sample_record())
170 .timestamp(1732373147000)
171 .source("auth-service")
172 .trace_id("abc-123-xyz")
173 .sequence(42)
174 .label("tenant", "acme")
175 .label("env", "prod")
176 .build();
177
178 assert_eq!(envelope.metadata.timestamp, Some(1732373147000));
179 assert_eq!(envelope.metadata.source, Some("auth-service".to_string()));
180 assert_eq!(envelope.metadata.trace_id, Some("abc-123-xyz".to_string()));
181 assert_eq!(envelope.metadata.sequence, Some(42));
182 assert_eq!(envelope.metadata.labels.len(), 2);
183 }
184
185 #[test]
186 fn test_validate_succeeds_for_valid_envelope() {
187 let envelope = EnvelopeBuilder::new(sample_record())
188 .timestamp(1732373147000)
189 .source("short")
190 .build();
191
192 assert!(envelope.validate().is_ok());
193 }
194}