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