scuffle_metrics/
value.rs

1use std::borrow::Cow;
2use std::sync::Arc;
3
4use opentelemetry::{Array, StringValue, Value};
5
6/// A compiler trick to create a specialization for a type.
7/// Its particularly useful when we using macros so that we can at compile time
8/// specialize for an arbitrary input type. We want to specialize for specific
9/// types that have a known conversion to a `Value`, otherwise use the
10/// `Into<Value>` trait. Or if the type implements `std::fmt::Display` or
11/// `std::fmt::Debug` we use that to convert to a `String` and then to a
12/// `Value`.
13#[doc(hidden)]
14pub struct SpecializeValue<T>(Option<T>);
15
16impl<T> SpecializeValue<T> {
17    pub fn new(value: T) -> Self {
18        Self(Some(value))
19    }
20
21    #[inline]
22    fn take(&mut self) -> T {
23        self.0.take().expect("value is not Some")
24    }
25}
26
27#[doc(hidden)]
28pub trait Specialization: private::Sealed {
29    fn take_value(&mut self) -> Option<Value>;
30}
31
32#[doc(hidden)]
33mod private {
34    use super::SpecializeValue;
35
36    pub trait Sealed {}
37
38    impl<T: Sealed> Sealed for &mut T {}
39    impl<T> Sealed for SpecializeValue<T> {}
40}
41
42macro_rules! sealed {
43    ($($t:ty),*) => {
44        $(impl private::Sealed for $t {})*
45    };
46}
47
48macro_rules! integer_specialization {
49    ($type:ty) => {
50        impl Specialization for Option<$type> {
51            #[inline]
52            fn take_value(&mut self) -> Option<Value> {
53                Some(Value::I64(self.take()? as i64))
54            }
55        }
56
57        sealed!(Option<$type>);
58    };
59}
60
61integer_specialization!(i32);
62integer_specialization!(i64);
63integer_specialization!(u32);
64integer_specialization!(i16);
65integer_specialization!(u16);
66integer_specialization!(i8);
67integer_specialization!(u8);
68
69impl Specialization for Option<f32> {
70    #[inline]
71    fn take_value(&mut self) -> Option<Value> {
72        Some(Value::F64(self.take()? as f64))
73    }
74}
75
76sealed!(Option<f32>);
77
78impl Specialization for Option<f64> {
79    #[inline]
80    fn take_value(&mut self) -> Option<Value> {
81        Some(Value::F64(self.take()?))
82    }
83}
84
85sealed!(Option<f64>);
86
87impl Specialization for Option<bool> {
88    #[inline]
89    fn take_value(&mut self) -> Option<Value> {
90        Some(Value::Bool(self.take()?))
91    }
92}
93
94sealed!(Option<bool>);
95
96impl Specialization for Option<&'static str> {
97    #[inline]
98    fn take_value(&mut self) -> Option<Value> {
99        Some(Value::String(self.take()?.into()))
100    }
101}
102
103sealed!(Option<&'static str>);
104
105impl Specialization for Option<StringValue> {
106    #[inline]
107    fn take_value(&mut self) -> Option<Value> {
108        Some(Value::String(self.take()?))
109    }
110}
111
112sealed!(Option<StringValue>);
113
114impl Specialization for Option<String> {
115    #[inline]
116    fn take_value(&mut self) -> Option<Value> {
117        Some(Value::String(self.take()?.into()))
118    }
119}
120
121sealed!(Option<String>);
122
123impl Specialization for Option<Arc<str>> {
124    #[inline]
125    fn take_value(&mut self) -> Option<Value> {
126        Some(Value::String(self.take()?.into()))
127    }
128}
129
130sealed!(Option<Arc<str>>);
131
132impl Specialization for Option<Cow<'static, str>> {
133    #[inline]
134    fn take_value(&mut self) -> Option<Value> {
135        Some(Value::String(self.take()?.into()))
136    }
137}
138
139sealed!(Option<Cow<'static, str>>);
140
141impl Specialization for Option<Value> {
142    #[inline]
143    fn take_value(&mut self) -> Option<Value> {
144        self.take()
145    }
146}
147
148sealed!(Option<Value>);
149
150impl Specialization for Option<Array> {
151    #[inline]
152    fn take_value(&mut self) -> Option<Value> {
153        Some(Value::Array(self.take()?))
154    }
155}
156
157sealed!(Option<Array>);
158
159impl Specialization for Option<Vec<bool>> {
160    #[inline]
161    fn take_value(&mut self) -> Option<Value> {
162        Some(Value::Array(Array::Bool(self.take()?)))
163    }
164}
165
166sealed!(Option<Vec<bool>>);
167
168macro_rules! integer_vector_specialization {
169    ($type:ty) => {
170        impl Specialization for Option<Vec<$type>> {
171            #[inline]
172            fn take_value(&mut self) -> Option<Value> {
173                Some(Value::Array(Array::I64(
174                    self.take()?.into_iter().map(|i| i as i64).collect(),
175                )))
176            }
177        }
178
179        sealed!(Option<Vec<$type>>);
180    };
181}
182
183integer_vector_specialization!(i32);
184integer_vector_specialization!(i64);
185integer_vector_specialization!(u32);
186integer_vector_specialization!(i16);
187integer_vector_specialization!(u16);
188integer_vector_specialization!(i8);
189integer_vector_specialization!(u8);
190
191impl Specialization for Option<Vec<f64>> {
192    #[inline]
193    fn take_value(&mut self) -> Option<Value> {
194        Some(Value::Array(Array::F64(self.take()?)))
195    }
196}
197
198sealed!(Option<Vec<f64>>);
199
200impl Specialization for Option<Vec<f32>> {
201    #[inline]
202    fn take_value(&mut self) -> Option<Value> {
203        Some(Value::Array(Array::F64(self.take()?.into_iter().map(|f| f as f64).collect())))
204    }
205}
206
207sealed!(Option<Vec<f32>>);
208
209impl Specialization for Option<Vec<&'static str>> {
210    #[inline]
211    fn take_value(&mut self) -> Option<Value> {
212        Some(Value::Array(Array::String(
213            self.take()?.into_iter().map(|s| s.into()).collect(),
214        )))
215    }
216}
217
218sealed!(Option<Vec<&'static str>>);
219
220impl Specialization for Option<Vec<StringValue>> {
221    #[inline]
222    fn take_value(&mut self) -> Option<Value> {
223        Some(Value::Array(Array::String(self.take()?)))
224    }
225}
226
227sealed!(Option<Vec<StringValue>>);
228
229impl Specialization for Option<Vec<String>> {
230    #[inline]
231    fn take_value(&mut self) -> Option<Value> {
232        Some(Value::Array(Array::String(
233            self.take()?.into_iter().map(|s| s.into()).collect(),
234        )))
235    }
236}
237
238sealed!(Option<Vec<String>>);
239
240impl Specialization for Option<Vec<Arc<str>>> {
241    #[inline]
242    fn take_value(&mut self) -> Option<Value> {
243        Some(Value::Array(Array::String(
244            self.take()?.into_iter().map(|s| s.into()).collect(),
245        )))
246    }
247}
248
249sealed!(Option<Vec<Arc<str>>>);
250
251impl Specialization for Option<Vec<Cow<'static, str>>> {
252    #[inline]
253    fn take_value(&mut self) -> Option<Value> {
254        Some(Value::Array(Array::String(
255            self.take()?.into_iter().map(|s| s.into()).collect(),
256        )))
257    }
258}
259
260sealed!(Option<Vec<Cow<'static, str>>>);
261
262impl<T: std::fmt::Display> Specialization for &mut &mut SpecializeValue<T> {
263    #[inline]
264    fn take_value(&mut self) -> Option<Value> {
265        Some(Value::String(self.take().to_string().into()))
266    }
267}
268
269impl<T: Into<Value>> Specialization for &mut &mut &mut SpecializeValue<T> {
270    #[inline]
271    fn take_value(&mut self) -> Option<Value> {
272        Some(self.take().into())
273    }
274}
275
276impl<T> Specialization for &mut &mut &mut &mut SpecializeValue<T>
277where
278    T: Specialization,
279{
280    #[inline]
281    fn take_value(&mut self) -> Option<Value> {
282        self.take().take_value()
283    }
284}
285
286impl<T> Specialization for &mut &mut &mut &mut &mut SpecializeValue<T>
287where
288    Option<T>: Specialization,
289{
290    #[inline]
291    fn take_value(&mut self) -> Option<Value> {
292        Some(self.take()).take_value()
293    }
294}
295
296#[doc(hidden)]
297#[macro_export]
298macro_rules! to_value {
299    ($value:expr) => {{
300        use $crate::value::Specialization;
301        (&mut &mut &mut &mut &mut &mut $crate::value::SpecializeValue::new($value)).take_value()
302    }};
303}
304
305#[cfg(test)]
306#[cfg_attr(all(test, coverage_nightly), coverage(off))]
307mod tests {
308    use super::*;
309
310    #[test]
311    fn test_specialization_i64() {
312        let value = to_value!(1);
313        assert_eq!(value, Some(Value::I64(1)));
314    }
315
316    #[test]
317    fn test_specialization_display() {
318        struct Displayable(i64);
319        impl std::fmt::Display for Displayable {
320            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321                write!(f, "{}", self.0)
322            }
323        }
324        let value = to_value!(Displayable(1));
325        assert_eq!(value, Some(Value::String("1".into())));
326    }
327
328    #[test]
329    fn test_specialization_into() {
330        struct Intoable(i64);
331        impl From<Intoable> for Value {
332            fn from(val: Intoable) -> Self {
333                Value::I64(val.0)
334            }
335        }
336        let value = to_value!(Intoable(1));
337        assert_eq!(value, Some(Value::I64(1)));
338    }
339
340    #[test]
341    fn test_specialization_array() {
342        let value = to_value!(vec![1, 2, 3]);
343        assert_eq!(value, Some(Value::Array(Array::I64(vec![1, 2, 3]))));
344    }
345
346    #[test]
347    fn test_specialization_integer_vector_option() {
348        let value = to_value!(Some(vec![1, 2, 3]));
349        assert_eq!(value, Some(Value::Array(Array::I64(vec![1, 2, 3]))));
350    }
351
352    #[test]
353    fn test_specialization_integer_vector_none() {
354        let value = to_value!(None::<Vec<i32>>);
355        assert_eq!(value, None);
356    }
357
358    #[test]
359    fn test_specialization_f64_vector() {
360        let value = to_value!(vec![1.0, 2.0, 3.0]);
361        assert_eq!(value, Some(Value::Array(Array::F64(vec![1.0, 2.0, 3.0]))));
362    }
363
364    #[test]
365    fn test_specialization_f32_vector() {
366        let value = to_value!(vec![1.0f32, 2.0f32, 3.0f32]);
367        assert_eq!(value, Some(Value::Array(Array::F64(vec![1.0, 2.0, 3.0]))));
368    }
369
370    #[test]
371    fn test_none_str() {
372        let value = to_value!(None::<&'static str>);
373        assert_eq!(value, None);
374    }
375
376    #[test]
377    fn test_some_str() {
378        let value = to_value!("hello");
379        assert_eq!(value, Some(Value::String("hello".into())));
380    }
381
382    #[test]
383    fn test_specialization_i32() {
384        let value = to_value!(1i32);
385        assert_eq!(value, Some(Value::I64(1)));
386    }
387
388    #[test]
389    fn test_specialization_u32() {
390        let value = to_value!(1u32);
391        assert_eq!(value, Some(Value::I64(1)));
392    }
393
394    #[test]
395    fn test_specialization_i16() {
396        let value = to_value!(1i16);
397        assert_eq!(value, Some(Value::I64(1)));
398    }
399
400    #[test]
401    fn test_specialization_u16() {
402        let value = to_value!(1u16);
403        assert_eq!(value, Some(Value::I64(1)));
404    }
405
406    #[test]
407    fn test_specialization_i8() {
408        let value = to_value!(1i8);
409        assert_eq!(value, Some(Value::I64(1)));
410    }
411
412    #[test]
413    fn test_specialization_u8() {
414        let value = to_value!(1u8);
415        assert_eq!(value, Some(Value::I64(1)));
416    }
417
418    #[test]
419    fn test_specialization_f32() {
420        let value = to_value!(1.0f32);
421        assert_eq!(value, Some(Value::F64(1.0)));
422    }
423
424    #[test]
425    fn test_specialization_bool() {
426        let value = to_value!(true);
427        assert_eq!(value, Some(Value::Bool(true)));
428    }
429
430    #[test]
431    fn test_specialization_string() {
432        let value = to_value!("hello".to_string());
433        assert_eq!(value, Some(Value::String("hello".into())));
434    }
435
436    #[test]
437    fn test_specialization_arc_str() {
438        let arc_str = Arc::from("hello");
439        let value = to_value!(arc_str);
440        assert_eq!(value, Some(Value::String("hello".into())));
441    }
442
443    #[test]
444    fn test_specialization_cow_str() {
445        let cow_str: Cow<'static, str> = Cow::Borrowed("hello");
446        let value = to_value!(cow_str);
447        assert_eq!(value, Some(Value::String("hello".into())));
448    }
449
450    #[test]
451    fn test_specialization_value() {
452        let val = Value::I64(42);
453        let value = to_value!(val);
454        assert_eq!(value, Some(Value::I64(42)));
455    }
456
457    #[test]
458    fn test_specialization_array_direct() {
459        let arr = Array::I64(vec![1, 2, 3]);
460        let value = to_value!(arr);
461        assert_eq!(value, Some(Value::Array(Array::I64(vec![1, 2, 3]))));
462    }
463
464    #[test]
465    fn test_specialization_vec_bool() {
466        let vec = vec![true, false, true];
467        let value = to_value!(vec);
468        assert_eq!(value, Some(Value::Array(Array::Bool(vec![true, false, true]))));
469    }
470
471    #[test]
472    fn test_specialization_vec_str() {
473        let vec = vec!["a", "b", "c"];
474        let value = to_value!(vec);
475        assert_eq!(
476            value,
477            Some(Value::Array(Array::String(vec!["a".into(), "b".into(), "c".into()])))
478        );
479    }
480
481    #[test]
482    fn test_specialization_vec_string() {
483        let vec = vec!["a".to_string(), "b".to_string(), "c".to_string()];
484        let value = to_value!(vec);
485        assert_eq!(
486            value,
487            Some(Value::Array(Array::String(vec!["a".into(), "b".into(), "c".into()])))
488        );
489    }
490
491    #[test]
492    fn test_specialization_vec_arc_str() {
493        let vec = vec![Arc::from("a"), Arc::from("b"), Arc::from("c")];
494        let value = to_value!(vec);
495        assert_eq!(
496            value,
497            Some(Value::Array(Array::String(vec!["a".into(), "b".into(), "c".into()])))
498        );
499    }
500
501    #[test]
502    fn test_specialization_vec_cow_str() {
503        let vec = vec![Cow::Borrowed("a"), Cow::Borrowed("b"), Cow::Borrowed("c")];
504        let value = to_value!(vec);
505        assert_eq!(
506            value,
507            Some(Value::Array(Array::String(vec!["a".into(), "b".into(), "c".into()])))
508        );
509    }
510
511    #[test]
512    fn test_specialization_option_i32_some() {
513        let value = to_value!(Some(1i32));
514        assert_eq!(value, Some(Value::I64(1)));
515    }
516
517    #[test]
518    fn test_specialization_option_i32_none() {
519        let value = to_value!(None::<i32>);
520        assert_eq!(value, None);
521    }
522
523    #[test]
524    fn test_specialization_option_f64_some() {
525        let value = to_value!(Some(1.0f64));
526        assert_eq!(value, Some(Value::F64(1.0)));
527    }
528
529    #[test]
530    fn test_specialization_option_f63_none() {
531        let value = to_value!(None::<f64>);
532        assert_eq!(value, None);
533    }
534
535    #[test]
536    fn test_specialization_option_string_some() {
537        let value = to_value!(Some("hello".to_string()));
538        assert_eq!(value, Some(Value::String("hello".into())));
539    }
540
541    #[test]
542    fn test_specialization_option_string_none() {
543        let value = to_value!(None::<String>);
544        assert_eq!(value, None);
545    }
546
547    #[test]
548    fn test_specialization_option_string_value_some() {
549        let value = to_value!(Some(StringValue::from("hello")));
550        assert_eq!(value, Some(Value::String("hello".into())));
551    }
552
553    #[test]
554    fn test_specialization_option_string_value_none() {
555        let value = to_value!(None::<StringValue>);
556        assert_eq!(value, None);
557    }
558
559    #[test]
560    fn test_specialization_option_vec_string_value_some() {
561        let value = to_value!(Some(vec![StringValue::from("a"), StringValue::from("b")]));
562        assert_eq!(value, Some(Value::Array(Array::String(vec!["a".into(), "b".into()]))));
563    }
564
565    #[test]
566    fn test_specialization_option_vec_string_value_none() {
567        let value = to_value!(None::<Vec<StringValue>>);
568        assert_eq!(value, None);
569    }
570}