opentelemetry/propagation/
mod.rs

1//! # OpenTelemetry Propagator interface
2//! Cross-cutting concerns send their state to the next process using Propagators, which are defined
3//! as objects used to read and write context data to and from messages exchanged by the applications.
4//!
5//! `Propagator`s leverage the [`Context`] to inject and extract data for each cross-cutting concern,
6//! such as `TraceContext` and [`Baggage`].
7//!
8//! The Propagators API is expected to be leveraged by users writing instrumentation libraries.
9//!
10//! Currently, the following `Propagator` types are supported:
11//! -  [`TextMapPropagator`], inject values into and extracts values from carriers as string key/value pairs
12//!
13//! A binary Propagator type will be added in
14//! the future, See [tracking issues](https://github.com/open-telemetry/opentelemetry-specification/issues/437)).
15//!
16//! `Propagator`s uses [`Injector`] and [`Extractor`] to read and write context data to and from messages.
17//! Each specific Propagator type defines its expected carrier type, such as a string map or a byte array.
18//!
19//! [`Baggage`]: crate::baggage::Baggage
20//! [`Context`]: crate::Context
21
22use std::collections::HashMap;
23
24pub mod composite;
25pub mod text_map_propagator;
26
27pub use composite::TextMapCompositePropagator;
28pub use text_map_propagator::TextMapPropagator;
29
30/// Injector provides an interface for adding fields from an underlying struct like `HashMap`
31pub trait Injector {
32    /// Add a key and value to the underlying data.
33    fn set(&mut self, key: &str, value: String);
34}
35
36/// Extractor provides an interface for removing fields from an underlying struct like `HashMap`
37pub trait Extractor {
38    /// Get a value from a key from the underlying data.
39    fn get(&self, key: &str) -> Option<&str>;
40
41    /// Collect all the keys from the underlying data.
42    fn keys(&self) -> Vec<&str>;
43
44    /// Get all values from a key from the underlying data.
45    fn get_all(&self, key: &str) -> Option<Vec<&str>> {
46        self.get(key).map(|value| vec![value])
47    }
48}
49
50impl<S: std::hash::BuildHasher> Injector for HashMap<String, String, S> {
51    /// Set a key and value in the HashMap.
52    fn set(&mut self, key: &str, value: String) {
53        self.insert(key.to_lowercase(), value);
54    }
55}
56
57impl<S: std::hash::BuildHasher> Extractor for HashMap<String, String, S> {
58    /// Get a value for a key from the HashMap.
59    fn get(&self, key: &str) -> Option<&str> {
60        self.get(&key.to_lowercase()).map(|v| v.as_str())
61    }
62
63    /// Collect all the keys from the HashMap.
64    fn keys(&self) -> Vec<&str> {
65        self.keys().map(|k| k.as_str()).collect::<Vec<_>>()
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn hash_map_get() {
75        let mut carrier = HashMap::new();
76        carrier.set("headerName", "value".to_string());
77
78        assert_eq!(
79            Extractor::get(&carrier, "HEADERNAME"),
80            Some("value"),
81            "case insensitive extraction"
82        );
83    }
84
85    #[test]
86    fn hash_map_get_all() {
87        let mut carrier = HashMap::new();
88        carrier.set("headerName", "value".to_string());
89
90        assert_eq!(
91            Extractor::get_all(&carrier, "HEADERNAME"),
92            Some(vec!["value"]),
93            "case insensitive get_all extraction"
94        );
95    }
96
97    #[test]
98    fn hash_map_get_all_missing_key() {
99        let mut carrier = HashMap::new();
100        carrier.set("headerName", "value".to_string());
101
102        assert_eq!(
103            Extractor::get_all(&carrier, "missing_key"),
104            None,
105            "case insensitive get_all extraction"
106        );
107    }
108
109    #[test]
110    fn hash_map_keys() {
111        let mut carrier = HashMap::new();
112        carrier.set("headerName1", "value1".to_string());
113        carrier.set("headerName2", "value2".to_string());
114
115        let got = Extractor::keys(&carrier);
116        assert_eq!(got.len(), 2);
117        assert!(got.contains(&"headername1"));
118        assert!(got.contains(&"headername2"));
119    }
120}