use camino::Utf8PathBuf;
use cordance_doctrine::DoctrineEntry;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::config::Config;
use crate::mcp::error::{McpToolError, McpToolResult};
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
pub struct TopicsParams {
#[serde(default)]
pub target: Option<String>,
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub struct TopicsOutput {
pub schema: String,
pub repo: String,
pub commit: Option<String>,
pub principles: Vec<TopicSummary>,
pub patterns: Vec<TopicSummary>,
pub checklists: Vec<TopicSummary>,
pub tooling: Vec<TopicSummary>,
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub struct TopicSummary {
pub topic: String,
#[schemars(with = "String")]
pub path: Utf8PathBuf,
pub sha256: String,
pub bytes: u64,
}
#[derive(Clone, Debug, Deserialize, JsonSchema)]
pub struct LookupParams {
#[serde(default)]
pub target: Option<String>,
pub topic: String,
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub struct LookupOutput {
pub schema: String,
pub repo: String,
pub commit: Option<String>,
pub matches: Vec<TopicSummary>,
}
#[allow(clippy::unnecessary_wraps)]
pub fn topics(target: &Utf8PathBuf, cfg: &Config) -> McpToolResult<TopicsOutput> {
let root = cfg.doctrine_root(target);
let idx = cordance_doctrine::load_doctrine_or_default(&root);
Ok(TopicsOutput {
schema: "cordance-doctrine-topics.v1".to_string(),
repo: idx.repo,
commit: idx.commit,
principles: idx.principles.iter().map(summarise).collect(),
patterns: idx.patterns.iter().map(summarise).collect(),
checklists: idx.checklists.iter().map(summarise).collect(),
tooling: idx.tooling.iter().map(summarise).collect(),
})
}
pub fn lookup(target: &Utf8PathBuf, cfg: &Config, topic: &str) -> McpToolResult<LookupOutput> {
if topic.is_empty() {
return Err(McpToolError::InvalidArgument("topic must not be empty".into()));
}
let root = cfg.doctrine_root(target);
let idx = cordance_doctrine::load_doctrine_or_default(&root);
let matches: Vec<TopicSummary> = idx
.principles
.iter()
.chain(idx.patterns.iter())
.chain(idx.checklists.iter())
.chain(idx.tooling.iter())
.filter(|e| e.topic.contains(topic) || topic.contains(&e.topic))
.map(summarise)
.collect();
Ok(LookupOutput {
schema: "cordance-doctrine-lookup.v1".to_string(),
repo: idx.repo,
commit: idx.commit,
matches,
})
}
fn summarise(e: &DoctrineEntry) -> TopicSummary {
TopicSummary {
topic: e.topic.clone(),
path: e.path.clone(),
sha256: e.sha256.clone(),
bytes: e.bytes,
}
}