Skip to main content

torrust_metrics/
prometheus.rs

1use torrust_clock::DurationSinceUnixEpoch;
2
3use crate::metric_collection::Error as MetricCollectionError;
4use crate::sample_collection::Error as SampleCollectionError;
5
6pub trait PrometheusSerializable {
7    /// Convert the implementing type into a Prometheus exposition format string.
8    ///
9    /// # Returns
10    ///
11    /// A `String` containing the serialized representation.
12    fn to_prometheus(&self) -> String;
13}
14
15// Blanket implementation for references
16impl<T: PrometheusSerializable> PrometheusSerializable for &T {
17    fn to_prometheus(&self) -> String {
18        (*self).to_prometheus()
19    }
20}
21
22pub trait PrometheusDeserializable: Sized {
23    /// Parse a Prometheus exposition text format string into `Self`.
24    ///
25    /// `now` is used as the sample timestamp when the exposition text does not
26    /// include a timestamp for a given sample.
27    ///
28    /// # Errors
29    ///
30    /// Returns an error if the input cannot be parsed or contains unsupported
31    /// or unknown metric types/values.
32    fn from_prometheus(input: &str, now: DurationSinceUnixEpoch) -> Result<Self, PrometheusDeserializationError>;
33}
34
35#[derive(thiserror::Error, Debug, Clone)]
36pub enum PrometheusDeserializationError {
37    /// The Prometheus text could not be parsed at all (syntax error).
38    #[error("Failed to parse Prometheus exposition text: {message}")]
39    ParseError { message: String },
40
41    /// The parser emitted a metric type that is syntactically valid but that
42    /// this implementation does not yet support (e.g. Histogram, Summary).
43    #[error("Unsupported Prometheus metric type '{metric_type}' for metric '{metric_name}'")]
44    UnsupportedType { metric_name: String, metric_type: String },
45
46    /// The parser emitted a metric type that is not recognised at all.
47    #[error("Unknown Prometheus metric type for metric '{metric_name}'")]
48    UnknownType { metric_name: String },
49
50    /// The value in the exposition does not match the declared metric type.
51    #[error("Value mismatch for metric '{metric_name}': expected {expected_type}, got {actual}")]
52    ValueMismatch {
53        metric_name: String,
54        expected_type: String,
55        actual: String,
56    },
57
58    /// The value is of an unknown/unrecognised kind.
59    #[error("Unknown value for metric '{metric_name}'")]
60    UnknownValue { metric_name: String },
61
62    /// The label set could not be converted (e.g. invalid label name or value).
63    #[error("Failed to convert label set for metric '{metric_name}': {message}")]
64    LabelConversion { metric_name: String, message: String },
65
66    /// A structural error when assembling collections from parsed data.
67    #[error("Failed to build collection data: {message}")]
68    CollectionError { message: String },
69}
70
71impl From<MetricCollectionError> for PrometheusDeserializationError {
72    fn from(error: MetricCollectionError) -> Self {
73        Self::CollectionError {
74            message: error.to_string(),
75        }
76    }
77}
78
79impl From<SampleCollectionError> for PrometheusDeserializationError {
80    fn from(error: SampleCollectionError) -> Self {
81        Self::CollectionError {
82            message: error.to_string(),
83        }
84    }
85}