use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use crate::error::{MetricsError, Result};
pub mod comparison;
pub mod format;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricResult {
pub name: String,
pub value: f64,
pub additional_values: Option<HashMap<String, f64>>,
pub timestamp: DateTime<Utc>,
pub metadata: Option<MetricMetadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MetricMetadata {
pub dataset_id: Option<String>,
pub model_id: Option<String>,
pub parameters: Option<HashMap<String, String>>,
pub additional_metadata: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricCollection {
pub name: String,
pub description: Option<String>,
pub metrics: Vec<MetricResult>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub version: String,
}
impl MetricCollection {
pub fn new(name: &str, description: Option<&str>) -> Self {
let now = Utc::now();
MetricCollection {
name: name.to_string(),
description: description.map(|s| s.to_string()),
metrics: Vec::new(),
created_at: now,
updated_at: now,
version: "1.0.0".to_string(),
}
}
pub fn add_metric(&mut self, metric: MetricResult) -> &mut Self {
self.metrics.push(metric);
self.updated_at = Utc::now();
self
}
pub fn with_version(&mut self, version: &str) -> &mut Self {
self.version = version.to_string();
self
}
pub fn save<P: AsRef<Path>>(&self, path: P, format: SerializationFormat) -> Result<()> {
let serialized = match format {
SerializationFormat::Json => serde_json::to_string_pretty(self)
.map_err(|e| MetricsError::SerializationError(e.to_string()))?,
SerializationFormat::Yaml => serde_yaml::to_string(self)
.map_err(|e| MetricsError::SerializationError(e.to_string()))?,
SerializationFormat::Toml => toml::to_string_pretty(self)
.map_err(|e| MetricsError::SerializationError(e.to_string()))?,
SerializationFormat::Cbor => {
let mut bytes = Vec::new();
ciborium::ser::into_writer(self, &mut bytes)
.map_err(|e| MetricsError::SerializationError(e.to_string()))?;
return save_binary(path, &bytes);
}
};
savetext(path, &serialized)
}
pub fn load<P: AsRef<Path>>(path: P, format: SerializationFormat) -> Result<Self> {
match format {
SerializationFormat::Json => {
let text = loadtext(path)?;
serde_json::from_str(&text)
.map_err(|e| MetricsError::SerializationError(e.to_string()))
}
SerializationFormat::Yaml => {
let text = loadtext(path)?;
serde_yaml::from_str(&text)
.map_err(|e| MetricsError::SerializationError(e.to_string()))
}
SerializationFormat::Toml => {
let text = loadtext(path)?;
toml::from_str(&text).map_err(|e| MetricsError::SerializationError(e.to_string()))
}
SerializationFormat::Cbor => {
let bytes = load_binary(path)?;
ciborium::de::from_reader(&bytes[..])
.map_err(|e| MetricsError::SerializationError(e.to_string()))
}
}
}
}
#[allow(dead_code)]
pub fn create_metric_result(
name: &str,
value: f64,
additional_values: Option<HashMap<String, f64>>,
metadata: Option<MetricMetadata>,
) -> MetricResult {
MetricResult {
name: name.to_string(),
value,
additional_values,
timestamp: Utc::now(),
metadata,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SerializationFormat {
Json,
Yaml,
Toml,
Cbor,
}
#[allow(dead_code)]
fn savetext<P: AsRef<Path>>(path: P, text: &str) -> Result<()> {
let mut file = File::create(path).map_err(|e| MetricsError::IOError(e.to_string()))?;
file.write_all(text.as_bytes())
.map_err(|e| MetricsError::IOError(e.to_string()))?;
Ok(())
}
#[allow(dead_code)]
fn loadtext<P: AsRef<Path>>(path: P) -> Result<String> {
let mut file = File::open(path).map_err(|e| MetricsError::IOError(e.to_string()))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.map_err(|e| MetricsError::IOError(e.to_string()))?;
Ok(contents)
}
#[allow(dead_code)]
fn save_binary<P: AsRef<Path>>(path: P, data: &[u8]) -> Result<()> {
let mut file = File::create(path).map_err(|e| MetricsError::IOError(e.to_string()))?;
file.write_all(data)
.map_err(|e| MetricsError::IOError(e.to_string()))?;
Ok(())
}
#[allow(dead_code)]
fn load_binary<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
let mut file = File::open(path).map_err(|e| MetricsError::IOError(e.to_string()))?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)
.map_err(|e| MetricsError::IOError(e.to_string()))?;
Ok(contents)
}