use crate::config_bag::{Storable, StoreReplace};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CapturedTelemetryAttributes {
values: HashMap<Arc<str>, Arc<str>>,
}
impl CapturedTelemetryAttributes {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) {
self.values
.insert(Arc::from(name.as_ref()), Arc::from(value.as_ref()));
}
pub fn get(&self, name: &str) -> Option<&str> {
self.values.get(name).map(|v| v.as_ref())
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.values.iter().map(|(k, v)| (k.as_ref(), v.as_ref()))
}
}
impl Storable for CapturedTelemetryAttributes {
type Storer = StoreReplace<Self>;
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RequestedTelemetryAttributes {
emit: Vec<Arc<str>>,
capture_only: Vec<Arc<str>>,
}
impl RequestedTelemetryAttributes {
pub fn new(names: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
Self {
emit: names.into_iter().map(|n| Arc::from(n.as_ref())).collect(),
capture_only: Vec::new(),
}
}
pub fn emit(&mut self, names: impl IntoIterator<Item = impl AsRef<str>>) {
self.emit
.extend(names.into_iter().map(|n| Arc::from(n.as_ref())));
}
pub fn capture_only(&mut self, names: impl IntoIterator<Item = impl AsRef<str>>) {
self.capture_only
.extend(names.into_iter().map(|n| Arc::from(n.as_ref())));
}
pub fn should_capture(&self, name: &str) -> bool {
self.emit
.iter()
.chain(self.capture_only.iter())
.any(|n| n.as_ref() == name)
}
pub fn should_emit(&self, name: &str) -> bool {
self.emit.iter().any(|n| n.as_ref() == name)
}
pub fn is_empty(&self) -> bool {
self.emit.is_empty() && self.capture_only.is_empty()
}
}
impl Storable for RequestedTelemetryAttributes {
type Storer = StoreReplace<Self>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_and_get() {
let mut attrs = CapturedTelemetryAttributes::new();
assert_eq!(attrs.iter().count(), 0);
attrs.insert("bucket", "example-bucket");
assert_eq!(attrs.get("bucket"), Some("example-bucket"));
assert_eq!(attrs.get("missing"), None);
assert_eq!(attrs.iter().count(), 1);
}
#[test]
fn insert_replaces_existing() {
let mut attrs = CapturedTelemetryAttributes::new();
attrs.insert("bucket", "first");
attrs.insert("bucket", "second");
assert_eq!(attrs.get("bucket"), Some("second"));
assert_eq!(attrs.iter().count(), 1);
}
#[test]
fn iter_yields_all_pairs() {
let mut attrs = CapturedTelemetryAttributes::new();
attrs.insert("bucket", "b");
attrs.insert("table", "t");
let mut pairs: Vec<_> = attrs.iter().collect();
pairs.sort();
assert_eq!(pairs, vec![("bucket", "b"), ("table", "t")]);
}
#[test]
fn emit_set_is_captured_and_emitted() {
let requested = RequestedTelemetryAttributes::new(["Bucket", "Key"]);
assert!(requested.should_capture("Bucket"));
assert!(requested.should_emit("Bucket"));
assert!(requested.should_capture("Key"));
assert!(requested.should_emit("Key"));
assert!(!requested.should_capture("VersionId"));
assert!(!requested.is_empty());
let empty = RequestedTelemetryAttributes::default();
assert!(empty.is_empty());
assert!(!empty.should_capture("Bucket"));
}
#[test]
fn capture_only_set_is_captured_but_not_emitted() {
let mut requested = RequestedTelemetryAttributes::default();
requested.emit(["Bucket"]);
requested.capture_only(["Prefix"]);
assert!(requested.should_capture("Prefix"));
assert!(!requested.should_emit("Prefix"));
assert!(requested.should_capture("Bucket"));
assert!(requested.should_emit("Bucket"));
assert!(!requested.is_empty());
}
}