Skip to main content

srad_eon/
birth.rs

1use std::{
2    collections::HashSet,
3    hash::{DefaultHasher, Hash, Hasher},
4    sync::Arc,
5};
6
7use srad_types::{
8    payload::{DataType, Metric},
9    traits,
10    utils::timestamp,
11    MetaData, MetricId, MetricValue, PropertySet, Template, TemplateDefinition,
12};
13
14use crate::{device::DeviceId, metric::MetricToken, node::TemplateRegistry};
15
16use thiserror::Error;
17
18#[derive(Error, Debug)]
19pub enum BirthMetricError {
20    #[error("Duplicate metric")]
21    DuplicateMetric,
22    #[error("The provided type does not support that datatype")]
23    MetricValueDatatypeMismatch,
24    #[error("The datatype is unsupported")]
25    UnsupportedDatatype,
26    #[error("A value was expected but not provided")]
27    ValueNotProvided,
28    #[error("The provided template uses a definition that has not been registered with the node.")]
29    UnregisteredTemplate,
30}
31
32/// Details about a metric to be included in a birth message
33pub struct BirthMetricDetails<T> {
34    name: String,
35    use_alias: bool,
36    datatype: DataType,
37    initial_value: Option<T>,
38    metadata: Option<MetaData>,
39    properties: Option<PropertySet>,
40    timestamp: u64,
41}
42
43impl<T> BirthMetricDetails<T> {
44    fn new(name: String, initial_value: Option<T>, datatype: DataType) -> Self {
45        Self {
46            name,
47            use_alias: true,
48            datatype,
49            initial_value,
50            metadata: None,
51            properties: None,
52            timestamp: timestamp(),
53        }
54    }
55
56    pub fn use_alias(mut self, use_alias: bool) -> Self {
57        self.use_alias = use_alias;
58        self
59    }
60
61    pub fn with_timestamp(mut self, timestamp: u64) -> Self {
62        self.timestamp = timestamp;
63        self
64    }
65
66    pub fn with_metadata(mut self, metadata: MetaData) -> Self {
67        self.metadata = Some(metadata);
68        self
69    }
70
71    pub fn with_properties<P: Into<PropertySet>>(mut self, properties: P) -> Self {
72        self.properties = Some(properties.into());
73        self
74    }
75
76    fn into_metric_value(self, value: Option<MetricValue>) -> Metric {
77        let mut birth_metric = Metric::new();
78        birth_metric.set_name(self.name).set_datatype(self.datatype);
79        birth_metric.timestamp = Some(self.timestamp);
80        birth_metric.metadata = self.metadata.map(MetaData::into);
81        if value.is_none() {
82            birth_metric.is_null = Some(true);
83        }
84        birth_metric.value = value.map(MetricValue::into);
85
86        birth_metric.properties = self.properties.map(PropertySet::into);
87        birth_metric
88    }
89}
90
91impl<T> BirthMetricDetails<T>
92where
93    T: traits::MetricValue,
94{
95    pub fn new_with_initial_value<S: Into<String>>(name: S, initial_value: T) -> Self {
96        Self::new(name.into(), Some(initial_value), T::default_datatype())
97    }
98
99    fn new_with_explicit_datatype(
100        name: String,
101        datatype: DataType,
102        initial_value: Option<T>,
103    ) -> Result<Self, BirthMetricError> {
104        if !T::supported_datatypes().contains(&datatype) {
105            return Err(BirthMetricError::MetricValueDatatypeMismatch);
106        }
107        Ok(Self::new(name, initial_value, datatype))
108    }
109
110    pub fn new_with_initial_value_explicit_type<S: Into<String>>(
111        name: S,
112        initial_value: T,
113        datatype: DataType,
114    ) -> Result<Self, BirthMetricError> {
115        Self::new_with_explicit_datatype(name.into(), datatype, Some(initial_value))
116    }
117
118    pub fn new_without_initial_value<S: Into<String>>(
119        name: S,
120        datatype: DataType,
121    ) -> Result<Self, BirthMetricError> {
122        Self::new_with_explicit_datatype(name.into(), datatype, None)
123    }
124}
125
126impl<T> BirthMetricDetails<T>
127where
128    T: Template,
129{
130    pub fn new_template_metric<S: Into<String>>(name: S, value: T) -> Self {
131        Self::new(name.into(), Some(value), DataType::Template)
132    }
133}
134
135enum AliasType {
136    Node,
137    Device { id: DeviceId },
138}
139
140pub enum BirthObjectType {
141    Node,
142    Device(DeviceId),
143}
144
145/// A struct used to register metrics to be included in a node or devices birth.
146pub struct BirthInitializer {
147    birth_metrics: Vec<Metric>,
148    metric_names: HashSet<String>,
149    metric_aliases: HashSet<u64>,
150    inserter_type: BirthObjectType,
151    template_registry: Arc<TemplateRegistry>,
152}
153
154impl BirthInitializer {
155    pub(crate) fn new(
156        inserter_type: BirthObjectType,
157        template_registry: Arc<TemplateRegistry>,
158    ) -> Self {
159        Self {
160            birth_metrics: Vec::new(),
161            metric_names: HashSet::new(),
162            metric_aliases: HashSet::new(),
163            inserter_type,
164            template_registry,
165        }
166    }
167
168    fn generate_alias(&mut self, alias_type: AliasType, metric_name: &String) -> u64 {
169        let mut hasher = DefaultHasher::new();
170        metric_name.hash(&mut hasher);
171        let hash = hasher.finish() as u32;
172        let id_part = match alias_type {
173            AliasType::Node => 0,
174            AliasType::Device { id } => id,
175        };
176        let mut alias = ((id_part as u64) << 32) | (hash as u64);
177        while self.metric_aliases.contains(&alias) {
178            alias += 1;
179        }
180        self.metric_aliases.insert(alias);
181        alias
182    }
183
184    fn create_metric_token<T>(
185        &mut self,
186        name: &String,
187        use_alias: bool,
188    ) -> Result<MetricToken<T>, BirthMetricError> {
189        let metric = name.into();
190
191        if self.metric_names.contains(&metric) {
192            return Err(BirthMetricError::DuplicateMetric);
193        }
194
195        let id = match use_alias {
196            true => {
197                let alias = match &self.inserter_type {
198                    BirthObjectType::Node => self.generate_alias(AliasType::Node, &metric),
199                    BirthObjectType::Device(id) => {
200                        self.generate_alias(AliasType::Device { id: *id }, &metric)
201                    }
202                };
203                MetricId::Alias(alias)
204            }
205            false => MetricId::Name(metric.clone()),
206        };
207        self.metric_names.insert(metric);
208        Ok(MetricToken::new(id))
209    }
210
211    pub(crate) fn register_template_definition(
212        &mut self,
213        name: String,
214        definition: TemplateDefinition,
215    ) -> Result<(), BirthMetricError> {
216        if self.metric_names.contains(&name) {
217            return Err(BirthMetricError::DuplicateMetric);
218        }
219
220        let mut metric = Metric::new();
221        metric.name = Some(name.clone());
222        metric.datatype = Some(DataType::Template as u32);
223        metric.value = Some(MetricValue::from(definition).into());
224        self.metric_names.insert(name);
225
226        Ok(())
227    }
228
229    /// Register a metric for the birth of the node/device
230    ///
231    /// Metric names must be unique.
232    /// **Note**: For metrics with Template datatypes, use [Self::register_template_metric] instead.
233    pub fn register_metric<T>(
234        &mut self,
235        mut details: BirthMetricDetails<T>,
236    ) -> Result<MetricToken<T>, BirthMetricError>
237    where
238        T: traits::MetricValue,
239    {
240        if details.datatype == DataType::Template {
241            debug_assert!(false, "Cannot register Template datatypes through this api");
242            return Err(BirthMetricError::UnsupportedDatatype);
243        }
244        let tok = self.create_metric_token(&details.name, details.use_alias)?;
245        let value = details.initial_value.take().map(T::into);
246        let mut metric = details.into_metric_value(value);
247        if let MetricId::Alias(alias) = &tok.id {
248            metric.set_alias(*alias);
249        }
250        self.birth_metrics.push(metric);
251        Ok(tok)
252    }
253
254    /// Register a new template metric
255    ///
256    /// **Note**: Templates must have been registered with [TemplateRegistry]
257    /// See [Self::register_metric] for further details
258    pub fn register_template_metric<T>(
259        &mut self,
260        mut details: BirthMetricDetails<T>,
261    ) -> Result<MetricToken<T>, BirthMetricError>
262    where
263        T: Template,
264    {
265        let template_instance = match details.initial_value.take() {
266            Some(value) => value.template_instance(),
267            None => {
268                //we should never really get here since BirthMetricDetails does not allow
269                //the provision of a template metric without an initial value
270                debug_assert!(false);
271                return Err(BirthMetricError::ValueNotProvided);
272            }
273        };
274
275        if !self
276            .template_registry
277            .contains(&template_instance.template_ref)
278        {
279            return Err(BirthMetricError::UnregisteredTemplate);
280        }
281
282        let tok = self.create_metric_token(&details.name, details.use_alias)?;
283        let mut metric: Metric = details.into_metric_value(Some(template_instance.into()));
284        if let MetricId::Alias(alias) = &tok.id {
285            metric.set_alias(*alias);
286        }
287        self.birth_metrics.push(metric);
288        Ok(tok)
289    }
290
291    pub(crate) fn finish(self) -> Vec<Metric> {
292        self.birth_metrics
293    }
294}