#![allow(non_snake_case)]
use std::collections::HashMap as FxHashMap;
use serde::{Serialize, Deserialize};
use crate::observability::tracing::{RiTraceId, RiSpanId};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiTraceContext {
pub version: u8,
pub trace_id: RiTraceId,
pub parent_id: RiSpanId,
pub trace_flags: u8,
pub trace_state: Option<String>,
}
impl Default for RiTraceContext {
fn default() -> Self {
Self {
version: 0x00,
trace_id: RiTraceId::default(),
parent_id: RiSpanId::default(),
trace_flags: 0x01,
trace_state: None,
}
}
}
impl RiTraceContext {
#[allow(dead_code)]
pub fn new(trace_id: RiTraceId, parent_id: RiSpanId) -> Self {
Self {
version: 0x00,
trace_id,
parent_id,
trace_flags: 0x01, trace_state: None,
}
}
#[allow(dead_code)]
pub fn from_header(header: &str) -> Option<Self> {
let parts: Vec<&str> = header.split('-').collect();
if parts.len() != 4 {
return None;
}
let version = u8::from_str_radix(parts[0], 16).ok()?;
let trace_id = RiTraceId::from_string(parts[1].to_string());
let parent_id = RiSpanId::from_string(parts[2].to_string());
let trace_flags = u8::from_str_radix(parts[3], 16).ok()?;
Some(Self {
version,
trace_id,
parent_id,
trace_flags,
trace_state: None,
})
}
#[allow(dead_code)]
pub fn to_header(&self) -> String {
format!(
"{:02x}-{}-{}-{:02x}",
self.version,
self.trace_id.as_str(),
self.parent_id.as_str(),
self.trace_flags
)
}
#[allow(dead_code)]
pub fn is_sampled(&self) -> bool {
(self.trace_flags & 0x01) != 0
}
#[allow(dead_code)]
pub fn set_sampled(&mut self, sampled: bool) {
if sampled {
self.trace_flags |= 0x01;
} else {
self.trace_flags &= !0x01;
}
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiTraceContext {
#[new]
fn py_new() -> Self {
Self::default()
}
#[staticmethod]
#[pyo3(name = "from_header")]
fn py_from_header(header: String) -> Option<Self> {
Self::from_header(&header)
}
#[pyo3(name = "to_header")]
fn py_to_header(&self) -> String {
self.to_header()
}
#[pyo3(name = "is_sampled")]
fn py_is_sampled(&self) -> bool {
self.is_sampled()
}
fn sampled(&mut self, sampled: bool) {
self.set_sampled(sampled);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiBaggage {
items: FxHashMap<String, String>,
}
impl Default for RiBaggage {
fn default() -> Self {
Self::new()
}
}
impl RiBaggage {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
items: FxHashMap::default(),
}
}
#[allow(dead_code)]
pub fn insert(&mut self, key: String, value: String) {
self.items.insert(key, value);
}
#[allow(dead_code)]
pub fn get(&self, key: &str) -> Option<&String> {
self.items.get(key)
}
#[allow(dead_code)]
pub fn remove(&mut self, key: &str) {
self.items.remove(key);
}
#[allow(dead_code)]
pub fn from_header(header: &str) -> Self {
let mut baggage = Self::new();
for item in header.split(',') {
let item = item.trim();
if let Some(eq_pos) = item.find('=') {
let key = item[..eq_pos].trim().to_string();
let value = item[eq_pos + 1..].trim().to_string();
baggage.insert(key, value);
}
}
baggage
}
#[allow(dead_code)]
pub fn to_header(&self) -> String {
self.items
.iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join(",")
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiBaggage {
#[new]
fn py_new() -> Self {
Self::new()
}
#[staticmethod]
#[pyo3(name = "from_header")]
fn py_from_header(header: String) -> Self {
Self::from_header(&header)
}
#[pyo3(name = "to_header")]
fn py_to_header(&self) -> String {
self.to_header()
}
fn add(&mut self, key: String, value: String) {
self.items.insert(key, value);
}
fn fetch(&self, key: String) -> Option<String> {
self.items.get(&key).cloned()
}
fn delete(&mut self, key: String) {
self.items.remove(&key);
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiContextCarrier {
pub trace_context: Option<RiTraceContext>,
pub baggage: RiBaggage,
}
impl Default for RiContextCarrier {
fn default() -> Self {
Self::new()
}
}
impl RiContextCarrier {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
trace_context: None,
baggage: RiBaggage::new(),
}
}
#[allow(dead_code)]
pub fn with_trace_context(mut self, trace_context: RiTraceContext) -> Self {
self.trace_context = Some(trace_context);
self
}
#[allow(dead_code)]
pub fn with_baggage(mut self, baggage: RiBaggage) -> Self {
self.baggage = baggage;
self
}
#[allow(dead_code)]
pub fn from_tracing_context(tracing_context: &crate::observability::tracing::RiTracingContext) -> Self {
let mut carrier = Self::new();
if let (Some(trace_id), Some(span_id)) = (
tracing_context.trace_id(),
tracing_context.span_id()
) {
let trace_context = RiTraceContext::new(
trace_id.clone(),
span_id.clone()
);
carrier.trace_context = Some(trace_context);
}
let baggage = RiBaggage::new();
carrier.baggage = baggage;
carrier
}
#[allow(dead_code)]
pub fn into_tracing_context(self) -> crate::observability::tracing::RiTracingContext {
let mut context = crate::observability::tracing::RiTracingContext::new();
if let Some(trace_context) = self.trace_context {
context = context.with_trace_id(trace_context.trace_id);
context = context.with_span_id(trace_context.parent_id);
}
context
}
#[allow(dead_code)]
pub fn from_headers(headers: &FxHashMap<String, String>) -> Self {
let mut carrier = Self::new();
if let Some(traceparent) = headers.get("traceparent") {
if let Some(trace_context) = RiTraceContext::from_header(traceparent) {
carrier.trace_context = Some(trace_context);
}
}
if let Some(baggage_header) = headers.get("baggage") {
carrier.baggage = RiBaggage::from_header(baggage_header);
}
carrier
}
#[allow(dead_code)]
pub fn inject_into_headers(&self, headers: &mut FxHashMap<String, String>) {
if let Some(ref trace_context) = self.trace_context {
headers.insert("traceparent".to_string(), trace_context.to_header());
}
let baggage_header = self.baggage.to_header();
if !baggage_header.is_empty() {
headers.insert("baggage".to_string(), baggage_header);
}
}
#[allow(dead_code)]
pub fn from_headers_and_set_current(headers: &FxHashMap<String, String>) -> Self {
let carrier = Self::from_headers(headers);
let tracing_context = carrier.clone().into_tracing_context();
tracing_context.set_as_current();
carrier
}
#[allow(dead_code)]
pub fn inject_current_into_headers(headers: &mut FxHashMap<String, String>) {
if let Some(tracing_context) = crate::observability::tracing::RiTracingContext::current() {
let carrier = Self::from_tracing_context(&tracing_context);
carrier.inject_into_headers(headers);
}
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiContextCarrier {
#[new]
fn py_new() -> Self {
Self::new()
}
#[pyo3(name = "with_trace_context")]
fn py_with_trace_context(&mut self, trace_context: RiTraceContext) {
self.trace_context = Some(trace_context);
}
#[pyo3(name = "get_trace_context")]
fn py_get_trace_context(&self) -> Option<RiTraceContext> {
self.trace_context.clone()
}
#[pyo3(name = "with_baggage")]
fn py_with_baggage(&mut self, baggage: RiBaggage) {
self.baggage = baggage;
}
#[pyo3(name = "get_baggage")]
fn py_get_baggage(&self) -> RiBaggage {
self.baggage.clone()
}
#[pyo3(name = "inject_into_headers")]
fn py_inject_into_headers(&self) -> FxHashMap<String, String> {
let mut headers = FxHashMap::default();
self.inject_into_headers(&mut headers);
headers
}
#[staticmethod]
#[pyo3(name = "from_headers")]
fn py_from_headers(headers: FxHashMap<String, String>) -> Self {
Self::from_headers(&headers)
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct W3CTracePropagator;
impl W3CTracePropagator {
#[allow(dead_code)]
pub fn new() -> Self {
Self
}
#[allow(dead_code)]
pub fn extract(&self, headers: &FxHashMap<String, String>) -> RiContextCarrier {
RiContextCarrier::from_headers(headers)
}
#[allow(dead_code)]
pub fn inject(&self, carrier: &RiContextCarrier, headers: &mut FxHashMap<String, String>) {
carrier.inject_into_headers(headers);
}
#[allow(dead_code)]
pub fn extract_and_set_current(&self, headers: &FxHashMap<String, String>) -> RiContextCarrier {
RiContextCarrier::from_headers_and_set_current(headers)
}
#[allow(dead_code)]
pub fn inject_current(&self, headers: &mut FxHashMap<String, String>) {
RiContextCarrier::inject_current_into_headers(headers);
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl W3CTracePropagator {
#[new]
fn py_new() -> Self {
Self::new()
}
#[pyo3(name = "extract")]
fn py_extract(&self, headers: FxHashMap<String, String>) -> RiContextCarrier {
self.extract(&headers)
}
#[pyo3(name = "inject")]
fn py_inject(&self, carrier: &RiContextCarrier) -> FxHashMap<String, String> {
let mut headers = FxHashMap::default();
self.inject(carrier, &mut headers);
headers
}
#[pyo3(name = "extract_and_set_current")]
fn py_extract_and_set_current(&self, headers: FxHashMap<String, String>) -> RiContextCarrier {
self.extract_and_set_current(&headers)
}
#[pyo3(name = "inject_current")]
fn py_inject_current(&self) -> FxHashMap<String, String> {
let mut headers = FxHashMap::default();
self.inject_current(&mut headers);
headers
}
}