#![deny(clippy::all, clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::missing_errors_doc)]
mod error;
pub(crate) mod modifiers;
pub(crate) mod util;
use std::{
borrow::Borrow,
collections::{hash_map::Entry, HashMap},
fmt::Display,
io::Write,
};
use serde::{
ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct},
Serialize,
};
use crate::{
modifiers::{
apply_path_modifications, BuiltLabel, LabelBehaviour, LabelStack, LabelStackWithPath,
ParsedModifiersList, PathStack, MAX_ALLOWED_SUFFIX_PARTS, MAX_DEPTH,
},
util::CowArcStr,
};
pub use crate::error::Error;
#[inline]
pub fn to_string<'a, T, L, LI>(
value: &T,
namespace: Option<&str>,
global_labels: L,
) -> Result<String, Error>
where
T: ?Sized + Serialize,
LI: Borrow<(&'a str, &'a str)>,
L: IntoIterator<Item = LI>,
{
let mut output = Vec::with_capacity(std::mem::size_of_val(value) * 8 * 12);
write(&mut output, value, namespace, global_labels)?;
Ok(String::from_utf8(output)?)
}
#[inline]
pub fn write<'a, T, L, LI, W: Write>(
output: &mut W,
value: &T,
namespace: Option<&str>,
global_labels: L,
) -> Result<(), Error>
where
T: ?Sized + Serialize,
LI: Borrow<(&'a str, &'a str)>,
L: IntoIterator<Item = LI>,
{
let mut serializer = Serializer::new(output, namespace);
serializer.labels.push(global_labels.into_iter().map(|v| {
let (key, value) = v.borrow();
Ok(BuiltLabel {
behaviour: LabelBehaviour::Replace,
key,
value: CowArcStr::Borrowed(value),
})
}))?;
value.serialize(&mut serializer)?;
Ok(())
}
struct Serializer<'a, W: Write> {
path: PathStack<'a>,
labels: LabelStack<'a>,
output: W,
modifier_cache: HashMap<&'a str, ParsedModifiersList<'a>>,
namespace: Option<CowArcStr<'a>>,
key: heapless::Vec<heapless::Vec<CowArcStr<'a>, { MAX_ALLOWED_SUFFIX_PARTS }>, 12>,
prevent_key_modification: bool,
}
impl<'a, W: Write> Serializer<'a, W> {
#[inline]
pub(crate) fn new(output: W, namespace: Option<&'a str>) -> Self {
Self {
path: PathStack::default(),
labels: LabelStack::default(),
output,
namespace: namespace.map(CowArcStr::Borrowed),
modifier_cache: HashMap::default(),
key: heapless::Vec::<_, MAX_ALLOWED_SUFFIX_PARTS>::default(),
prevent_key_modification: false,
}
}
#[inline]
pub fn write_value<S: Display>(&mut self, v: S) -> Result<(), Error> {
if let Some(namespace) = &self.namespace {
write!(self.output, "{namespace}_").map_err(Error::Write)?;
}
let mut prefix = "";
let popped_path = if self.prevent_key_modification {
None
} else if let Some(last_set_index) = self.path.last_set_index() {
self.path
.get_mut(last_set_index)
.and_then(Option::take)
.map(|v| (last_set_index, v))
} else {
None
};
if let Some((_, path)) = &popped_path {
write!(self.output, "{prefix}{path}").map_err(Error::Write)?;
prefix = "_";
}
for key_stack in &self.key {
for key in key_stack {
write!(self.output, "{prefix}{key}").map_err(Error::Write)?;
if prefix.is_empty() {
prefix = "_";
}
}
}
let labels = LabelStackWithPath {
path_stack: &self.path,
label_stack: &self.labels,
};
writeln!(self.output, "{labels} {v}").map_err(Error::Write)?;
if let Some((popped_index, popped_path)) = popped_path {
self.path[popped_index] = Some(popped_path);
}
Ok(())
}
}
impl<'a, 'b, W: Write> serde::Serializer for &'a mut Serializer<'b, W> {
type Ok = ();
type Error = Error;
type SerializeSeq = Self;
type SerializeTuple = Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
type SerializeMap = MapSerializer<'a, 'b, W>;
type SerializeStruct = Self;
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
#[inline]
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
self.write_value(u8::from(v))
}
#[inline]
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
self.write_value(v)
}
#[inline]
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
Err(Error::NotSupported("char"))
}
#[inline]
fn serialize_str(self, _v: &str) -> Result<Self::Ok, Self::Error> {
Err(Error::NotSupported("&str"))
}
#[inline]
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(Error::NotSupported("&[u8]"))
}
#[inline]
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Error::NotSupported("unit struct"))
}
#[inline]
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Err(Error::NotSupported("unit variant"))
}
#[inline]
fn serialize_newtype_struct<T>(
self,
name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
let modifiers = match self.modifier_cache.entry(name) {
Entry::Occupied(inner) => inner.into_mut(),
Entry::Vacant(inner) => inner.insert(
ParsedModifiersList::parse(name)
.map_err(|e| Error::ParseModifiers(name, e.to_string()))?
.1,
),
};
let old_namespace = if let Some(new_namespace) = modifiers.internal.namespace.as_ref() {
let old_namespace = self.namespace.take();
self.namespace = Some(new_namespace.clone());
Some(old_namespace)
} else {
None
};
let has_label_modifiers = !modifiers.labels.is_empty();
if has_label_modifiers {
let labels = modifiers
.labels
.iter()
.filter_map(|definition| definition.build_label(&self.path).transpose());
self.labels.push(labels)?;
}
let (old_path, old_prevent_key_mod) = if modifiers.key_modifiers.is_empty() {
(None, None)
} else {
let mut new_path = self.path.clone();
let suffix = apply_path_modifications(&mut new_path, &modifiers.key_modifiers)?;
self.key.push(suffix.names).map_err(|val| {
Error::TooManyKeyPushers(MAX_ALLOWED_SUFFIX_PARTS, format!("{val:?}"))
})?;
(
Some(std::mem::replace(&mut self.path, new_path)),
suffix
.prevent_key_modification
.then(|| std::mem::replace(&mut self.prevent_key_modification, true)),
)
};
value.serialize(&mut *self)?;
if has_label_modifiers {
self.labels.pop();
}
if let Some(old_path) = old_path {
self.path = old_path;
self.key.pop();
}
if let Some(old_prevent_key_mod) = old_prevent_key_mod {
self.prevent_key_modification = old_prevent_key_mod;
}
if let Some(old_namespace) = old_namespace {
self.namespace = old_namespace;
}
Ok(())
}
#[inline]
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
Err(Error::NotSupported("newtype variant"))
}
#[inline]
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(self)
}
#[inline]
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(Error::NotSupported("tuple"))
}
#[inline]
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(Error::NotSupported("tuple struct"))
}
#[inline]
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(Error::NotSupported("tuple variant"))
}
#[inline]
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(MapSerializer {
root: self,
keys_pushed: None,
})
}
#[inline]
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(self)
}
#[inline]
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(Error::NotSupported("struct variant"))
}
}
impl<W: Write> SerializeStruct for &mut Serializer<'_, W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
self.path
.push(key.into())
.map_err(|_| Error::TooManyNestedMetrics(MAX_DEPTH, key, self.path.to_string()))?;
value.serialize(&mut **self)?;
self.path.pop();
Ok(())
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<W: Write> SerializeSeq for &mut Serializer<'_, W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
struct MapSerializer<'a, 'b, W: Write> {
root: &'a mut Serializer<'b, W>,
keys_pushed: Option<u16>,
}
impl<W: Write> SerializeMap for MapSerializer<'_, '_, W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
let mut serializer = MapKeySerializer {
path: &mut self.root.path,
pushed: 0,
};
key.serialize(&mut serializer)?;
self.keys_pushed = Some(serializer.pushed);
Ok(())
}
#[inline]
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut *self.root)?;
for _ in 0..self
.keys_pushed
.take()
.expect("serialize_value called without serialize_key")
{
self.root.path.pop();
}
Ok(())
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
struct MapKeySerializer<'a, 'b> {
path: &'a mut PathStack<'b>,
pushed: u16,
}
impl<'a> MapKeySerializer<'_, 'a> {
#[inline]
fn push(&mut self, value: impl Into<CowArcStr<'a>>) -> Result<(), Error> {
self.path
.push(value.into())
.map_err(|_| Error::TooManyNestedMetrics(MAX_DEPTH, "map", self.path.to_string()))?;
self.pushed += 1;
Ok(())
}
}
impl serde::Serializer for &mut MapKeySerializer<'_, '_> {
type Ok = ();
type Error = Error;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
type SerializeMap = Impossible<Self::Ok, Self::Error>;
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
#[inline]
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
self.push(if v { "true" } else { "false" })
}
#[inline]
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v.into())
}
#[inline]
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v.into())
}
#[inline]
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v.into())
}
#[inline]
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
self.push(v)
}
#[inline]
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
self.serialize_u64(v.into())
}
#[inline]
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
self.serialize_u64(v.into())
}
#[inline]
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
self.serialize_u64(v.into())
}
#[inline]
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
self.push(v)
}
#[inline]
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
self.push(v.to_string())
}
#[inline]
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
self.push(v.to_string())
}
#[inline]
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
self.push(v)
}
#[inline]
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
self.push(v.to_string())
}
#[inline]
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(Error::NotSupported("bytes"))
}
#[inline]
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.push("")
}
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
self.push("")
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Error::NotSupported("unit struct"))
}
#[inline]
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
self.push(variant)
}
#[inline]
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(self)
}
#[inline]
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Ok(self)
}
#[inline]
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(Error::NotSupported("tuple struct"))
}
#[inline]
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(Error::NotSupported("tuple variant"))
}
#[inline]
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(Error::NotSupported("map"))
}
#[inline]
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Err(Error::NotSupported("struct"))
}
#[inline]
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(Error::NotSupported("struct variant"))
}
}
impl serde::ser::SerializeSeq for &mut MapKeySerializer<'_, '_> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl serde::ser::SerializeTuple for &mut MapKeySerializer<'_, '_> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
#[cfg(test)]
mod test {
use std::collections::BTreeMap;
use serde::{ser::SerializeMap, Serialize, Serializer};
mod serializer {
use metered::HitCount;
use serde::Serialize;
use std::collections::BTreeMap;
#[test]
fn basic() {
#[derive(Serialize)]
pub struct Root {
something: Test,
}
#[derive(Serialize)]
pub struct Test {
hello_world: Wrapper,
}
pub struct Wrapper(bool);
impl Serialize for Wrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct("<|hello=world", &self.0)
}
}
let input = Root {
something: Test {
hello_world: Wrapper(true),
},
};
#[allow(clippy::needless_borrow)]
let actual = crate::to_string(&input, None, &[]).unwrap();
assert_eq!(actual, "something_hello_world{hello = \"world\"} 1\n");
}
#[test]
fn overflows_labels() {
#[derive(Serialize)]
pub struct Root {
first: OtherWrapper,
something: Test,
}
pub struct OtherWrapper(bool);
impl Serialize for OtherWrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct("|hello=world", &self.0)
}
}
pub struct Test {
hello_world: Wrapper,
}
impl Serialize for Test {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct("|a=base", &self.hello_world)
}
}
pub struct Wrapper(bool);
impl Serialize for Wrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct(
"<|a[::]=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=12,l=12,m=13,n=14,o=15,p=16,q=17,r=18,s=19,t=20,u=21,v=22,w=23,x=24,y=25,z=26,aa=27,ab=28,ac=29,ad=30,ae=31,af=32,ag=33",
&self.0,
)
}
}
let input = Root {
first: OtherWrapper(true),
something: Test {
hello_world: Wrapper(true),
},
};
#[allow(clippy::needless_borrow)]
let actual = crate::to_string(&input, None, &[]);
insta::assert_snapshot!(actual.unwrap_err());
}
#[test]
fn skip_key_modification() {
#[derive(Serialize)]
pub struct Root {
something: Test,
}
#[derive(Serialize)]
pub struct Test {
hello_world: Wrapper,
}
pub struct Wrapper(bool);
impl Serialize for Wrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct("<.|hello=world", &self.0)
}
}
let input = Root {
something: Test {
hello_world: Wrapper(true),
},
};
#[allow(clippy::needless_borrow)]
let actual = crate::to_string(&input, None, &[]).unwrap();
assert_eq!(
actual,
"hello_world{hello = \"world\", path = \"something\"} 1\n"
);
}
#[test]
fn geoip() {
#[derive(Debug, Default)]
pub struct CountryCodeCount(HitCount);
impl Serialize for CountryCodeCount {
fn serialize<S: serde::ser::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_newtype_struct("!!<.|country_code==<", &self.0)
}
}
#[derive(Default, Serialize)]
struct GeoIpMetrics {
country_code: BTreeMap<String, CountryCodeCount>,
}
#[derive(Default, Serialize)]
struct Metrics {
geoip: GeoIpMetrics,
}
let mut metrics = Metrics::default();
for ch in 'A'..='Z' {
metrics
.geoip
.country_code
.insert(format!("A{ch}"), CountryCodeCount::default());
}
let actual = crate::to_string(&metrics, Some("example"), [("foo", "bar")]).unwrap();
insta::assert_snapshot!(actual);
}
}
mod namespace {
use serde::Serialize;
#[test]
fn override_works() {
#[derive(Serialize)]
pub struct Root {
something: Test,
}
#[derive(Serialize)]
pub struct Test {
hello_world: Wrapper,
something_else: bool,
}
pub struct Wrapper(bool);
impl Serialize for Wrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct(
"<|hello=\"wor\\\"ld\"|:namespace=override",
&self.0,
)
}
}
let input = Root {
something: Test {
hello_world: Wrapper(true),
something_else: true,
},
};
#[allow(clippy::needless_borrow)]
let actual = crate::to_string(&input, Some("hello_world"), &[]).unwrap();
insta::assert_snapshot!(actual);
}
}
mod metered_integ {
use metered::{metered, HitCount, ResponseTime, Throughput};
use std::collections::HashMap;
#[derive(serde::Serialize)]
pub struct ServiceMetricRegistry<'a> {
biz: &'a BizMetrics,
baz: &'a BazMetrics,
}
#[derive(Default)]
pub struct Biz {
metrics: BizMetrics,
}
#[metered(registry = BizMetrics)]
impl Biz {
#[measure([HitCount, Throughput, ResponseTime])]
pub fn bizle(&self) {}
}
#[derive(Default)]
pub struct Baz {
metrics: BazMetrics,
}
#[metered(registry = BazMetrics)]
impl Baz {
#[measure([HitCount, Throughput, ResponseTime])]
pub fn bazle(&self) {}
}
#[test]
#[allow(clippy::pedantic)]
fn normal_registry() {
let biz = Biz::default();
let baz = Baz::default();
let ret = crate::to_string(
&ServiceMetricRegistry {
biz: &biz.metrics,
baz: &baz.metrics,
},
None,
HashMap::new(),
)
.unwrap();
insta::assert_snapshot!(ret);
}
#[test]
#[allow(clippy::pedantic)]
fn wrapped_registry() {
#[derive(serde::Serialize)]
pub struct MyWrapperRegistry<'a> {
wrapper: ServiceMetricRegistry<'a>,
}
let biz = Biz::default();
let baz = Baz::default();
let mut labels = HashMap::new();
labels.insert("service", "my_cool_service");
let ret = crate::to_string(
&MyWrapperRegistry {
wrapper: ServiceMetricRegistry {
biz: &biz.metrics,
baz: &baz.metrics,
},
},
Some("global"),
labels,
)
.unwrap();
insta::assert_snapshot!(ret);
}
}
mod compat {
use serde::{Serialize, Serializer};
#[test]
fn ensure_skips_lazy_evaluated_for_01_compat() {
#[derive(Serialize)]
pub struct A {
b: B,
}
pub struct B;
impl Serialize for B {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_newtype_struct("-----|", &2)
}
}
#[allow(clippy::needless_borrow)]
let ret = crate::to_string(&A { b: B }, Some("global"), &[]).unwrap();
assert_eq!(ret, "global_b 2\n");
}
}
#[test]
fn fake_nested_metrics() {
#[derive(Default)]
pub struct Metrics(BTreeMap<(u32, u32, u32, u32), u32>);
impl Serialize for Metrics {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for ((a, b, c, d), v) in &self.0 {
map.serialize_entry(
"my_metric",
&Label(
"!|a==<",
a,
Label("!|b==<", b, Label("!|c==<", c, Label("!|d==<", d, v))),
),
)?;
}
map.end()
}
}
struct Label<K, V>(&'static str, K, V);
impl<K: Serialize, V: Serialize> Serialize for Label<K, V> {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
PushToStackAndSerialize(&self.1, MetricAlias(self.0, &self.2)).serialize(ser)
}
}
struct PushToStackAndSerialize<K, V>(K, V);
impl<K: Serialize, V: Serialize> Serialize for PushToStackAndSerialize<K, V> {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
let mut map = ser.serialize_map(Some(1))?;
map.serialize_entry(&self.0, &self.1)?;
map.end()
}
}
pub struct MetricAlias<V>(&'static str, V);
impl<V: Serialize> Serialize for MetricAlias<V> {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_newtype_struct(self.0, &self.1)
}
}
let mut metric = Metrics::default();
metric.0.insert((0, 0, 0, 0), 123);
metric.0.insert((1, 2, 3, 4), 456);
#[allow(clippy::needless_borrow)]
let ret = crate::to_string(&metric, None, &[]).unwrap();
insta::assert_snapshot!(ret);
}
#[test]
fn tuple_unpacking() {
#[derive(Default, Serialize)]
pub struct Metrics(BTreeMap<(u8, u8, u8, u8), TakeValues<u64>>);
pub struct TakeValues<T>(T);
impl<T: Serialize> Serialize for TakeValues<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_newtype_struct("!!!!|a==!!!<,b==!!<,c==!<,d==<", &self.0)
}
}
let mut metrics = Metrics::default();
metrics.0.insert((0, 0, 0, 0), TakeValues(123));
metrics.0.insert((1, 2, 3, 4), TakeValues(456));
#[allow(clippy::needless_borrow)]
let ret = crate::to_string(&metrics, None, &[]).unwrap();
insta::assert_snapshot!(ret);
}
}