1use std::collections::VecDeque;
4
5use obs_proto::obs::v1::ObsEnvelope;
6
7use crate::codegen_helpers::SpanFrame;
8
9#[derive(Debug, Clone)]
13pub enum ScopeField {
14 TraceId(String),
16 SpanId(String),
18 ParentSpanId(String),
20 Label(&'static str, String),
22}
23
24impl ScopeField {
25 #[must_use]
27 pub fn name(&self) -> &'static str {
28 match self {
29 Self::TraceId(_) => "trace_id",
30 Self::SpanId(_) => "span_id",
31 Self::ParentSpanId(_) => "parent_span_id",
32 Self::Label(k, _) => k,
33 }
34 }
35
36 #[must_use]
38 pub fn value(&self) -> &str {
39 match self {
40 Self::TraceId(v) | Self::SpanId(v) | Self::ParentSpanId(v) | Self::Label(_, v) => v,
41 }
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48#[non_exhaustive]
49pub enum ScopeKind {
50 Scope,
52 Context,
54}
55
56#[derive(Debug, Clone)]
58pub struct ScopeFrame {
59 fields: Vec<ScopeField>,
60 kind: ScopeKind,
61 tail_capacity: u16,
62 tail_buffer: VecDeque<ObsEnvelope>,
63 seen_error: bool,
64 traceparent_sampled: Option<bool>,
65 span_name: Option<&'static str>,
66 span_target: Option<&'static str>,
67}
68
69impl ScopeFrame {
70 #[must_use]
73 pub fn new(fields: Vec<ScopeField>, kind: ScopeKind, tail_capacity: u16) -> Self {
74 let cap = match kind {
75 ScopeKind::Scope => tail_capacity as usize,
76 ScopeKind::Context => 0,
77 };
78 Self {
79 fields,
80 kind,
81 tail_capacity,
82 tail_buffer: VecDeque::with_capacity(cap),
83 seen_error: false,
84 traceparent_sampled: None,
85 span_name: None,
86 span_target: None,
87 }
88 }
89
90 pub fn set_traceparent_sampled(&mut self, sampled: bool) {
93 self.traceparent_sampled = Some(sampled);
94 }
95
96 pub fn set_span_identity(&mut self, name: &'static str, target: &'static str) {
99 self.span_name = Some(name);
100 self.span_target = Some(target);
101 }
102
103 #[must_use]
105 pub fn traceparent_sampled(&self) -> Option<bool> {
106 self.traceparent_sampled
107 }
108
109 #[must_use]
111 pub fn fields(&self) -> &[ScopeField] {
112 &self.fields
113 }
114
115 #[must_use]
117 pub fn kind(&self) -> ScopeKind {
118 self.kind
119 }
120
121 #[must_use]
123 pub fn seen_error(&self) -> bool {
124 self.seen_error
125 }
126
127 pub fn mark_error(&mut self) {
129 self.seen_error = true;
130 }
131
132 #[must_use]
134 pub fn tail_capacity(&self) -> u16 {
135 self.tail_capacity
136 }
137
138 pub fn push_tail(&mut self, env: ObsEnvelope) {
140 if self.kind == ScopeKind::Context {
141 return;
142 }
143 if self.tail_capacity == 0 {
144 return;
145 }
146 if self.tail_buffer.len() >= self.tail_capacity as usize {
147 self.tail_buffer.pop_front();
148 }
149 self.tail_buffer.push_back(env);
150 }
151
152 #[must_use]
155 pub fn drain_tail(&mut self) -> Vec<ObsEnvelope> {
156 self.tail_buffer.drain(..).collect()
157 }
158
159 #[must_use]
161 pub fn tail_snapshot(&self) -> Vec<ObsEnvelope> {
162 self.tail_buffer.iter().cloned().collect()
163 }
164
165 #[must_use]
167 pub fn as_span_frame(&self) -> Option<SpanFrame<'_>> {
168 Some(SpanFrame {
169 name: self.span_name?,
170 target: self.span_target?,
171 })
172 }
173}