mod dimensions;
mod flags;
mod force;
mod formatter;
mod primitive;
mod quantized;
pub use dimensions::{WithDimension, WithDimensions, WithVecDimensions};
pub use force::{FlagConstructor, ForceFlag, ForceFlagEntryWriter};
pub use formatter::{FormattedValue, Lifted, NotLifted, ToString, ValueFormatter};
pub use quantized::{Quantized, QuantizingValueWriter, quantize_observation};
use std::{borrow::Cow, fmt::Write, sync::Arc};
pub use flags::{Distribution, MetricFlags, MetricOptions};
use crate::{
CowStr, Unit, ValidationError,
unit::UnitTag,
unit::{self, Convert, WithUnit},
};
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a metric value",
note = "If `{Self}` is a metric *entry*, flatten it using `#[metrics(flatten)]`"
)]
pub trait Value {
#[cfg(not(metrique_require_explicit_impls))]
const SHAPE: crate::descriptor::FieldShape<'static> = crate::descriptor::FieldShape::Opaque;
#[cfg(metrique_require_explicit_impls)]
const SHAPE: crate::descriptor::FieldShape<'static>;
#[cfg(not(metrique_require_explicit_impls))]
const UNIT: crate::Unit = crate::Unit::None;
#[cfg(metrique_require_explicit_impls)]
const UNIT: crate::Unit;
fn write(&self, writer: impl ValueWriter);
}
pub trait ValueWriter: Sized {
fn string(self, value: &str);
fn metric<'a>(
self,
distribution: impl IntoIterator<Item = Observation>,
unit: Unit,
dimensions: impl IntoIterator<Item = (&'a str, &'a str)>,
flags: MetricFlags<'_>,
);
fn error(self, error: ValidationError);
fn invalid(self, reason: impl Into<String>) {
self.error(ValidationError::invalid(reason))
}
#[cfg(not(metrique_require_explicit_impls))]
fn values<'a, V: Value + 'a>(self, values: impl IntoIterator<Item = &'a V>) {
write_values_as_string(self, values)
}
#[cfg(metrique_require_explicit_impls)]
fn values<'a, V: Value + 'a>(self, values: impl IntoIterator<Item = &'a V>);
}
#[doc(hidden)]
pub const VALUES_INLINE_CAPACITY: usize = 8;
pub fn write_values_as_string<'a, V: Value + 'a>(
writer: impl ValueWriter,
values: impl IntoIterator<Item = &'a V>,
) {
let mut buf = String::new();
for value in values {
let before = buf.len();
if !buf.is_empty() {
buf.push(',');
}
let after_sep = buf.len();
value.write(StringCapture(&mut buf));
if buf.len() <= after_sep {
buf.truncate(before);
}
}
writer.string(&buf);
}
pub(crate) struct StringCapture<'a>(pub(crate) &'a mut String);
impl ValueWriter for StringCapture<'_> {
fn string(self, value: &str) {
self.0.push_str(value);
}
fn metric<'a>(
self,
distribution: impl IntoIterator<Item = Observation>,
_unit: Unit,
_dimensions: impl IntoIterator<Item = (&'a str, &'a str)>,
_flags: MetricFlags<'_>,
) {
let mut first = true;
for obs in distribution {
if !first {
self.0.push(',');
}
first = false;
match obs {
Observation::Unsigned(v) => {
let _ = write!(self.0, "{v}");
}
Observation::Floating(v) => {
let _ = write!(self.0, "{v}");
}
Observation::Repeated { total, .. } => {
let _ = write!(self.0, "{total}");
}
}
}
}
fn error(self, _error: ValidationError) {}
fn values<'a, V: Value + 'a>(self, values: impl IntoIterator<Item = &'a V>) {
write_values_as_string(self, values)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum Observation {
Unsigned(u64),
Floating(f64),
Repeated {
total: f64,
occurrences: u64,
},
}
impl Value for Observation {
const SHAPE: crate::descriptor::FieldShape<'static> =
crate::descriptor::FieldShape::Known(crate::descriptor::KnownShape::F64);
const UNIT: crate::Unit = crate::Unit::None;
fn write(&self, writer: impl ValueWriter) {
writer.metric([*self], unit::None::UNIT, [], MetricFlags::empty())
}
}
impl MetricValue for Observation {
type Unit = unit::None;
}
pub trait MetricValue: Value {
type Unit: UnitTag;
fn with_unit<U: UnitTag>(self) -> WithUnit<Self, U>
where
Self: Sized,
Self::Unit: Convert<U>,
{
self.into()
}
fn with_dimension(self, key: impl Into<CowStr>, value: impl Into<CowStr>) -> WithDimension<Self>
where
Self: Sized,
{
WithDimension::new(self, key, value)
}
fn with_dimensions<C, I, const N: usize>(
self,
dimensions: impl IntoIterator<Item = (C, I)>,
) -> WithDimensions<Self, N>
where
Self: Sized,
C: Into<CowStr>,
I: Into<CowStr>,
{
WithDimensions::new_with_dimensions(self, dimensions)
}
fn quantized(
self,
bits: crate::quantize::SignificantBits,
rounding: crate::quantize::Rounding,
) -> Quantized<Self, crate::quantize::Quantizer>
where
Self: Sized,
{
Quantized::new(self, crate::quantize::Quantizer::new(bits, rounding))
}
}
impl<T: Value + ?Sized> Value for &T {
const SHAPE: crate::descriptor::FieldShape<'static> = T::SHAPE;
const UNIT: crate::Unit = T::UNIT;
fn write(&self, writer: impl ValueWriter) {
(**self).write(writer)
}
}
impl<T: Value> Value for Option<T> {
const SHAPE: crate::descriptor::FieldShape<'static> =
crate::descriptor::FieldShape::Optional(crate::descriptor::ShapeRef::new(&T::SHAPE));
const UNIT: crate::Unit = T::UNIT;
fn write(&self, writer: impl ValueWriter) {
if let Some(data) = self.as_ref() {
data.write(writer)
}
}
}
impl<T: Value> Value for Box<T> {
const SHAPE: crate::descriptor::FieldShape<'static> = T::SHAPE;
const UNIT: crate::Unit = T::UNIT;
fn write(&self, writer: impl ValueWriter) {
(**self).write(writer)
}
}
impl<T: Value + ?Sized> Value for Arc<T> {
const SHAPE: crate::descriptor::FieldShape<'static> = T::SHAPE;
const UNIT: crate::Unit = T::UNIT;
fn write(&self, writer: impl ValueWriter) {
(**self).write(writer)
}
}
impl<T: Value + ToOwned + ?Sized> Value for Cow<'_, T> {
const SHAPE: crate::descriptor::FieldShape<'static> = T::SHAPE;
const UNIT: crate::Unit = T::UNIT;
fn write(&self, writer: impl ValueWriter) {
(**self).write(writer)
}
}
impl<T: MetricValue + ?Sized> MetricValue for &T {
type Unit = T::Unit;
}
impl<T: MetricValue> MetricValue for Option<T> {
type Unit = T::Unit;
}
impl<T: MetricValue> MetricValue for Box<T> {
type Unit = T::Unit;
}
impl<T: MetricValue + ?Sized> MetricValue for Arc<T> {
type Unit = T::Unit;
}
impl<T: MetricValue + ToOwned + ?Sized> MetricValue for Cow<'_, T> {
type Unit = T::Unit;
}