Skip to main content

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    #[allow(unused_variables)]
36    /// Hint to reserve capacity for at least `additional` more entries to be inserted.
37    fn reserve(&mut self, additional: usize) {}
38}
39
40/// Extractor provides an interface for removing fields from an underlying struct like `HashMap`
41pub trait Extractor {
42    /// Get a value from a key from the underlying data.
43    fn get(&self, key: &str) -> Option<&str>;
44
45    /// Collect all the keys from the underlying data.
46    fn keys(&self) -> Vec<&str>;
47
48    /// Get all values from a key from the underlying data.
49    fn get_all(&self, key: &str) -> Option<Vec<&str>> {
50        self.get(key).map(|value| vec![value])
51    }
52}
53
54impl<S: std::hash::BuildHasher> Injector for HashMap<String, String, S> {
55    /// Set a key and value in the HashMap.
56    fn set(&mut self, key: &str, value: String) {
57        self.insert(key.to_lowercase(), value);
58    }
59
60    /// Reserves capacity for at least `additional` more entries to be inserted.
61    fn reserve(&mut self, additional: usize) {
62        self.reserve(additional);
63    }
64}
65
66impl<S: std::hash::BuildHasher> Extractor for HashMap<String, String, S> {
67    /// Get a value for a key from the HashMap.
68    fn get(&self, key: &str) -> Option<&str> {
69        self.get(&key.to_lowercase()).map(|v| v.as_str())
70    }
71
72    /// Collect all the keys from the HashMap.
73    fn keys(&self) -> Vec<&str> {
74        self.keys().map(|k| k.as_str()).collect::<Vec<_>>()
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn hash_map_get() {
84        let mut carrier = HashMap::new();
85        carrier.set("headerName", "value".to_string());
86
87        assert_eq!(
88            Extractor::get(&carrier, "HEADERNAME"),
89            Some("value"),
90            "case insensitive extraction"
91        );
92    }
93
94    #[test]
95    fn hash_map_get_all() {
96        let mut carrier = HashMap::new();
97        carrier.set("headerName", "value".to_string());
98
99        assert_eq!(
100            Extractor::get_all(&carrier, "HEADERNAME"),
101            Some(vec!["value"]),
102            "case insensitive get_all extraction"
103        );
104    }
105
106    #[test]
107    fn hash_map_get_all_missing_key() {
108        let mut carrier = HashMap::new();
109        carrier.set("headerName", "value".to_string());
110
111        assert_eq!(
112            Extractor::get_all(&carrier, "missing_key"),
113            None,
114            "case insensitive get_all extraction"
115        );
116    }
117
118    #[test]
119    fn hash_map_keys() {
120        let mut carrier = HashMap::new();
121        carrier.set("headerName1", "value1".to_string());
122        carrier.set("headerName2", "value2".to_string());
123
124        let got = Extractor::keys(&carrier);
125        assert_eq!(got.len(), 2);
126        assert!(got.contains(&"headername1"));
127        assert!(got.contains(&"headername2"));
128    }
129
130    #[test]
131    fn hash_map_injector_reserve() {
132        let mut carrier = HashMap::new();
133
134        // Test that reserve doesn't panic and works correctly
135        Injector::reserve(&mut carrier, 10);
136
137        // Verify the HashMap still works after reserve
138        Injector::set(&mut carrier, "test_key", "test_value".to_string());
139        assert_eq!(Extractor::get(&carrier, "test_key"), Some("test_value"));
140
141        // Test reserve with zero capacity
142        Injector::reserve(&mut carrier, 0);
143        Injector::set(&mut carrier, "another_key", "another_value".to_string());
144        assert_eq!(
145            Extractor::get(&carrier, "another_key"),
146            Some("another_value")
147        );
148
149        // Test that capacity is actually reserved (at least the requested amount)
150        let mut new_carrier = HashMap::new();
151        Injector::reserve(&mut new_carrier, 5);
152        let initial_capacity = new_carrier.capacity();
153
154        // Add some elements and verify capacity doesn't decrease
155        for i in 0..3 {
156            Injector::set(
157                &mut new_carrier,
158                &format!("key{}", i),
159                format!("value{}", i),
160            );
161        }
162
163        assert!(new_carrier.capacity() >= initial_capacity);
164        assert!(new_carrier.capacity() >= 5);
165    }
166
167    #[test]
168    fn injector_reserve() {
169        // Test to have full line coverage of default method
170        struct TestInjector();
171        impl Injector for TestInjector {
172            fn set(&mut self, _key: &str, _value: String) {}
173        }
174        let mut test_injector = TestInjector();
175        Injector::reserve(&mut test_injector, 4711);
176        Injector::set(&mut test_injector, "key", "value".to_string());
177    }
178}