1use thiserror::Error;
2
3use crate::{
4 payload::{self, metric, DataType},
5 traits::{self, HasDataType},
6 FromValueTypeError, MetricValue, ParameterValue,
7};
8
9pub trait TemplateMetricValue {
10 type Error;
11
12 fn to_template_metric_value(self) -> Option<MetricValue>;
13
14 fn try_from_template_metric_value(value: Option<MetricValue>) -> Result<Self, Self::Error>
15 where
16 Self: Sized;
17}
18
19impl<T> TemplateMetricValue for T
20where
21 T: traits::MetricValue,
22{
23 type Error = ();
24
25 fn to_template_metric_value(self) -> Option<MetricValue> {
26 Some(T::into(self))
27 }
28
29 fn try_from_template_metric_value(value: Option<MetricValue>) -> Result<Self, Self::Error>
30 where
31 Self: Sized,
32 {
33 match value {
34 Some(value) => Self::try_from(value).map_err(|_| ()),
35 None => Err(()),
36 }
37 }
38}
39
40impl<T> TemplateMetricValue for Option<T>
41where
42 T: traits::MetricValue,
43{
44 type Error = ();
45
46 fn to_template_metric_value(self) -> Option<MetricValue> {
47 self.map(T::into)
48 }
49
50 fn try_from_template_metric_value(value: Option<MetricValue>) -> Result<Self, Self::Error>
51 where
52 Self: Sized,
53 {
54 match value {
55 Some(value) => {
56 let value = value.try_into().map_err(|_| ())?;
57 Ok(Some(value))
58 }
59 None => Ok(None),
60 }
61 }
62}
63
64pub trait TemplateParameterValue {
65 type Error;
66 fn to_template_parameter_value(self) -> Option<ParameterValue>;
67 fn try_from_template_parameter_value(
68 value: Option<ParameterValue>,
69 ) -> Result<Self, Self::Error>
70 where
71 Self: Sized;
72}
73
74impl<T> TemplateParameterValue for T
75where
76 T: traits::ParameterValue,
77{
78 type Error = ();
79
80 fn to_template_parameter_value(self) -> Option<ParameterValue> {
81 Some(T::into(self))
82 }
83
84 fn try_from_template_parameter_value(value: Option<ParameterValue>) -> Result<Self, Self::Error>
85 where
86 Self: Sized,
87 {
88 match value {
89 Some(value) => Self::try_from(value).map_err(|_| ()),
90 None => Err(()),
91 }
92 }
93}
94
95impl<T> TemplateParameterValue for Option<T>
96where
97 T: traits::ParameterValue,
98{
99 type Error = ();
100
101 fn to_template_parameter_value(self) -> Option<ParameterValue> {
102 self.map(T::into)
103 }
104
105 fn try_from_template_parameter_value(value: Option<ParameterValue>) -> Result<Self, Self::Error>
106 where
107 Self: Sized,
108 {
109 match value {
110 Some(value) => {
111 let value = value.try_into().map_err(|_| ())?;
112 Ok(Some(value))
113 }
114 None => Ok(None),
115 }
116 }
117}
118
119pub trait TemplateMetricValuePartial {
121 type Error;
122 fn metric_value_if_ne(&self, other: &Self) -> Option<Option<MetricValue>>;
123 fn try_update_from_metric_value(
124 &mut self,
125 other: Option<MetricValue>,
126 ) -> Result<(), Self::Error>;
127}
128
129macro_rules! impl_template_metric_value_partial {
130
131 ($($ty:ty),* $(,)?) => {
132 $(
133 impl TemplateMetricValuePartial for $ty {
134 type Error = ();
135 fn metric_value_if_ne(&self, other: &Self) -> Option<Option<MetricValue>> {
136 if self == other {
137 return None
138 }
139 Some(Some(self.clone().into()))
140 }
141 fn try_update_from_metric_value(&mut self, other: Option<MetricValue>) -> Result<(), Self::Error> {
142 *self = <$ty>::try_from_template_metric_value(other)?;
143 Ok(())
144 }
145 }
146 )*
147 };
148
149 ($($ty:ty),* $(,)?) => {
150 $(
151 impl TemplateMetricValuePartial for Vec<$ty> {
152 fn metric_value_if_ne(&self, other: &Self) -> Option<Option<MetricValue>> {
153 if self == other {
154 return None
155 }
156 Some(Some(self.clone().into()))
157 }
158 fn try_update_from_metric_value(&mut self, other: Option<MetricValue>) -> Result<(), ()> {
159 *self = <$ty>::try_from_template_metric_value(other)?;
160 Ok(())
161 }
162 }
163 )*
164 };
165
166}
167
168impl_template_metric_value_partial!(bool, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, String);
169
170impl<T> TemplateMetricValuePartial for Option<T>
171where
172 T: TemplateMetricValuePartial + TemplateMetricValue + Into<MetricValue> + PartialEq + Clone,
173{
174 type Error = ();
175
176 fn metric_value_if_ne(&self, other: &Self) -> Option<Option<MetricValue>> {
177 if self == other {
178 return None;
179 }
180 Some(self.clone().map(|x| x.into()))
181 }
182
183 fn try_update_from_metric_value(
184 &mut self,
185 other: Option<MetricValue>,
186 ) -> Result<(), Self::Error> {
187 *self = match other {
188 Some(value) => Some(T::try_from_template_metric_value(Some(value)).map_err(|_| ())?),
189 None => None,
190 };
191 Ok(())
192 }
193}
194
195pub type TemplateMetric = payload::Metric;
196
197impl TemplateMetric {
198 pub fn new_template_metric_raw(
199 name: String,
200 datatype: DataType,
201 value: Option<MetricValue>,
202 ) -> Self {
203 TemplateMetric {
204 name: Some(name),
205 alias: None,
206 timestamp: None,
207 datatype: Some(datatype as u32),
208 is_historical: None,
209 is_transient: None,
210 is_null: None,
211 metadata: None,
212 properties: None,
213 value: value.map(payload::metric::Value::from),
214 }
215 }
216
217 pub fn new_template_metric<T: TemplateMetricValue + traits::HasDataType>(
218 name: String,
219 value: T,
220 ) -> Self {
221 Self::new_template_metric_raw(
222 name,
223 T::default_datatype(),
224 value.to_template_metric_value(),
225 )
226 }
227}
228
229pub type TemplateParameter = payload::template::Parameter;
230
231impl TemplateParameter {
232 pub fn new_template_parameter<T: TemplateParameterValue + traits::HasDataType>(
233 name: String,
234 value: T,
235 ) -> Self {
236 TemplateParameter {
237 name: Some(name),
238 r#type: Some(T::default_datatype() as u32),
239 value: value
240 .to_template_parameter_value()
241 .map(payload::template::parameter::Value::from),
242 }
243 }
244}
245
246#[derive(Debug, Clone, PartialEq)]
247pub struct TemplateDefinition {
248 pub version: Option<String>,
249 pub metrics: Vec<TemplateMetric>,
250 pub parameters: Vec<TemplateParameter>,
251}
252
253impl HasDataType for TemplateDefinition {
254 fn supported_datatypes() -> &'static [DataType] {
255 static SUPPORTED_TYPES: [DataType; 1] = [DataType::Template];
256 &SUPPORTED_TYPES
257 }
258}
259
260impl traits::MetricValue for TemplateDefinition {}
261
262impl From<TemplateDefinition> for payload::Template {
263 fn from(value: TemplateDefinition) -> Self {
264 payload::Template {
265 version: value.version,
266 metrics: value.metrics,
267 parameters: value.parameters,
268 template_ref: None,
269 is_definition: Some(true),
270 }
271 }
272}
273
274impl From<TemplateDefinition> for MetricValue {
275 fn from(value: TemplateDefinition) -> Self {
276 MetricValue::new(metric::Value::TemplateValue(value.into()))
277 }
278}
279
280impl TryFrom<MetricValue> for TemplateDefinition {
281 type Error = FromValueTypeError;
282
283 fn try_from(value: MetricValue) -> Result<Self, Self::Error> {
284 if let metric::Value::TemplateValue(template) = value.0 {
285 if template.template_ref.is_some() {
287 return Err(FromValueTypeError::InvalidValue(
288 "Template payload violates tck-id-payloads-template-definition-ref".into(),
289 ));
290 }
291 if template.is_definition.unwrap_or(false) {
293 return Err(FromValueTypeError::InvalidValue(
294 "Template payload violates tck-id-payloads-template-definition-is_definition"
295 .into(),
296 ));
297 }
298 Ok(TemplateDefinition {
299 version: template.version,
300 metrics: template.metrics,
301 parameters: template.parameters,
302 })
303 } else {
304 Err(FromValueTypeError::InvalidVariantType)
305 }
306 }
307}
308
309#[derive(Debug, PartialEq)]
310pub struct TemplateInstance {
311 pub template_ref: String,
313 pub version: Option<String>,
314 pub metrics: Vec<TemplateMetric>,
315 pub parameters: Vec<TemplateParameter>,
316}
317
318impl HasDataType for TemplateInstance {
319 fn supported_datatypes() -> &'static [DataType] {
320 static SUPPORTED_TYPES: [DataType; 1] = [DataType::Template];
321 &SUPPORTED_TYPES
322 }
323}
324
325impl traits::MetricValue for TemplateInstance {}
326
327impl From<TemplateInstance> for payload::Template {
328 fn from(value: TemplateInstance) -> Self {
329 payload::Template {
330 version: value.version,
331 metrics: value.metrics,
332 parameters: value.parameters,
333 template_ref: Some(value.template_ref),
334 is_definition: Some(false),
335 }
336 }
337}
338
339impl From<TemplateInstance> for MetricValue {
340 fn from(value: TemplateInstance) -> Self {
341 MetricValue::new(metric::Value::TemplateValue(value.into()))
342 }
343}
344
345impl TryFrom<MetricValue> for TemplateInstance {
346 type Error = FromValueTypeError;
347
348 fn try_from(value: MetricValue) -> Result<Self, Self::Error> {
349 if let metric::Value::TemplateValue(template) = value.0 {
350 if template.is_definition.unwrap_or(true) {
352 return Err(FromValueTypeError::InvalidValue(
353 "Template payload violates tck-id-payloads-template-instance-is_definition"
354 .into(),
355 ));
356 }
357 let template_ref = template
358 .template_ref
359 .ok_or(FromValueTypeError::InvalidValue(
360 "Template payload violates tck-id-payloads-template-instance-ref".into(),
361 ))?;
362 Ok(TemplateInstance {
363 template_ref,
364 version: template.version,
365 metrics: template.metrics,
366 parameters: template.parameters,
367 })
368 } else {
369 Err(FromValueTypeError::InvalidVariantType)
370 }
371 }
372}
373
374#[derive(Debug)]
375pub enum TemplateValue {
376 Definition(TemplateDefinition),
377 Instance(TemplateInstance),
378}
379
380impl TryFrom<MetricValue> for TemplateValue {
381 type Error = FromValueTypeError;
382
383 fn try_from(value: MetricValue) -> Result<Self, Self::Error> {
384 if let metric::Value::TemplateValue(template) = &value.0 {
385 let is_def = match template.is_definition {
386 Some(is_def) => is_def,
387 None => {
388 return Err(FromValueTypeError::InvalidValue(
389 "Template field template_ref cannot be None".into(),
390 ))
391 }
392 };
393 Ok(match is_def {
394 true => TemplateValue::Definition(TemplateDefinition::try_from(value)?),
395 false => TemplateValue::Instance(TemplateInstance::try_from(value)?),
396 })
397 } else {
398 Err(FromValueTypeError::InvalidVariantType)
399 }
400 }
401}
402
403pub trait TemplateMetadata {
407 fn template_version() -> Option<&'static str> {
409 None
410 }
411
412 fn template_name() -> &'static str;
414
415 fn template_definition_metric_name() -> String {
423 let version = Self::template_version();
424 match version {
425 Some(version) => format!("{}:{}", Self::template_name(), version),
426 None => Self::template_name().into(),
427 }
428 }
429}
430
431#[derive(Debug, Error)]
432pub enum TemplateError {
433 #[error("Invalid Template Payload")]
434 InvalidPayload,
435 #[error("Unexpected Parameter: {0}")]
436 UnknownParameter(String),
437 #[error("Unexpected Metric: {0}")]
438 UnknownMetric(String),
439 #[error("Template ref mismatch: {0}")]
440 RefMismatch(String),
441 #[error("Template version mismatch")]
442 VersionMismatch,
443 #[error("Invalid value for parameter field {0}")]
444 InvalidParameterValue(String),
445 #[error("Invalid value for metric field {0}")]
446 InvalidMetricValue(String),
447}
448
449pub trait Template: TemplateMetadata + TryFrom<TemplateInstance> {
454 fn template_definition() -> TemplateDefinition;
456 fn template_instance(&self) -> TemplateInstance;
458}
459
460impl<T> HasDataType for T
461where
462 T: Template,
463{
464 fn supported_datatypes() -> &'static [DataType] {
465 static SUPPORTED_TYPES: [DataType; 1] = [DataType::Template];
466 &SUPPORTED_TYPES
467 }
468}
469
470pub trait PartialTemplate: Template {
476 fn template_instance_from_difference(&self, other: &Self) -> Option<TemplateInstance>;
478 fn update_from_instance(&mut self, instance: TemplateInstance) -> Result<(), TemplateError>;
480}