diff --git a/opentelemetry/CHANGELOG.md b/opentelemetry/CHANGELOG.md index 80e1c3249c..6e8c9743e0 100644 --- a/opentelemetry/CHANGELOG.md +++ b/opentelemetry/CHANGELOG.md @@ -4,6 +4,7 @@ - *Breaking* Change return type of `opentelemetry::global::set_tracer_provider` to Unit to align with metrics counterpart - Add `get_all` method to `opentelemetry::propagation::Extractor` to return all values of the given propagation key and provide a default implementation. +- Add an `IntoIterator` implementation for `opentelemetry::trace::TraceState` to allow iterating through its key-value pair collection. ## 0.30.0 diff --git a/opentelemetry/src/trace/span_context.rs b/opentelemetry/src/trace/span_context.rs index c74e159135..66343f960e 100644 --- a/opentelemetry/src/trace/span_context.rs +++ b/opentelemetry/src/trace/span_context.rs @@ -211,6 +211,50 @@ impl FromStr for TraceState { } } +/// Iterator over TraceState key-value pairs as (&str, &str) +#[derive(Debug)] +pub struct TraceStateIter<'a> { + inner: Option>, +} + +impl<'a> Iterator for TraceStateIter<'a> { + type Item = (&'a str, &'a str); + + fn next(&mut self) -> Option { + self.inner + .as_mut()? + .next() + .map(|(key, value)| (key.as_str(), value.as_str())) + } + + fn size_hint(&self) -> (usize, Option) { + match &self.inner { + Some(iter) => iter.size_hint(), + None => (0, Some(0)), + } + } +} + +impl ExactSizeIterator for TraceStateIter<'_> { + fn len(&self) -> usize { + match &self.inner { + Some(iter) => iter.len(), + None => 0, + } + } +} + +impl<'a> IntoIterator for &'a TraceState { + type Item = (&'a str, &'a str); + type IntoIter = TraceStateIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + TraceStateIter { + inner: self.0.as_ref().map(|deque| deque.iter()), + } + } +} + /// A specialized `Result` type for trace state operations. type TraceStateResult = Result; @@ -417,4 +461,39 @@ mod tests { }" ); } + + #[test] + fn test_tracestate_iter_empty() { + let ts = TraceState::NONE; + let mut iter = ts.into_iter(); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.len(), 0); + } + + #[test] + fn test_tracestate_iter_single() { + let ts = TraceState::from_key_value(vec![("foo", "bar")]).unwrap(); + let mut iter = ts.into_iter(); + assert_eq!(iter.next(), Some(("foo", "bar"))); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + } + + #[test] + fn test_tracestate_iter_multiple() { + let ts = TraceState::from_key_value(vec![("foo", "bar"), ("apple", "banana")]).unwrap(); + let mut iter = ts.into_iter(); + assert_eq!(iter.next(), Some(("foo", "bar"))); + assert_eq!(iter.next(), Some(("apple", "banana"))); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_tracestate_iter_size_hint_and_len() { + let ts = TraceState::from_key_value(vec![("foo", "bar"), ("apple", "banana")]).unwrap(); + let iter = ts.into_iter(); + assert_eq!(iter.size_hint(), (2, Some(2))); + assert_eq!(iter.len(), 2); + } }