srad_eon/
birth.rs

1use std::{
2    collections::HashSet,
3    hash::{DefaultHasher, Hash, Hasher},
4};
5
6use srad_types::{
7    payload::{DataType, Metric},
8    traits,
9    utils::timestamp,
10    MetaData, MetricId, MetricValue, PropertySet,
11};
12
13use crate::{error::Error, metric::MetricToken, registry::DeviceId};
14
15/// Details about a metric to be included in a birth message
16pub struct BirthMetricDetails<T> {
17    name: String,
18    use_alias: bool,
19    datatype: DataType,
20    initial_value: Option<T>,
21    metadata: Option<MetaData>,
22    properties: Option<PropertySet>,
23    timestamp: u64,
24}
25
26impl<T> BirthMetricDetails<T>
27where
28    T: traits::MetricValue,
29{
30    fn new(name: String, initial_value: Option<T>, datatype: DataType) -> Self {
31        Self {
32            name,
33            use_alias: true,
34            datatype,
35            initial_value,
36            metadata: None,
37            properties: None,
38            timestamp: timestamp(),
39        }
40    }
41
42    pub fn new_with_initial_value<S: Into<String>>(name: S, initial_value: T) -> Self {
43        Self::new(name.into(), Some(initial_value), T::default_datatype())
44    }
45
46    fn new_with_explicit_datatype(
47        name: String,
48        datatype: DataType,
49        initial_value: Option<T>,
50    ) -> Result<Self, Error> {
51        if !T::supported_datatypes().contains(&datatype) {
52            return Err(Error::MetricValueDatatypeMismatch);
53        }
54        Ok(Self::new(name, initial_value, datatype))
55    }
56
57    pub fn new_with_initial_value_explicit_type<S: Into<String>>(
58        name: S,
59        initial_value: T,
60        datatype: DataType,
61    ) -> Result<Self, Error> {
62        Self::new_with_explicit_datatype(name.into(), datatype, Some(initial_value))
63    }
64
65    pub fn new_without_initial_value<S: Into<String>>(
66        name: S,
67        datatype: DataType,
68    ) -> Result<Self, Error> {
69        Self::new_with_explicit_datatype(name.into(), datatype, None)
70    }
71
72    pub fn use_alias(mut self, use_alias: bool) -> Self {
73        self.use_alias = use_alias;
74        self
75    }
76
77    pub fn with_timestamp(mut self, timestamp: u64) -> Self {
78        self.timestamp = timestamp;
79        self
80    }
81
82    pub fn with_metadata(mut self, metadata: MetaData) -> Self {
83        self.metadata = Some(metadata);
84        self
85    }
86
87    pub fn with_properties<P: Into<PropertySet>>(mut self, properties: P) -> Self {
88        self.properties = Some(properties.into());
89        self
90    }
91}
92
93impl<T> From<BirthMetricDetails<T>> for Metric
94where
95    T: traits::MetricValue,
96{
97    fn from(value: BirthMetricDetails<T>) -> Metric {
98        let mut birth_metric = Metric::new();
99        birth_metric
100            .set_name(value.name)
101            .set_datatype(value.datatype);
102        birth_metric.timestamp = Some(value.timestamp);
103        birth_metric.metadata = value.metadata.map(MetaData::into);
104        if let Some(val) = value.initial_value {
105            let val: MetricValue = val.into();
106            birth_metric.set_value(val.into());
107        }
108        birth_metric.properties = value.properties.map(PropertySet::into);
109
110        birth_metric
111    }
112}
113
114enum AliasType {
115    Node,
116    Device { id: DeviceId },
117}
118
119pub enum BirthObjectType {
120    Node,
121    Device(DeviceId),
122}
123
124pub struct BirthInitializer {
125    birth_metrics: Vec<Metric>,
126    metric_names: HashSet<String>,
127    metric_aliases: HashSet<u64>,
128    inserter_type: BirthObjectType,
129}
130
131impl BirthInitializer {
132    pub(crate) fn new(inserter_type: BirthObjectType) -> Self {
133        Self {
134            birth_metrics: Vec::new(),
135            metric_names: HashSet::new(),
136            metric_aliases: HashSet::new(),
137            inserter_type,
138        }
139    }
140
141    fn generate_alias(&mut self, alias_type: AliasType, metric_name: &String) -> u64 {
142        let mut hasher = DefaultHasher::new();
143        metric_name.hash(&mut hasher);
144        let hash = hasher.finish() as u32;
145        let id_part = match alias_type {
146            AliasType::Node => 0,
147            AliasType::Device { id } => id,
148        };
149        let mut alias = ((id_part as u64) << 32) | (hash as u64);
150        while self.metric_aliases.contains(&alias) {
151            alias += 1;
152        }
153        self.metric_aliases.insert(alias);
154        alias
155    }
156
157    fn create_metric_token<T: traits::MetricValue>(
158        &mut self,
159        name: &String,
160        use_alias: bool,
161    ) -> Result<MetricToken<T>, Error> {
162        let metric = name.into();
163
164        if self.metric_names.contains(&metric) {
165            return Err(Error::DuplicateMetric);
166        }
167
168        let id = match use_alias {
169            true => {
170                let alias = match &self.inserter_type {
171                    BirthObjectType::Node => self.generate_alias(AliasType::Node, &metric),
172                    BirthObjectType::Device(id) => {
173                        self.generate_alias(AliasType::Device { id: *id }, &metric)
174                    }
175                };
176                MetricId::Alias(alias)
177            }
178            false => MetricId::Name(metric),
179        };
180
181        Ok(MetricToken::new(id))
182    }
183
184    pub fn register_metric<T: traits::MetricValue>(
185        &mut self,
186        details: BirthMetricDetails<T>,
187    ) -> Result<MetricToken<T>, Error> {
188        let tok = self.create_metric_token(&details.name, details.use_alias)?;
189        let mut metric: Metric = details.into();
190        if let MetricId::Alias(alias) = &tok.id {
191            metric.set_alias(*alias);
192        }
193        self.birth_metrics.push(metric);
194        Ok(tok)
195    }
196
197    pub(crate) fn finish(self) -> Vec<Metric> {
198        self.birth_metrics
199    }
200}