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