use super::{AgentCapability, Discoverable, SemanticRole, WidgetSchema};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Default)]
pub struct OntologyRegistry {
schemas: HashMap<String, WidgetSchema>,
tree: Option<UiTree>,
}
impl OntologyRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_schema(&mut self, schema: WidgetSchema) {
self.schemas.insert(schema.name.clone(), schema);
}
pub fn register<W: Discoverable>(&mut self) {
self.register_schema(W::schema());
}
pub fn list_types(&self) -> Vec<&str> {
self.schemas.keys().map(|s| s.as_str()).collect()
}
pub fn get_schema(&self, name: &str) -> Option<&WidgetSchema> {
self.schemas.get(name)
}
pub fn find_by_role(&self, role: SemanticRole) -> Vec<&WidgetSchema> {
self.schemas
.values()
.filter(|s| s.default_role == role)
.collect()
}
pub fn search(&self, query: &str) -> Vec<&WidgetSchema> {
let query_lower = query.to_lowercase();
self.schemas
.values()
.filter(|s| {
s.name.to_lowercase().contains(&query_lower)
|| s.description.to_lowercase().contains(&query_lower)
|| s.tags
.iter()
.any(|t| t.to_lowercase().contains(&query_lower))
})
.collect()
}
pub fn export_catalog(&self) -> serde_json::Value {
serde_json::to_value(&self.schemas).unwrap_or_default()
}
pub fn validate_action_params(
&self,
widget_type: &str,
action: &str,
params: &serde_json::Value,
) -> Result<(), String> {
let Some(schema) = self.schemas.get(widget_type) else {
return Ok(()); };
let Some(declared) = schema.actions.iter().find(|a| a.name == action) else {
return Ok(()); };
declared.validate_params(params)
}
pub fn set_tree(&mut self, tree: UiTree) {
self.tree = Some(tree);
}
pub fn tree(&self) -> Option<&UiTree> {
self.tree.as_ref()
}
pub fn find_node(&self, agent_id: &str) -> Option<&UiNode> {
self.tree.as_ref().and_then(|t| t.find(agent_id))
}
pub fn export_tree(&self) -> serde_json::Value {
match &self.tree {
Some(tree) => serde_json::to_value(tree).unwrap_or_default(),
None => serde_json::Value::Null,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiTree {
pub root: UiNode,
}
impl UiTree {
pub fn new(root: UiNode) -> Self {
Self { root }
}
pub fn find(&self, agent_id: &str) -> Option<&UiNode> {
self.root.find(agent_id)
}
pub fn find_by_role(&self, role: SemanticRole) -> Vec<&UiNode> {
let mut results = Vec::new();
self.root.collect_by_role(role, &mut results);
results
}
pub fn focusable_nodes(&self) -> Vec<&UiNode> {
let mut results = Vec::new();
self.root.collect_by_capability("focusable", &mut results);
results
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiNode {
pub agent_id: Option<String>,
pub widget_type: String,
pub role: SemanticRole,
pub capabilities: Vec<AgentCapability>,
pub state: serde_json::Value,
pub label: Option<String>,
pub bounds: Option<NodeBounds>,
pub children: Vec<UiNode>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct NodeBounds {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
impl UiNode {
pub fn new(widget_type: impl Into<String>, role: SemanticRole) -> Self {
Self {
agent_id: None,
widget_type: widget_type.into(),
role,
capabilities: Vec::new(),
state: serde_json::Value::Null,
label: None,
bounds: None,
children: Vec::new(),
}
}
pub fn with_id(mut self, id: impl Into<String>) -> Self {
self.agent_id = Some(id.into());
self
}
pub fn with_label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn with_bounds(mut self, x: u16, y: u16, width: u16, height: u16) -> Self {
self.bounds = Some(NodeBounds {
x,
y,
width,
height,
});
self
}
pub fn with_capability(mut self, cap: AgentCapability) -> Self {
self.capabilities.push(cap);
self
}
pub fn with_state(mut self, state: serde_json::Value) -> Self {
self.state = state;
self
}
pub fn with_child(mut self, child: UiNode) -> Self {
self.children.push(child);
self
}
fn find(&self, agent_id: &str) -> Option<&UiNode> {
if self.agent_id.as_deref() == Some(agent_id) {
return Some(self);
}
for child in &self.children {
if let Some(node) = child.find(agent_id) {
return Some(node);
}
}
None
}
fn collect_by_role<'a>(&'a self, role: SemanticRole, results: &mut Vec<&'a UiNode>) {
if self.role == role {
results.push(self);
}
for child in &self.children {
child.collect_by_role(role, results);
}
}
fn collect_by_capability<'a>(&'a self, cap_name: &str, results: &mut Vec<&'a UiNode>) {
if self.capabilities.iter().any(|c| c.name() == cap_name) {
results.push(self);
}
for child in &self.children {
child.collect_by_capability(cap_name, results);
}
}
}