use serde_json::{Value, json};
use super::Rail;
use crate::mcp::tools::ToolResult;
pub fn list<R: Rail>(rail: &R) -> ToolResult {
let record = match rail.topology() {
Ok(record) => record,
Err(error) => return ToolResult::failed(&error.to_string(), json!({})),
};
let stations: Vec<Value> = stations_of(&record)
.iter()
.map(|station| {
let mut listed = json!({
"id": station.get("id").cloned().unwrap_or(Value::Null),
"name": station.get("name").cloned().unwrap_or(Value::Null),
"device": station.get("device").cloned().unwrap_or(Value::Null),
"kind": station.get("kind").cloned().unwrap_or(Value::Null),
"state": station.get("state").cloned().unwrap_or(Value::Null),
});
if let Some(subjects) = station.get("subjects") {
listed["subjects"] = subjects.clone();
}
if let Some(activities) = station.get("activities").and_then(Value::as_array) {
listed["activities"] = activities
.iter()
.filter_map(|activity| activity.get("name").cloned())
.collect();
}
listed
})
.collect();
ToolResult::ok(json!({
"rev": record.get("rev").cloned().unwrap_or(Value::Null),
"stations": stations,
}))
}
pub fn describe<R: Rail>(rail: &R, subject: &str) -> ToolResult {
let record = match rail.topology() {
Ok(record) => record,
Err(error) => return ToolResult::failed(&error.to_string(), json!({})),
};
let served = stations_of(&record).iter().find(|station| {
station
.get("subjects")
.and_then(Value::as_array)
.is_some_and(|subjects| subjects.iter().any(|s| s.as_str() == Some(subject)))
});
served.map_or_else(
|| ToolResult::failed(&format!("no station serves {subject}"), json!({})),
|station| {
ToolResult::ok(json!({
"rev": record.get("rev").cloned().unwrap_or(Value::Null),
"subject": subject,
"station": station.get("id").cloned().unwrap_or(Value::Null),
"name": station.get("name").cloned().unwrap_or(Value::Null),
"device": station.get("device").cloned().unwrap_or(Value::Null),
"activities": station.get("activities").cloned().unwrap_or_else(|| json!([])),
}))
},
)
}
fn stations_of(record: &Value) -> &[Value] {
record
.get("stations")
.and_then(Value::as_array)
.map_or(&[], Vec::as_slice)
}
#[cfg(test)]
#[path = "directory_tests.rs"]
mod tests;