use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub enum ComponentKind {
#[default]
Text,
Code,
Config,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentMeta {
pub kind: ComponentKind,
pub description: String,
pub language: Option<String>,
pub constraints: Option<HashMap<String, String>>,
}
impl ComponentMeta {
pub fn text(description: impl Into<String>) -> Self {
Self {
kind: ComponentKind::Text,
description: description.into(),
language: None,
constraints: None,
}
}
pub fn code(description: impl Into<String>, language: impl Into<String>) -> Self {
Self {
kind: ComponentKind::Code,
description: description.into(),
language: Some(language.into()),
constraints: None,
}
}
pub fn config(description: impl Into<String>) -> Self {
Self {
kind: ComponentKind::Config,
description: description.into(),
language: None,
constraints: None,
}
}
pub fn with_constraints(mut self, constraints: HashMap<String, String>) -> Self {
self.constraints = Some(constraints);
self
}
}
pub type ComponentMetaMap = HashMap<String, ComponentMeta>;
pub fn component_kind(meta: &ComponentMetaMap, name: &str) -> ComponentKind {
meta.get(name).map_or(ComponentKind::Text, |m| m.kind)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_kind_is_text() {
assert_eq!(ComponentKind::default(), ComponentKind::Text);
}
#[test]
fn component_kind_lookup_defaults_to_text() {
let meta = ComponentMetaMap::new();
assert_eq!(component_kind(&meta, "unknown"), ComponentKind::Text);
}
#[test]
fn component_kind_lookup_returns_configured() {
let mut meta = ComponentMetaMap::new();
meta.insert(
"model".into(),
ComponentMeta::code("Model architecture", "python"),
);
meta.insert(
"config".into(),
ComponentMeta::config("Training hyperparameters"),
);
assert_eq!(component_kind(&meta, "model"), ComponentKind::Code);
assert_eq!(component_kind(&meta, "config"), ComponentKind::Config);
assert_eq!(component_kind(&meta, "instructions"), ComponentKind::Text);
}
#[test]
fn config_constraints() {
let meta = ComponentMeta::config("Hyperparams").with_constraints(HashMap::from([
("DEPTH".into(), "integer, 1-32".into()),
("LR".into(), "float, 1e-5 to 1.0".into()),
]));
assert_eq!(meta.kind, ComponentKind::Config);
assert!(meta.constraints.is_some());
assert_eq!(meta.constraints.as_ref().unwrap().len(), 2);
}
#[test]
fn serde_round_trip() {
let meta = ComponentMeta::code("Architecture", "rust");
let json = serde_json::to_string(&meta).unwrap();
let back: ComponentMeta = serde_json::from_str(&json).unwrap();
assert_eq!(back.kind, ComponentKind::Code);
assert_eq!(back.language.as_deref(), Some("rust"));
}
}