carbond_lib/metrics/
metric.rs

1// SPDX-FileCopyrightText: 2024 Andreas Schmidt <andreas.schmidt@cs.uni-saarland.de>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5use async_trait::async_trait;
6use log::trace;
7use std::{fs, path::Path, str::FromStr};
8use thiserror::Error;
9use tokio::io;
10
11use crate::fs::create_file;
12
13#[derive(Error, Debug)]
14pub enum MetricError {
15    #[error("Could not read/write metric at {location}.")]
16    Io {
17        location: String,
18        #[source]
19        source: io::Error,
20    },
21    #[error("error while interacting with fs")]
22    Fs(#[from] io::Error),
23    #[error("Format of metric \"{0}\" is invalid for \"{1}\".")]
24    ParseMetric(String, String),
25    #[error("{0}: {1}.")]
26    Other(String, String),
27}
28
29#[async_trait]
30pub trait Metric: FromStr + ToString {
31    type Unit;
32    const NAME: &'static str;
33
34    fn get_value(&self) -> Self::Unit;
35    fn neutral() -> Self;
36    fn from_value(value: Self::Unit) -> Self;
37
38    /// Reads and parses the corresponding metric value from the file system.
39    async fn try_read_from_fs(file_path: &str) -> Result<Self, MetricError> {
40        let raw = tokio::fs::read_to_string(file_path)
41            .await
42            .map_err(|source| MetricError::Io {
43                location: file_path.to_owned(),
44                source,
45            })?;
46        let instance = Self::from_str(&raw)
47            .map_err(|_| MetricError::ParseMetric(Self::NAME.to_owned(), file_path.to_owned()))?;
48        Ok(instance)
49    }
50
51    /// Reads and parses the corresponding metric value from the file system synchronously.
52    fn try_read_from_fs_sync(file_path: &str) -> Result<Self, MetricError> {
53        let raw = fs::read_to_string(file_path).map_err(|source| MetricError::Io {
54            location: file_path.to_owned(),
55            source,
56        })?;
57        let instance = Self::from_str(&raw)
58            .map_err(|_| MetricError::ParseMetric(Self::NAME.to_owned(), file_path.to_owned()))?;
59        Ok(instance)
60    }
61
62    /// Reads and parses the corresponding metric value from the file system.
63    async fn try_read_from_path(file_path: &Path) -> Result<Self, MetricError> {
64        let path = file_path.to_str().ok_or(MetricError::Other(
65            Self::NAME.to_owned(),
66            "Could not convert path".to_owned(),
67        ))?;
68        Self::try_read_from_fs(path).await
69    }
70
71    /// Reads and parses the corresponding metric value from the file system synchronously.
72    fn try_read_from_path_sync(file_path: &Path) -> Result<Self, MetricError> {
73        let path = file_path.to_str().ok_or(MetricError::Other(
74            Self::NAME.to_owned(),
75            "Could not convert path".to_owned(),
76        ))?;
77        Self::try_read_from_fs_sync(path)
78    }
79
80    /// Writes the metric value in a readable format to the file system.
81    async fn try_write_to_fs(&self, file_path: &str) -> Result<(), MetricError> {
82        let path = Path::new(file_path);
83        trace!("Write {:#?} to {:#?}", self.to_string(), file_path);
84        create_file(path).await.map_err(|source| MetricError::Io {
85            location: file_path.to_owned(),
86            source,
87        })?;
88        tokio::fs::write(&path, self.to_string())
89            .await
90            .map_err(|source| MetricError::Io {
91                location: file_path.to_owned(),
92                source,
93            })?;
94        Ok(())
95    }
96
97    /// Writes the metric value in a readable format to the file system.
98    async fn try_write_to_path(&self, file_path: &Path) -> Result<(), MetricError> {
99        let path = file_path.to_str().ok_or(MetricError::Other(
100            Self::NAME.to_owned(),
101            "Could not convert path".to_owned(),
102        ))?;
103        self.try_write_to_fs(path).await
104    }
105}