laminar_connectors/schema/
types.rs1#![allow(clippy::disallowed_types)] use std::any::Any;
13use std::collections::HashMap;
14
15#[derive(Debug, Clone)]
20pub struct RawRecord {
21 pub key: Option<Vec<u8>>,
23
24 pub value: Vec<u8>,
26
27 pub timestamp: Option<i64>,
29
30 pub headers: HashMap<String, Vec<u8>>,
32
33 pub metadata: SourceMetadata,
35}
36
37impl RawRecord {
38 #[must_use]
40 pub fn new(value: Vec<u8>) -> Self {
41 Self {
42 key: None,
43 value,
44 timestamp: None,
45 headers: HashMap::new(),
46 metadata: SourceMetadata::empty(),
47 }
48 }
49
50 #[must_use]
52 pub fn with_key(mut self, key: Vec<u8>) -> Self {
53 self.key = Some(key);
54 self
55 }
56
57 #[must_use]
59 pub fn with_timestamp(mut self, ts: i64) -> Self {
60 self.timestamp = Some(ts);
61 self
62 }
63
64 #[must_use]
66 pub fn with_header(mut self, key: impl Into<String>, value: Vec<u8>) -> Self {
67 self.headers.insert(key.into(), value);
68 self
69 }
70
71 #[must_use]
73 pub fn with_metadata(mut self, metadata: SourceMetadata) -> Self {
74 self.metadata = metadata;
75 self
76 }
77}
78
79pub struct SourceMetadata {
84 inner: Option<Box<dyn Any + Send + Sync>>,
85}
86
87impl SourceMetadata {
88 #[must_use]
90 pub fn empty() -> Self {
91 Self { inner: None }
92 }
93
94 pub fn new<T: Any + Send + Sync>(value: T) -> Self {
96 Self {
97 inner: Some(Box::new(value)),
98 }
99 }
100
101 #[must_use]
103 pub fn is_empty(&self) -> bool {
104 self.inner.is_none()
105 }
106
107 #[must_use]
109 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
110 self.inner.as_ref()?.downcast_ref::<T>()
111 }
112}
113
114impl std::fmt::Debug for SourceMetadata {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 if self.inner.is_some() {
117 write!(f, "SourceMetadata(<opaque>)")
118 } else {
119 write!(f, "SourceMetadata(empty)")
120 }
121 }
122}
123
124impl Clone for SourceMetadata {
125 fn clone(&self) -> Self {
126 Self::empty()
128 }
129}
130
131#[derive(Debug, Clone, Default)]
136pub struct FieldMeta {
137 pub field_id: Option<u32>,
139
140 pub description: Option<String>,
142
143 pub source_type: Option<String>,
145
146 pub default_expr: Option<String>,
148
149 pub properties: HashMap<String, String>,
151}
152
153impl FieldMeta {
154 #[must_use]
156 pub fn new() -> Self {
157 Self::default()
158 }
159
160 #[must_use]
162 pub fn with_field_id(mut self, id: u32) -> Self {
163 self.field_id = Some(id);
164 self
165 }
166
167 #[must_use]
169 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
170 self.description = Some(desc.into());
171 self
172 }
173
174 #[must_use]
176 pub fn with_source_type(mut self, src_type: impl Into<String>) -> Self {
177 self.source_type = Some(src_type.into());
178 self
179 }
180
181 #[must_use]
183 pub fn with_default(mut self, expr: impl Into<String>) -> Self {
184 self.default_expr = Some(expr.into());
185 self
186 }
187
188 #[must_use]
190 pub fn with_property(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
191 self.properties.insert(key.into(), value.into());
192 self
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_raw_record_builder() {
202 let record = RawRecord::new(b"hello".to_vec())
203 .with_key(b"k1".to_vec())
204 .with_timestamp(1000)
205 .with_header("content-type", b"application/json".to_vec());
206
207 assert_eq!(record.key.as_deref(), Some(b"k1".as_slice()));
208 assert_eq!(record.value, b"hello");
209 assert_eq!(record.timestamp, Some(1000));
210 assert!(record.headers.contains_key("content-type"));
211 }
212
213 #[test]
214 fn test_source_metadata_empty() {
215 let meta = SourceMetadata::empty();
216 assert!(meta.is_empty());
217 assert!(meta.downcast_ref::<String>().is_none());
218 }
219
220 #[test]
221 fn test_source_metadata_typed() {
222 let meta = SourceMetadata::new(42u64);
223 assert!(!meta.is_empty());
224 assert_eq!(meta.downcast_ref::<u64>(), Some(&42u64));
225 assert!(meta.downcast_ref::<String>().is_none());
226 }
227
228 #[test]
229 fn test_source_metadata_debug() {
230 let empty = SourceMetadata::empty();
231 assert!(format!("{empty:?}").contains("empty"));
232
233 let full = SourceMetadata::new("data");
234 assert!(format!("{full:?}").contains("opaque"));
235 }
236
237 #[test]
238 fn test_field_meta_builder() {
239 let meta = FieldMeta::new()
240 .with_field_id(1)
241 .with_description("User ID")
242 .with_source_type("BIGINT")
243 .with_default("0")
244 .with_property("pii", "true");
245
246 assert_eq!(meta.field_id, Some(1));
247 assert_eq!(meta.description.as_deref(), Some("User ID"));
248 assert_eq!(meta.source_type.as_deref(), Some("BIGINT"));
249 assert_eq!(meta.default_expr.as_deref(), Some("0"));
250 assert_eq!(meta.properties.get("pii").map(String::as_str), Some("true"));
251 }
252}