use std::{
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use crate::{
observer::{Observer, with_observer_task_sync},
scope::{ScopeField, ScopeFrame, ScopeKind, pop_frame, push_frame},
};
pin_project! {
#[must_use = "Instrumented<F> is a future; await it to drive the inner future"]
pub struct Instrumented<F> {
#[pin]
inner: F,
scope_seed: Option<ScopeSeed>,
observer: Option<Arc<dyn Observer>>,
}
}
impl<F> std::fmt::Debug for Instrumented<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Instrumented")
.field("has_scope", &self.scope_seed.is_some())
.field("has_observer", &self.observer.is_some())
.finish()
}
}
#[derive(Debug, Clone)]
struct ScopeSeed {
fields: Vec<ScopeField>,
kind: ScopeKind,
tail_capacity: u16,
}
impl ScopeSeed {
fn into_frame(self) -> ScopeFrame {
ScopeFrame::new(self.fields, self.kind, self.tail_capacity)
}
fn from_frame(f: &ScopeFrame) -> Self {
Self {
fields: f.fields().to_vec(),
kind: f.kind(),
tail_capacity: f.tail_capacity(),
}
}
}
impl<F: Future> Future for Instrumented<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
let this = self.project();
let _scope_guard = this
.scope_seed
.as_ref()
.map(|seed| PollScopeGuard::push(seed.clone().into_frame()));
match this.observer.as_ref() {
Some(o) => with_observer_task_sync(o.clone(), || this.inner.poll(cx)),
None => this.inner.poll(cx),
}
}
}
struct PollScopeGuard;
impl PollScopeGuard {
fn push(frame: ScopeFrame) -> Self {
let _ = push_frame(frame);
Self
}
}
impl Drop for PollScopeGuard {
fn drop(&mut self) {
let _ = pop_frame();
}
}
pub trait Instrument: Future + Sized {
fn instrument(self, scope: ScopeFrame) -> Instrumented<Self> {
Instrumented {
inner: self,
scope_seed: Some(ScopeSeed::from_frame(&scope)),
observer: None,
}
}
}
impl<F: Future> Instrument for F {}
pub trait WithObserver: Future + Sized {
fn with_observer(self, observer: Arc<dyn Observer>) -> Instrumented<Self> {
Instrumented {
inner: self,
scope_seed: None,
observer: Some(observer),
}
}
}
impl<F: Future> WithObserver for F {}
impl<F: Future> Instrumented<F> {
pub fn instrument(mut self, scope: ScopeFrame) -> Self {
self.scope_seed = Some(ScopeSeed::from_frame(&scope));
self
}
pub fn with_observer(mut self, observer: Arc<dyn Observer>) -> Self {
self.observer = Some(observer);
self
}
}