use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ResearchId(String);
impl ResearchId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for ResearchId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for ResearchId {
fn from(s: String) -> Self {
Self(s)
}
}
impl From<&str> for ResearchId {
fn from(s: &str) -> Self {
Self(s.to_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum ResearchStatus {
#[default]
Available,
Queued,
InProgress,
Completed,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResearchProject {
pub id: ResearchId,
pub name: String,
pub description: String,
pub status: ResearchStatus,
pub progress: f32,
pub cost: i64,
pub metrics: HashMap<String, f32>,
#[serde(default)]
pub metadata: serde_json::Value,
}
impl ResearchProject {
pub fn new(
id: impl Into<String>,
name: impl Into<String>,
description: impl Into<String>,
) -> Self {
Self {
id: ResearchId::new(id),
name: name.into(),
description: description.into(),
status: ResearchStatus::Available,
progress: 0.0,
cost: 0,
metrics: HashMap::new(),
metadata: serde_json::Value::Null,
}
}
pub fn with_cost(mut self, cost: i64) -> Self {
self.cost = cost;
self
}
pub fn with_metrics(mut self, metrics: HashMap<String, f32>) -> Self {
self.metrics = metrics;
self
}
pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
self.metadata = metadata;
self
}
pub fn add_metric(mut self, name: impl Into<String>, value: f32) -> Self {
self.metrics.insert(name.into(), value);
self
}
pub fn get_metric(&self, name: &str) -> Option<f32> {
self.metrics.get(name).copied()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResearchResult {
pub project_id: ResearchId,
pub success: bool,
pub final_metrics: HashMap<String, f32>,
#[serde(default)]
pub metadata: serde_json::Value,
}
impl ResearchResult {
pub fn new(project_id: ResearchId, success: bool) -> Self {
Self {
project_id,
success,
final_metrics: HashMap::new(),
metadata: serde_json::Value::Null,
}
}
pub fn with_metrics(mut self, metrics: HashMap<String, f32>) -> Self {
self.final_metrics = metrics;
self
}
pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
self.metadata = metadata;
self
}
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum ResearchError {
#[error("Research project not found")]
NotFound,
#[error("Research project already queued")]
AlreadyQueued,
#[error("Research project already completed")]
AlreadyCompleted,
#[error("Research project is not in progress")]
NotInProgress,
#[error("Insufficient resources")]
InsufficientResources,
#[error("Prerequisites not met: {0}")]
PrerequisitesNotMet(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_research_id_creation() {
let id = ResearchId::new("test_project");
assert_eq!(id.as_str(), "test_project");
assert_eq!(id.to_string(), "test_project");
}
#[test]
fn test_research_id_from_string() {
let id: ResearchId = "test_project".into();
assert_eq!(id.as_str(), "test_project");
}
#[test]
fn test_research_status_default() {
assert_eq!(ResearchStatus::default(), ResearchStatus::Available);
}
#[test]
fn test_research_project_creation() {
let project = ResearchProject::new("test", "Test Project", "A test project");
assert_eq!(project.id.as_str(), "test");
assert_eq!(project.name, "Test Project");
assert_eq!(project.description, "A test project");
assert_eq!(project.status, ResearchStatus::Available);
assert_eq!(project.progress, 0.0);
assert_eq!(project.cost, 0);
assert!(project.metrics.is_empty());
}
#[test]
fn test_research_project_with_cost() {
let project = ResearchProject::new("test", "Test", "Test").with_cost(5000);
assert_eq!(project.cost, 5000);
}
#[test]
fn test_research_project_with_metrics() {
let mut metrics = HashMap::new();
metrics.insert("effectiveness".into(), 1.5);
metrics.insert("reliability".into(), 0.85);
let project = ResearchProject::new("test", "Test", "Test").with_metrics(metrics);
assert_eq!(project.get_metric("effectiveness"), Some(1.5));
assert_eq!(project.get_metric("reliability"), Some(0.85));
assert_eq!(project.get_metric("nonexistent"), None);
}
#[test]
fn test_research_project_add_metric() {
let project = ResearchProject::new("test", "Test", "Test")
.add_metric("effectiveness", 1.5)
.add_metric("reliability", 0.85);
assert_eq!(project.get_metric("effectiveness"), Some(1.5));
assert_eq!(project.get_metric("reliability"), Some(0.85));
}
#[test]
fn test_research_result_creation() {
let result = ResearchResult::new(ResearchId::new("test"), true);
assert_eq!(result.project_id.as_str(), "test");
assert!(result.success);
assert!(result.final_metrics.is_empty());
}
#[test]
fn test_research_result_with_metrics() {
let mut metrics = HashMap::new();
metrics.insert("effectiveness".into(), 1.5);
let result = ResearchResult::new(ResearchId::new("test"), true).with_metrics(metrics);
assert_eq!(result.final_metrics.get("effectiveness"), Some(&1.5));
}
}