veecle_telemetry/protocol/
owned.rs

1//! Type aliases for owned/sendable usage (IPC, serialization).
2//!
3//! These aliases use [`Value`] which is fully owned and Send-safe.
4//! Use these for telemetry that needs to cross thread boundaries or be serialized.
5//!
6//!
7//! See the [main protocol module][super] docs for more details on the protocol modules provided.
8
9use alloc::string::ToString;
10use alloc::vec::Vec;
11use serde::{Deserialize, Serialize};
12
13use crate::protocol::{base, transient};
14
15/// Owned storage family using owned data.
16///
17/// This family uses owned types that are Send-safe and can cross
18/// thread boundaries or be serialized.
19#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
20pub struct Owned;
21
22impl base::Sealed for Owned {}
23impl base::StorageFamily for Owned {
24    type String<'a>
25        = alloc::string::String
26    where
27        Self: 'a;
28
29    type List<'a, T: Clone + core::fmt::Debug + serde::Serialize + 'a>
30        = alloc::vec::Vec<T>
31    where
32        Self: 'a;
33
34    type Value<'a>
35        = Value
36    where
37        Self: 'a;
38}
39
40// Re-export non-generic types for convenience.
41pub use base::{
42    ProcessId, Severity, SpanAddLinkMessage, SpanCloseMessage, SpanContext, SpanEnterMessage,
43    SpanExitMessage, SpanId, ThreadId, TimeSyncMessage,
44};
45
46/// Key-value pair with owned value (Send-safe, for IPC).
47pub type KeyValue = base::KeyValue<'static, Owned>;
48/// Instance message with owned values (Send-safe, for IPC).
49pub type InstanceMessage = base::InstanceMessage<'static, Owned>;
50/// Telemetry message with owned values (Send-safe, for IPC).
51pub type TelemetryMessage = base::TelemetryMessage<'static, Owned>;
52/// Log message with owned values (Send-safe, for IPC).
53pub type LogMessage = base::LogMessage<'static, Owned>;
54/// Tracing message with owned values (Send-safe, for IPC).
55pub type TracingMessage = base::TracingMessage<'static, Owned>;
56/// Span create message with owned values (Send-safe, for IPC).
57pub type SpanCreateMessage = base::SpanCreateMessage<'static, Owned>;
58/// Span set attribute message with owned values (Send-safe, for IPC).
59pub type SpanSetAttributeMessage = base::SpanSetAttributeMessage<'static, Owned>;
60/// Span add event message with owned values (Send-safe, for IPC).
61pub type SpanAddEventMessage = base::SpanAddEventMessage<'static, Owned>;
62
63/// An owned value that can be sent across thread boundaries.
64///
65/// Unlike [`transient::Value`], this type is fully owned and does not contain
66/// any non-Send types like `format_args!`. This makes it suitable for
67/// serialization and sending across threads via channels.
68///
69/// Cross-serialization compatible with [`transient::Value`] - the transient
70/// variants will be converted to owned types during serialization.
71///
72/// # Examples
73///
74/// ```rust
75/// use veecle_telemetry::protocol::{owned, transient};
76///
77/// // Create a `transient::Value` with `format_args!`.
78/// let count = 42;
79/// let transient = transient::Value::Formatted(format_args!("count: {count}"));
80///
81/// // Serialize to JSON.
82/// let json = serde_json::to_string(&transient)?;
83/// assert_eq!(json, r#"{"String":"count: 42"}"#);
84///
85/// // Deserialize as `owned::Value`.
86/// let owned: owned::Value = serde_json::from_str(&json)?;
87/// let owned::Value::String(string) = owned else { panic!("unexpected variant") };
88/// assert_eq!(string, "count: 42");
89/// # Ok::<(), serde_json::Error>(())
90/// ```
91#[derive(Clone, Debug, Serialize, Deserialize)]
92#[cfg(feature = "alloc")]
93pub enum Value {
94    /// A string value (owned)
95    String(alloc::string::String),
96
97    /// A boolean value
98    Bool(bool),
99
100    /// A 64-bit signed integer
101    I64(i64),
102
103    /// A 64-bit floating-point number
104    F64(f64),
105}
106
107#[cfg(feature = "alloc")]
108impl core::fmt::Display for Value {
109    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110        match self {
111            // For strings, debug print so they will get delimiters, since we are explicitly
112            // representing strings rather than directly human-targeted text, and they will be used
113            // in situations where knowing where the string ends is important.
114            Self::String(value) => write!(f, "{value:?}"),
115            Self::Bool(value) => write!(f, "{value}"),
116            Self::I64(value) => write!(f, "{value}"),
117            Self::F64(value) => write!(f, "{value}"),
118        }
119    }
120}
121
122// Conversions from transient types to owned types
123
124impl From<transient::InstanceMessage<'_>> for InstanceMessage {
125    fn from(value: transient::InstanceMessage<'_>) -> Self {
126        InstanceMessage {
127            thread_id: value.thread_id,
128            message: value.message.into(),
129        }
130    }
131}
132
133impl From<transient::TelemetryMessage<'_>> for TelemetryMessage {
134    fn from(value: transient::TelemetryMessage<'_>) -> Self {
135        match value {
136            transient::TelemetryMessage::Log(msg) => TelemetryMessage::Log(msg.into()),
137            transient::TelemetryMessage::Tracing(msg) => TelemetryMessage::Tracing(msg.into()),
138            transient::TelemetryMessage::TimeSync(msg) => TelemetryMessage::TimeSync(msg),
139        }
140    }
141}
142
143impl From<transient::LogMessage<'_>> for LogMessage {
144    fn from(value: transient::LogMessage<'_>) -> Self {
145        LogMessage {
146            time_unix_nano: value.time_unix_nano,
147            severity: value.severity,
148            body: value.body.to_string(),
149            attributes: Vec::from_iter(value.attributes.as_ref().iter().map(|kv| kv.into())),
150        }
151    }
152}
153
154impl From<transient::TracingMessage<'_>> for TracingMessage {
155    fn from(value: transient::TracingMessage<'_>) -> Self {
156        match value {
157            transient::TracingMessage::CreateSpan(msg) => TracingMessage::CreateSpan(msg.into()),
158            transient::TracingMessage::EnterSpan(msg) => TracingMessage::EnterSpan(msg),
159            transient::TracingMessage::ExitSpan(msg) => TracingMessage::ExitSpan(msg),
160            transient::TracingMessage::CloseSpan(msg) => TracingMessage::CloseSpan(msg),
161            transient::TracingMessage::AddEvent(msg) => TracingMessage::AddEvent(msg.into()),
162            transient::TracingMessage::AddLink(msg) => TracingMessage::AddLink(msg),
163            transient::TracingMessage::SetAttribute(msg) => {
164                TracingMessage::SetAttribute(msg.into())
165            }
166        }
167    }
168}
169
170impl From<transient::SpanCreateMessage<'_>> for SpanCreateMessage {
171    fn from(value: transient::SpanCreateMessage<'_>) -> Self {
172        SpanCreateMessage {
173            span_id: value.span_id,
174            name: value.name.to_string(),
175            start_time_unix_nano: value.start_time_unix_nano,
176            attributes: Vec::from_iter(value.attributes.as_ref().iter().map(|kv| kv.into())),
177        }
178    }
179}
180
181impl From<transient::SpanSetAttributeMessage<'_>> for SpanSetAttributeMessage {
182    fn from(value: transient::SpanSetAttributeMessage<'_>) -> Self {
183        SpanSetAttributeMessage {
184            span_id: value.span_id,
185            attribute: value.attribute.into(),
186        }
187    }
188}
189
190impl From<transient::SpanAddEventMessage<'_>> for SpanAddEventMessage {
191    fn from(value: transient::SpanAddEventMessage<'_>) -> Self {
192        SpanAddEventMessage {
193            span_id: value.span_id,
194            name: value.name.to_string(),
195            time_unix_nano: value.time_unix_nano,
196            attributes: Vec::from_iter(value.attributes.as_ref().iter().map(|kv| kv.into())),
197        }
198    }
199}
200
201impl From<transient::KeyValue<'_>> for KeyValue {
202    fn from(value: transient::KeyValue<'_>) -> Self {
203        KeyValue {
204            key: value.key.to_string(),
205            value: value.value.into(),
206        }
207    }
208}
209
210impl From<&transient::KeyValue<'_>> for KeyValue {
211    fn from(value: &transient::KeyValue<'_>) -> Self {
212        KeyValue {
213            key: value.key.to_string(),
214            value: (&value.value).into(),
215        }
216    }
217}
218
219impl From<transient::Value<'_>> for Value {
220    fn from(value: transient::Value<'_>) -> Self {
221        match value {
222            transient::Value::String(s) => Value::String(s.to_string()),
223            transient::Value::Formatted(s) => Value::String(s.to_string()),
224            transient::Value::Bool(b) => Value::Bool(b),
225            transient::Value::I64(i) => Value::I64(i),
226            transient::Value::F64(f) => Value::F64(f),
227        }
228    }
229}
230
231impl From<&transient::Value<'_>> for Value {
232    fn from(value: &transient::Value<'_>) -> Self {
233        match value {
234            transient::Value::String(s) => Value::String(s.to_string()),
235            transient::Value::Formatted(s) => Value::String(s.to_string()),
236            transient::Value::Bool(b) => Value::Bool(*b),
237            transient::Value::I64(i) => Value::I64(*i),
238            transient::Value::F64(f) => Value::F64(*f),
239        }
240    }
241}