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
15pub 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}