use std::ops::DerefMut;
pub struct Instrumented<T, U> {
value: T,
metrics: U,
}
pub type Result<T, E, Metrics> = Instrumented<std::result::Result<T, E>, Metrics>;
impl<T, U> Instrumented<T, U> {
pub fn discard_metrics(self) -> T {
self.value
}
pub fn into_parts(self) -> (T, U) {
(self.value, self.metrics)
}
pub fn split_metrics_to(self, mut target: impl DerefMut<Target = Option<U>>) -> T {
*target = Some(self.metrics);
self.value
}
pub fn from_parts(value: T, metrics: U) -> Self {
Self { value, metrics }
}
pub fn instrument(mut metrics: U, f: impl FnOnce(&mut U) -> T) -> Self {
let value = f(&mut metrics);
Self { value, metrics }
}
pub async fn instrument_async(mut metrics: U, f: impl AsyncFnOnce(&mut U) -> T) -> Self {
let value = f(&mut metrics).await;
Self { value, metrics }
}
pub fn finalize_metrics(mut self, f: impl FnOnce(&T, &mut U)) -> Self {
f(&self.value, &mut self.metrics);
self
}
}
impl<T, E, U> Instrumented<std::result::Result<T, E>, U> {
pub fn on_error(self, f: impl FnOnce(&E, &mut U)) -> Self {
self.finalize_metrics(|res, metrics| {
if let Err(e) = res {
f(e, metrics);
}
})
}
pub fn on_success(self, f: impl FnOnce(&T, &mut U)) -> Self {
self.finalize_metrics(|res, metrics| {
if let Ok(v) = res {
f(v, metrics);
}
})
}
}
impl<T, Entry, Sink> Instrumented<T, crate::AppendAndCloseOnDrop<Entry, Sink>>
where
Entry: crate::CloseEntry,
Sink: crate::EntrySink<crate::RootEntry<Entry::Closed>>,
{
pub fn emit(self) -> T {
let (value, metrics) = self.into_parts();
drop(metrics); value
}
}