#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QueryTier {
DirectSql,
GraphWalk,
Llm,
}
static RELATION_KEYWORDS: &[&str] = &[
"depends on",
"touched by",
"touches",
"occurred in",
"occurs in",
"related to",
"connected to",
"what depends",
"what touches",
"what files",
"what errors",
"which files",
"which errors",
];
fn looks_like_entity_ref(query: &str) -> bool {
let trimmed = query.trim();
if trimmed.starts_with('#') && trimmed[1..].chars().all(|c| c.is_ascii_digit()) {
return true;
}
if trimmed.split_whitespace().count() == 1 {
return true;
}
false
}
fn contains_relation_keyword(query: &str) -> bool {
let lower = query.to_lowercase();
RELATION_KEYWORDS.iter().any(|kw| lower.contains(kw))
}
pub fn route_query(query: &str) -> QueryTier {
if contains_relation_keyword(query) {
return QueryTier::GraphWalk;
}
if looks_like_entity_ref(query) {
return QueryTier::DirectSql;
}
QueryTier::DirectSql
}
#[allow(dead_code)]
pub fn parse_tier(s: &str) -> Option<QueryTier> {
match s {
"auto" => None, "sql" => Some(QueryTier::DirectSql),
"walk" => Some(QueryTier::GraphWalk),
"llm" => Some(QueryTier::Llm),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn entity_ref_triggers_direct_sql() {
assert_eq!(route_query("#42"), QueryTier::DirectSql);
assert_eq!(route_query("E0308"), QueryTier::DirectSql);
assert_eq!(route_query("src/main.rs"), QueryTier::DirectSql);
}
#[test]
fn relation_keyword_triggers_graph_walk() {
assert_eq!(
route_query("what errors occurred in src/main.rs"),
QueryTier::GraphWalk
);
assert_eq!(
route_query("which files are touched by commit abc123"),
QueryTier::GraphWalk
);
assert_eq!(
route_query("what depends on dirge-core"),
QueryTier::GraphWalk
);
}
#[test]
fn ambiguous_query_falls_back_to_direct_sql() {
assert_eq!(route_query("show me everything"), QueryTier::DirectSql);
}
#[test]
fn parse_tier_variants() {
assert_eq!(parse_tier("sql"), Some(QueryTier::DirectSql));
assert_eq!(parse_tier("walk"), Some(QueryTier::GraphWalk));
assert_eq!(parse_tier("llm"), Some(QueryTier::Llm));
assert_eq!(parse_tier("auto"), None);
assert_eq!(parse_tier("garbage"), None);
}
}