1use crate::wasi::{self, clocks0_2_0::wall_clock};
2use serde::{
3 Deserialize,
4 de::{self, SeqAccess, Visitor},
5};
6use std::{
7 fmt,
8 time::{Duration, SystemTime, UNIX_EPOCH},
9};
10
11impl From<wasi::otel::types::KeyValue> for opentelemetry::KeyValue {
12 fn from(kv: wasi::otel::types::KeyValue) -> Self {
13 let owned: OwnedValue = from_json(&kv.value);
14 let value: opentelemetry::Value = owned.into();
15 opentelemetry::KeyValue::new(kv.key, value)
16 }
17}
18
19impl From<OwnedValue> for opentelemetry::Value {
20 fn from(value: OwnedValue) -> Self {
21 match value {
22 OwnedValue::String(s) => opentelemetry::Value::String(s.into()),
23 OwnedValue::Bool(v) => opentelemetry::Value::Bool(v),
24 OwnedValue::F64(v) => opentelemetry::Value::F64(v),
25 OwnedValue::I64(v) => opentelemetry::Value::I64(v),
26 OwnedValue::Array(arr) => opentelemetry::Value::Array(match arr {
27 OwnedArray::Bool(v) => opentelemetry::Array::Bool(v),
28 OwnedArray::F64(v) => opentelemetry::Array::F64(v),
29 OwnedArray::I64(v) => opentelemetry::Array::I64(v),
30 OwnedArray::String(v) => opentelemetry::Array::String(
31 v.into_iter()
32 .map(opentelemetry::StringValue::from)
33 .collect(),
34 ),
35 }),
36 }
37 }
38}
39
40enum OwnedValue {
41 Bool(bool),
42 I64(i64),
43 F64(f64),
44 String(String),
45 Array(OwnedArray),
46}
47
48enum OwnedArray {
49 Bool(Vec<bool>),
50 I64(Vec<i64>),
51 F64(Vec<f64>),
52 String(Vec<String>),
53}
54
55impl<'de> Deserialize<'de> for OwnedValue {
56 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
57 where
58 D: serde::Deserializer<'de>,
59 {
60 struct ValueVisitor;
61
62 impl<'de> Visitor<'de> for ValueVisitor {
63 type Value = OwnedValue;
64
65 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
66 formatter.write_str("a boolean, number, string, or array")
67 }
68
69 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
70 where
71 E: de::Error,
72 {
73 Ok(OwnedValue::Bool(value))
74 }
75
76 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
77 where
78 E: de::Error,
79 {
80 Ok(OwnedValue::I64(value))
81 }
82
83 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
84 where
85 E: de::Error,
86 {
87 Ok(OwnedValue::F64(value))
88 }
89
90 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
92 where
93 E: de::Error,
94 {
95 i64::try_from(value)
96 .map(|v| Ok(OwnedValue::I64(v)))
97 .map_err(|_| de::Error::custom("Integer too large for i64"))?
98 }
99
100 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
101 where
102 E: de::Error,
103 {
104 Ok(OwnedValue::String(value.to_owned()))
105 }
106
107 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
108 where
109 A: SeqAccess<'de>,
110 {
111 let mut elements = Vec::new();
112
113 if let Some(first) = seq.next_element::<serde_json::Value>()? {
115 elements.push(first);
116
117 while let Some(elem) = seq.next_element::<serde_json::Value>()? {
119 elements.push(elem);
120 }
121
122 if elements.is_empty() {
123 return Ok(OwnedValue::Array(OwnedArray::Bool(vec![])));
124 }
125
126 match &elements[0] {
127 serde_json::Value::Bool(_) => {
128 let bools: Result<Vec<bool>, _> = elements
129 .iter()
130 .map(|v| {
131 v.as_bool()
132 .ok_or_else(|| de::Error::custom("Mixed types in array"))
133 })
134 .collect();
135 Ok(OwnedValue::Array(OwnedArray::Bool(bools?)))
136 }
137 serde_json::Value::Number(n) if n.is_i64() => {
138 let ints: Result<Vec<i64>, _> = elements
139 .iter()
140 .map(|v| {
141 v.as_i64()
142 .ok_or_else(|| de::Error::custom("Mixed types in array"))
143 })
144 .collect();
145 Ok(OwnedValue::Array(OwnedArray::I64(ints?)))
146 }
147 serde_json::Value::Number(n) if n.is_f64() => {
148 let nums: Result<Vec<f64>, _> = elements
149 .iter()
150 .map(|v| {
151 v.as_f64()
152 .ok_or_else(|| de::Error::custom("Mixed types in array"))
153 })
154 .collect();
155 Ok(OwnedValue::Array(OwnedArray::F64(nums?)))
156 }
157 serde_json::Value::String(_) => {
158 let strings: Result<Vec<String>, _> = elements
159 .iter()
160 .map(|v| match v.as_str() {
161 Some(s) => Ok(s.to_string()),
162 None => Err(de::Error::custom("Mixed types in array")),
163 })
164 .collect();
165 Ok(OwnedValue::Array(OwnedArray::String(strings?)))
166 }
167 _ => Err(de::Error::custom("Unsupported array element type")),
168 }
169 } else {
170 Ok(OwnedValue::Array(OwnedArray::Bool(vec![])))
172 }
173 }
174 }
175
176 deserializer.deserialize_any(ValueVisitor)
177 }
178}
179
180pub(crate) fn from_json<T: for<'de> Deserialize<'de>>(json: &str) -> T {
182 serde_json::from_str(json).unwrap_or_else(|e| {
183 panic!(
184 "Failed to deserialize JSON to {}\
185 \n Input: {}\
186 \n Error: {}",
187 std::any::type_name::<T>(),
188 json,
189 e
190 )
191 })
192}
193
194impl From<wasi::otel::types::InstrumentationScope> for opentelemetry::InstrumentationScope {
195 fn from(value: wasi::otel::types::InstrumentationScope) -> Self {
196 let builder =
197 Self::builder(value.name).with_attributes(value.attributes.into_iter().map(Into::into));
198 match (value.version, value.schema_url) {
199 (Some(version), Some(schema_url)) => builder
200 .with_version(version)
201 .with_schema_url(schema_url)
202 .build(),
203 (Some(version), None) => builder.with_version(version).build(),
204 (None, Some(schema_url)) => builder.with_schema_url(schema_url).build(),
205 (None, None) => builder.build(),
206 }
207 }
208}
209
210impl From<wall_clock::Datetime> for SystemTime {
211 fn from(timestamp: wall_clock::Datetime) -> Self {
212 UNIX_EPOCH
213 + Duration::from_secs(timestamp.seconds)
214 + Duration::from_nanos(timestamp.nanoseconds as u64)
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 macro_rules! compare_json_and_literal {
223 ($json:expr, $literal:expr) => {{
224 let left: serde_json::Value = from_json($json);
225 let right: serde_json::Value = serde_json::json!($literal);
226 assert_eq!(left, right);
227 }};
228 }
229
230 #[test]
231 fn deserialize_from_json_to_otel_value() {
232 compare_json_and_literal!("false", false);
233 compare_json_and_literal!("[false,true,true]", vec![false, true, true]);
234 compare_json_and_literal!("6", 6);
235 compare_json_and_literal!("[1,2,3,4]", vec![1, 2, 3, 4]);
236 compare_json_and_literal!("-6", -6);
237 compare_json_and_literal!("[-1,-2,-3,-4]", vec![-1, -2, -3, -4]);
238 compare_json_and_literal!("123.456", 123.456);
239 compare_json_and_literal!("[1.0,2.1,3.2,4.3]", vec![1.0, 2.1, 3.2, 4.3]);
240 compare_json_and_literal!("\"test\"", "test");
241 compare_json_and_literal!(
242 "[\"Hello, world!\",\"Goodnight, moon.\"]",
243 vec!["Hello, world!", "Goodnight, moon."]
244 );
245 }
246}