use std::sync::Arc;
use obs_proto::obs::v1::{ObsEnvelope, SamplingReason as PSamplingReason};
use super::{ScopeField, ScopeFrame, ScopeKind, pop_frame, push_frame};
use crate::observer::{Observer, enter_emit_envelope};
#[must_use = "the scope guard is popped on Drop; bind to a name like `_scope`"]
pub struct ScopeGuard {
armed: bool,
}
impl std::fmt::Debug for ScopeGuard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScopeGuard")
.field("armed", &self.armed)
.finish()
}
}
impl ScopeGuard {
pub fn enter(fields: Vec<ScopeField>, tail_capacity: u16) -> Self {
let _ = push_frame(ScopeFrame::new(fields, ScopeKind::Scope, tail_capacity));
Self { armed: true }
}
pub fn enter_context(fields: Vec<ScopeField>) -> Self {
let _ = push_frame(ScopeFrame::new(fields, ScopeKind::Context, 0));
Self { armed: true }
}
pub fn enter_with_identity(
fields: Vec<ScopeField>,
kind: ScopeKind,
tail_capacity: u16,
name: &'static str,
target: &'static str,
) -> Self {
let mut frame = ScopeFrame::new(fields, kind, tail_capacity);
frame.set_span_identity(name, target);
let _ = push_frame(frame);
Self { armed: true }
}
pub fn enter_with_frame(frame: ScopeFrame) -> Self {
let _ = push_frame(frame);
Self { armed: true }
}
#[must_use]
pub fn into_inner(mut self) -> ScopeFrame {
self.armed = false;
pop_frame().unwrap_or_else(|| ScopeFrame::new(Vec::new(), ScopeKind::Scope, 64))
}
}
impl Drop for ScopeGuard {
fn drop(&mut self) {
if !self.armed {
return;
}
let Some(frame) = pop_frame() else {
return;
};
finish_scope_frame(frame);
}
}
pub(crate) fn finish_scope_frame(mut frame: ScopeFrame) {
if frame.kind() != ScopeKind::Scope {
return;
}
if !frame.seen_error() {
return;
}
flush_tail_buffer(&mut frame);
}
fn flush_tail_buffer(frame: &mut ScopeFrame) {
let observer = crate::observer::observer();
flush_through(&observer, frame);
}
fn flush_through(observer: &Arc<dyn Observer>, frame: &mut ScopeFrame) {
for mut env in frame.drain_tail() {
env.sampling_reason =
::buffa::EnumValue::Known(PSamplingReason::SAMPLING_REASON_TAIL_ERROR);
flush_one(observer, env);
}
}
fn flush_one(observer: &Arc<dyn Observer>, env: ObsEnvelope) {
enter_emit_envelope(observer, env);
}