use crate::{span, tracer, Annotation, CurrentGuard, Endpoint, Kind, TraceContext};
use pin_project_lite::pin_project;
use std::future::Future;
use std::mem;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Instant;
pub struct Attached(CurrentGuard);
pub struct Detached(());
#[allow(clippy::large_enum_variant)]
pub(crate) enum SpanState {
Real {
span: span::Builder,
start_instant: Instant,
},
Nop,
}
pub struct OpenSpan<T> {
_mode: T,
context: TraceContext,
state: SpanState,
}
impl<T> Drop for OpenSpan<T> {
fn drop(&mut self) {
if let SpanState::Real {
span,
start_instant,
} = &mut self.state
{
if let Some(tracer) = tracer::TRACER.borrow() {
let span = span.duration(start_instant.elapsed()).build();
tracer.reporter.report(span);
}
}
}
}
impl<T> OpenSpan<T> {
#[inline]
pub fn context(&self) -> TraceContext {
self.context
}
#[inline]
pub fn name(&mut self, name: &str) {
if let SpanState::Real { span, .. } = &mut self.state {
span.name(name);
}
}
#[inline]
pub fn with_name(mut self, name: &str) -> OpenSpan<T> {
self.name(name);
self
}
#[inline]
pub fn kind(&mut self, kind: Kind) {
if let SpanState::Real { span, .. } = &mut self.state {
span.kind(kind);
}
}
#[inline]
pub fn with_kind(mut self, kind: Kind) -> OpenSpan<T> {
self.kind(kind);
self
}
#[inline]
pub fn remote_endpoint(&mut self, remote_endpoint: Endpoint) {
if let SpanState::Real { span, .. } = &mut self.state {
span.remote_endpoint(remote_endpoint);
}
}
#[inline]
pub fn with_remote_endpoint(mut self, remote_endpoint: Endpoint) -> OpenSpan<T> {
self.remote_endpoint(remote_endpoint);
self
}
#[inline]
pub fn annotate(&mut self, value: &str) {
if let SpanState::Real { span, .. } = &mut self.state {
let annotation = Annotation::now(value);
span.annotation(annotation);
}
}
#[inline]
pub fn with_annotation(mut self, value: &str) -> OpenSpan<T> {
self.annotate(value);
self
}
#[inline]
pub fn tag(&mut self, key: &str, value: &str) {
if let SpanState::Real { span, .. } = &mut self.state {
span.tag(key, value);
}
}
#[inline]
pub fn with_tag(mut self, key: &str, value: &str) -> OpenSpan<T> {
self.tag(key, value);
self
}
}
impl OpenSpan<Attached> {
#[inline]
pub(crate) fn new(context: TraceContext, state: SpanState) -> OpenSpan<Attached> {
OpenSpan {
_mode: Attached(crate::set_current(context)),
context,
state,
}
}
#[inline]
pub fn detach(mut self) -> OpenSpan<Detached> {
OpenSpan {
_mode: Detached(()),
context: self.context,
state: mem::replace(&mut self.state, SpanState::Nop),
}
}
}
impl OpenSpan<Detached> {
#[inline]
pub fn attach(mut self) -> OpenSpan<Attached> {
OpenSpan {
_mode: Attached(crate::set_current(self.context)),
context: self.context,
state: mem::replace(&mut self.state, SpanState::Nop),
}
}
#[inline]
pub fn bind<F>(self, future: F) -> Bind<F>
where
F: Future,
{
Bind {
span: Some(self),
future,
}
}
}
pin_project! {
pub struct Bind<T> {
span: Option<OpenSpan<Detached>>,
#[pin]
future: T,
}
}
impl<T> Future for Bind<T>
where
T: Future,
{
type Output = T::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let _guard = crate::set_current(
this.span
.as_ref()
.expect("future polled after completion")
.context(),
);
let r = this.future.poll(cx);
if r.is_ready() {
*this.span = None;
}
r
}
}