1use std::sync::Arc;
8
9use obs_proto::obs::v1::{ObsEnvelope, SamplingReason as PSamplingReason};
10
11use super::{ScopeField, ScopeFrame, ScopeKind, pop_frame, push_frame};
12use crate::observer::{Observer, enter_emit_envelope};
13
14#[must_use = "the scope guard is popped on Drop; bind to a name like `_scope`"]
19pub struct ScopeGuard {
20 armed: bool,
22}
23
24impl std::fmt::Debug for ScopeGuard {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 f.debug_struct("ScopeGuard")
27 .field("armed", &self.armed)
28 .finish()
29 }
30}
31
32impl ScopeGuard {
33 pub fn enter(fields: Vec<ScopeField>, tail_capacity: u16) -> Self {
35 let _ = push_frame(ScopeFrame::new(fields, ScopeKind::Scope, tail_capacity));
36 Self { armed: true }
37 }
38
39 pub fn enter_context(fields: Vec<ScopeField>) -> Self {
41 let _ = push_frame(ScopeFrame::new(fields, ScopeKind::Context, 0));
42 Self { armed: true }
43 }
44
45 pub fn enter_with_identity(
48 fields: Vec<ScopeField>,
49 kind: ScopeKind,
50 tail_capacity: u16,
51 name: &'static str,
52 target: &'static str,
53 ) -> Self {
54 let mut frame = ScopeFrame::new(fields, kind, tail_capacity);
55 frame.set_span_identity(name, target);
56 let _ = push_frame(frame);
57 Self { armed: true }
58 }
59
60 pub fn enter_with_frame(frame: ScopeFrame) -> Self {
66 let _ = push_frame(frame);
67 Self { armed: true }
68 }
69
70 #[must_use]
75 pub fn into_inner(mut self) -> ScopeFrame {
76 self.armed = false;
77 pop_frame().unwrap_or_else(|| ScopeFrame::new(Vec::new(), ScopeKind::Scope, 64))
82 }
83}
84
85impl Drop for ScopeGuard {
86 fn drop(&mut self) {
87 if !self.armed {
88 return;
89 }
90 let Some(frame) = pop_frame() else {
91 return;
92 };
93 finish_scope_frame(frame);
94 }
95}
96
97pub(crate) fn finish_scope_frame(mut frame: ScopeFrame) {
98 if frame.kind() != ScopeKind::Scope {
99 return;
100 }
101 if !frame.seen_error() {
102 return;
103 }
104 flush_tail_buffer(&mut frame);
105}
106
107fn flush_tail_buffer(frame: &mut ScopeFrame) {
108 let observer = crate::observer::observer();
109 flush_through(&observer, frame);
110}
111
112fn flush_through(observer: &Arc<dyn Observer>, frame: &mut ScopeFrame) {
113 for mut env in frame.drain_tail() {
114 env.sampling_reason =
115 ::buffa::EnumValue::Known(PSamplingReason::SAMPLING_REASON_TAIL_ERROR);
116 flush_one(observer, env);
121 }
122}
123
124fn flush_one(observer: &Arc<dyn Observer>, env: ObsEnvelope) {
125 enter_emit_envelope(observer, env);
129}