use opentelemetry::Value;
pub trait ToValue {
fn to_value(&self) -> Option<Value>;
}
impl<T: ToValue + ?Sized> ToValue for &T {
fn to_value(&self) -> Option<Value> {
(*self).to_value()
}
}
impl<T: ToValue> ToValue for Option<T> {
fn to_value(&self) -> Option<Value> {
self.as_ref().and_then(|val| val.to_value())
}
}
macro_rules! impl_to_value_copy {
($ty:ty => $variant:ident) => {
impl ToValue for $ty {
fn to_value(&self) -> Option<Value> {
Some(Value::$variant(*self))
}
}
};
($ty:ty => $variant:ident as $cast:ty) => {
impl ToValue for $ty {
fn to_value(&self) -> Option<Value> {
Some(Value::$variant(*self as $cast))
}
}
};
}
impl ToValue for str {
fn to_value(&self) -> Option<Value> {
Some(Value::String(self.to_string().into()))
}
}
impl ToValue for String {
fn to_value(&self) -> Option<Value> {
Some(Value::String(self.clone().into()))
}
}
impl_to_value_copy!(i64 => I64);
impl_to_value_copy!(i32 => I64 as i64);
impl_to_value_copy!(i16 => I64 as i64);
impl_to_value_copy!(i8 => I64 as i64);
impl_to_value_copy!(u64 => I64 as i64);
impl_to_value_copy!(u32 => I64 as i64);
impl_to_value_copy!(u16 => I64 as i64);
impl_to_value_copy!(u8 => I64 as i64);
impl_to_value_copy!(usize => I64 as i64);
impl_to_value_copy!(isize => I64 as i64);
impl_to_value_copy!(f64 => F64);
impl_to_value_copy!(f32 => F64 as f64);
impl_to_value_copy!(bool => Bool);
impl ToValue for Value {
fn to_value(&self) -> Option<Value> {
Some(self.clone())
}
}
#[cfg(feature = "http")]
mod http_impl {
use super::{ToValue, Value};
impl ToValue for http::Method {
fn to_value(&self) -> Option<Value> {
Some(Value::String(self.to_string().into()))
}
}
impl ToValue for http::StatusCode {
fn to_value(&self) -> Option<Value> {
Some(Value::I64(self.as_u16() as i64))
}
}
impl ToValue for http::Version {
fn to_value(&self) -> Option<Value> {
Some(Value::String(format!("{self:?}").into()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_types() {
let hello: &str = "hello";
assert_eq!(
ToValue::to_value(&hello),
Some(Value::String("hello".into()))
);
assert_eq!(
ToValue::to_value(&String::from("world")),
Some(Value::String("world".into()))
);
}
#[test]
fn test_integer_types() {
assert_eq!(ToValue::to_value(&42i64), Some(Value::I64(42)));
assert_eq!(ToValue::to_value(&42i32), Some(Value::I64(42)));
assert_eq!(ToValue::to_value(&42i16), Some(Value::I64(42)));
assert_eq!(ToValue::to_value(&42i8), Some(Value::I64(42)));
assert_eq!(ToValue::to_value(&42u64), Some(Value::I64(42)));
assert_eq!(ToValue::to_value(&42u32), Some(Value::I64(42)));
assert_eq!(ToValue::to_value(&42u16), Some(Value::I64(42)));
assert_eq!(ToValue::to_value(&42u8), Some(Value::I64(42)));
}
#[test]
fn test_float_types() {
assert_eq!(ToValue::to_value(&2.5f64), Some(Value::F64(2.5)));
assert_eq!(ToValue::to_value(&2.5f32), Some(Value::F64(2.5f32 as f64)));
}
#[test]
fn test_bool() {
assert_eq!(ToValue::to_value(&true), Some(Value::Bool(true)));
assert_eq!(ToValue::to_value(&false), Some(Value::Bool(false)));
}
#[test]
fn test_option_some() {
let value: Option<i64> = Some(42);
assert_eq!(ToValue::to_value(&value), Some(Value::I64(42)));
}
#[test]
fn test_option_none() {
let value: Option<i64> = None;
assert_eq!(ToValue::to_value(&value), None);
}
#[test]
fn test_option_some_string() {
let value: Option<String> = Some("hello".to_string());
assert_eq!(
ToValue::to_value(&value),
Some(Value::String("hello".into()))
);
}
#[test]
fn test_double_reference() {
let value = 42i64;
let ref1 = &value;
let ref2 = &ref1;
assert_eq!(ToValue::to_value(ref2), Some(Value::I64(42)));
}
#[test]
fn test_value_passthrough() {
let value = Value::String("test".into());
assert_eq!(
ToValue::to_value(&value),
Some(Value::String("test".into()))
);
}
}
#[cfg(feature = "grpc")]
mod tonic_impl {
use super::{ToValue, Value};
impl ToValue for tonic::Code {
fn to_value(&self) -> Option<Value> {
Some(Value::I64(i32::from(*self) as i64))
}
}
impl ToValue for tonic::Status {
fn to_value(&self) -> Option<Value> {
self.code().to_value()
}
}
impl ToValue for tonic::metadata::MetadataKey<tonic::metadata::Ascii> {
fn to_value(&self) -> Option<Value> {
Some(Value::String(self.as_str().to_string().into()))
}
}
impl ToValue for tonic::metadata::MetadataKey<tonic::metadata::Binary> {
fn to_value(&self) -> Option<Value> {
Some(Value::String(self.as_str().to_string().into()))
}
}
impl ToValue for tonic::metadata::MetadataValue<tonic::metadata::Ascii> {
fn to_value(&self) -> Option<Value> {
self.to_str()
.ok()
.map(|s| Value::String(s.to_string().into()))
}
}
impl ToValue for tonic::metadata::MetadataValue<tonic::metadata::Binary> {
fn to_value(&self) -> Option<Value> {
use base64::Engine;
let bytes = self.as_encoded_bytes();
let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
Some(Value::String(encoded.into()))
}
}
}
#[cfg(all(test, feature = "http"))]
mod http_tests {
use super::*;
#[test]
fn test_http_method() {
assert_eq!(
ToValue::to_value(&http::Method::GET),
Some(Value::String("GET".into()))
);
assert_eq!(
ToValue::to_value(&http::Method::POST),
Some(Value::String("POST".into()))
);
}
#[test]
fn test_http_status_code() {
assert_eq!(
ToValue::to_value(&http::StatusCode::OK),
Some(Value::I64(200))
);
assert_eq!(
ToValue::to_value(&http::StatusCode::NOT_FOUND),
Some(Value::I64(404))
);
}
#[test]
fn test_http_version() {
assert_eq!(
ToValue::to_value(&http::Version::HTTP_11),
Some(Value::String("HTTP/1.1".into()))
);
assert_eq!(
ToValue::to_value(&http::Version::HTTP_2),
Some(Value::String("HTTP/2.0".into()))
);
}
#[test]
fn test_http_option() {
let method: Option<http::Method> = Some(http::Method::GET);
assert_eq!(
ToValue::to_value(&method),
Some(Value::String("GET".into()))
);
let no_method: Option<http::Method> = None;
assert_eq!(ToValue::to_value(&no_method), None);
}
}
#[cfg(all(test, feature = "grpc"))]
mod tonic_tests {
use super::*;
#[test]
fn test_tonic_code() {
assert_eq!(ToValue::to_value(&tonic::Code::Ok), Some(Value::I64(0)));
assert_eq!(
ToValue::to_value(&tonic::Code::InvalidArgument),
Some(Value::I64(3))
);
assert_eq!(
ToValue::to_value(&tonic::Code::NotFound),
Some(Value::I64(5))
);
}
#[test]
fn test_tonic_status() {
let status = tonic::Status::not_found("resource missing");
assert_eq!(
ToValue::to_value(&status),
Some(Value::I64(5)) );
}
#[test]
fn test_tonic_metadata_key() {
let key: tonic::metadata::MetadataKey<tonic::metadata::Ascii> =
"x-request-id".parse().unwrap();
assert_eq!(
ToValue::to_value(&key),
Some(Value::String("x-request-id".into()))
);
}
#[test]
fn test_tonic_metadata_value_ascii() {
let value: tonic::metadata::MetadataValue<tonic::metadata::Ascii> =
"test-value".parse().unwrap();
assert_eq!(
ToValue::to_value(&value),
Some(Value::String("test-value".into()))
);
}
#[test]
fn test_tonic_code_option() {
let code: Option<tonic::Code> = Some(tonic::Code::Ok);
assert_eq!(ToValue::to_value(&code), Some(Value::I64(0)));
let no_code: Option<tonic::Code> = None;
assert_eq!(ToValue::to_value(&no_code), None);
}
}