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
32pub 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
145pub 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 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 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 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}