#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::str::FromStr;
pub const TRACE_FLAG_NOT_SAMPLED: u8 = 0x00;
pub const TRACE_FLAG_SAMPLED: u8 = 0x01;
pub const TRACE_FLAG_DEFERRED: u8 = 0x02;
pub const TRACE_FLAG_DEBUG: u8 = 0x04;
lazy_static::lazy_static! {
static ref TRACE_STATE_KEY: regex::Regex = regex::Regex::new(r#"^[a-z0-9]+[a-z0-9|\\_|\\-|\\*|\\/]*$"#).unwrap();
}
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash)]
pub struct TraceId(u128);
impl TraceId {
pub fn invalid() -> Self {
TraceId(0)
}
pub fn from_u128(item: u128) -> Self {
TraceId(item)
}
pub fn to_u128(self) -> u128 {
self.0
}
pub fn to_hex(self) -> String {
format!("{:032x}", self.0)
}
pub fn to_byte_array(self) -> [u8; 16] {
self.0.to_be_bytes()
}
pub fn from_hex(hex: &str) -> Self {
TraceId(u128::from_str_radix(hex, 16).unwrap_or(0))
}
pub fn from_byte_array(byte_array: [u8; 16]) -> Self {
TraceId(u128::from_be_bytes(byte_array))
}
}
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash)]
pub struct SpanId(u64);
impl SpanId {
pub fn invalid() -> Self {
SpanId(0)
}
pub fn from_u64(item: u64) -> Self {
SpanId(item)
}
pub fn to_u64(self) -> u64 {
self.0
}
pub fn to_hex(self) -> String {
format!("{:016x}", self.0)
}
pub fn to_byte_array(self) -> [u8; 8] {
self.0.to_be_bytes()
}
pub fn from_hex(hex: &str) -> Self {
SpanId(u64::from_str_radix(hex, 16).unwrap_or(0))
}
pub fn from_byte_array(byte_array: [u8; 8]) -> Self {
SpanId(u64::from_be_bytes(byte_array))
}
}
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TraceState(Option<VecDeque<(String, String)>>);
impl TraceState {
fn valid_key(key: &str) -> bool {
if key.len() > 256 {
return false;
}
TRACE_STATE_KEY.is_match(key)
}
fn valid_value(value: &str) -> bool {
if value.len() > 256 {
return false;
}
!(value.contains(',') || value.contains('='))
}
#[allow(clippy::all)]
pub fn from_key_value<T, K, V>(trace_state: T) -> Result<Self, ()>
where
T: IntoIterator<Item = (K, V)>,
K: ToString,
V: ToString,
{
let ordered_data = trace_state
.into_iter()
.map(|(key, value)| {
let (key, value) = (key.to_string(), value.to_string());
if !TraceState::valid_key(key.as_str()) || !TraceState::valid_value(value.as_str())
{
return Err(());
}
Ok((key, value))
})
.collect::<Result<VecDeque<_>, ()>>()?;
if ordered_data.is_empty() {
Ok(TraceState(None))
} else {
Ok(TraceState(Some(ordered_data)))
}
}
pub fn get(&self, key: &str) -> Option<&str> {
self.0.as_ref().and_then(|kvs| {
kvs.iter().find_map(|item| {
if item.0.as_str() == key {
Some(item.1.as_str())
} else {
None
}
})
})
}
#[allow(clippy::all)]
pub fn insert(&self, key: String, value: String) -> Result<TraceState, ()> {
if !TraceState::valid_key(key.as_str()) || !TraceState::valid_value(value.as_str()) {
return Err(());
}
let mut trace_state = self.delete(key.clone())?;
let kvs = trace_state.0.get_or_insert(VecDeque::with_capacity(1));
kvs.push_front((key, value));
Ok(trace_state)
}
#[allow(clippy::all)]
pub fn delete(&self, key: String) -> Result<TraceState, ()> {
if !TraceState::valid_key(key.as_str()) {
return Err(());
}
let mut owned = self.clone();
let kvs = owned.0.as_mut().ok_or(())?;
if let Some(index) = kvs.iter().position(|x| *x.0 == *key) {
kvs.remove(index);
} else {
return Err(());
}
Ok(owned)
}
pub fn header(&self) -> String {
self.header_delimited("=", ",")
}
pub fn header_delimited(&self, entry_delimiter: &str, list_delimiter: &str) -> String {
self.0
.as_ref()
.map(|kvs| {
kvs.iter()
.map(|(key, value)| format!("{}{}{}", key, entry_delimiter, value))
.collect::<Vec<String>>()
.join(list_delimiter)
})
.unwrap_or_default()
}
}
impl FromStr for TraceState {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let list_members: Vec<&str> = s.split_terminator(',').collect();
let mut key_value_pairs: Vec<(String, String)> = Vec::with_capacity(list_members.len());
for list_member in list_members {
match list_member.find('=') {
None => return Err(()),
Some(separator_index) => {
let (key, value) = list_member.split_at(separator_index);
key_value_pairs
.push((key.to_string(), value.trim_start_matches('=').to_string()));
}
}
}
Ok(TraceState::from_key_value(key_value_pairs)?)
}
}
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct SpanContext {
trace_id: TraceId,
span_id: SpanId,
trace_flags: u8,
is_remote: bool,
trace_state: TraceState,
}
impl SpanContext {
pub fn empty_context() -> Self {
SpanContext::new(
TraceId::invalid(),
SpanId::invalid(),
0,
false,
TraceState::default(),
)
}
pub fn new(
trace_id: TraceId,
span_id: SpanId,
trace_flags: u8,
is_remote: bool,
trace_state: TraceState,
) -> Self {
SpanContext {
trace_id,
span_id,
trace_flags,
is_remote,
trace_state,
}
}
pub fn trace_id(&self) -> TraceId {
self.trace_id
}
pub fn span_id(&self) -> SpanId {
self.span_id
}
pub fn trace_flags(&self) -> u8 {
self.trace_flags
}
pub fn is_valid(&self) -> bool {
self.trace_id.0 != 0 && self.span_id.0 != 0
}
pub fn is_remote(&self) -> bool {
self.is_remote
}
pub fn is_deferred(&self) -> bool {
(self.trace_flags & TRACE_FLAG_DEFERRED) == TRACE_FLAG_DEFERRED
}
pub fn is_debug(&self) -> bool {
(self.trace_flags & TRACE_FLAG_DEBUG) == TRACE_FLAG_DEBUG
}
pub fn is_sampled(&self) -> bool {
(self.trace_flags & TRACE_FLAG_SAMPLED) == TRACE_FLAG_SAMPLED
}
pub fn trace_state(&self) -> &TraceState {
&self.trace_state
}
}
#[cfg(test)]
mod tests {
use super::*;
#[rustfmt::skip]
fn trace_id_test_data() -> Vec<(TraceId, &'static str, [u8; 16])> {
vec![
(TraceId(0), "00000000000000000000000000000000", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
(TraceId(42), "0000000000000000000000000000002a", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42]),
(TraceId(126642714606581564793456114182061442190), "5f467fe7bf42676c05e20ba4a90e448e", [95, 70, 127, 231, 191, 66, 103, 108, 5, 226, 11, 164, 169, 14, 68, 142])
]
}
#[rustfmt::skip]
fn span_id_test_data() -> Vec<(SpanId, &'static str, [u8; 8])> {
vec![
(SpanId(0), "0000000000000000", [0, 0, 0, 0, 0, 0, 0, 0]),
(SpanId(42), "000000000000002a", [0, 0, 0, 0, 0, 0, 0, 42]),
(SpanId(5508496025762705295), "4c721bf33e3caf8f", [76, 114, 27, 243, 62, 60, 175, 143])
]
}
#[rustfmt::skip]
fn trace_state_test_data() -> Vec<(TraceState, &'static str, &'static str)> {
vec![
(TraceState::from_key_value(vec![("foo", "bar")]).unwrap(), "foo=bar", "foo"),
(TraceState::from_key_value(vec![("foo", ""), ("apple", "banana")]).unwrap(), "foo=,apple=banana", "apple"),
(TraceState::from_key_value(vec![("foo", "bar"), ("apple", "banana")]).unwrap(), "foo=bar,apple=banana", "apple"),
]
}
#[test]
fn test_trace_id() {
for test_case in trace_id_test_data() {
assert_eq!(test_case.0.to_hex(), test_case.1);
assert_eq!(test_case.0.to_byte_array(), test_case.2);
assert_eq!(test_case.0, TraceId::from_hex(test_case.1));
assert_eq!(test_case.0, TraceId::from_byte_array(test_case.2));
}
}
#[test]
fn test_span_id() {
for test_case in span_id_test_data() {
assert_eq!(test_case.0.to_hex(), test_case.1);
assert_eq!(test_case.0.to_byte_array(), test_case.2);
assert_eq!(test_case.0, SpanId::from_hex(test_case.1));
assert_eq!(test_case.0, SpanId::from_byte_array(test_case.2));
}
}
#[test]
fn test_trace_state() {
for test_case in trace_state_test_data() {
assert_eq!(test_case.0.clone().header(), test_case.1);
let new_key = format!("{}-{}", test_case.0.get(test_case.2).unwrap(), "test");
let updated_trace_state = test_case.0.insert(test_case.2.into(), new_key.clone());
assert!(updated_trace_state.is_ok());
let updated_trace_state = updated_trace_state.unwrap();
let updated = format!("{}={}", test_case.2, new_key);
let index = updated_trace_state.clone().header().find(&updated);
assert!(index.is_some());
assert_eq!(index.unwrap(), 0);
let deleted_trace_state = updated_trace_state.delete(test_case.2.to_string());
assert!(deleted_trace_state.is_ok());
let deleted_trace_state = deleted_trace_state.unwrap();
assert!(deleted_trace_state.get(test_case.2).is_none());
}
}
}