use super::dispatch::{DetectScore, ToolDialect};
use super::{DialectError, DialectEvidence, Gemma3ToolCodeDialect, OpenAiDialect, ToolDialectId};
use crate::Error;
#[non_exhaustive]
pub struct ToolDialectRegistry {
dialects: Vec<Box<dyn ToolDialect>>,
}
impl std::fmt::Debug for ToolDialectRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ids: Vec<ToolDialectId> = self.dialects.iter().map(|d| d.id()).collect();
f.debug_struct("ToolDialectRegistry")
.field("dialects", &ids)
.finish()
}
}
impl ToolDialectRegistry {
#[must_use]
pub fn builtin() -> ToolDialectRegistry {
ToolDialectRegistry {
dialects: vec![Box::new(OpenAiDialect), Box::new(Gemma3ToolCodeDialect)],
}
}
#[must_use]
pub(crate) fn get(&self, id: ToolDialectId) -> Option<&dyn ToolDialect> {
self.dialects
.iter()
.find(|d| d.id() == id)
.map(std::convert::AsRef::as_ref)
}
pub fn resolve(
&self,
evidence: &DialectEvidence,
) -> std::result::Result<ToolDialectId, DialectError> {
let mut best: Option<DetectScore> = None;
let mut leader: Option<ToolDialectId> = None;
let mut tied: Vec<ToolDialectId> = Vec::new();
for dialect in &self.dialects {
let Some(score) = dialect.detect(evidence) else {
continue;
};
let id = dialect.id();
match best {
Some(current) if score < current => {}
Some(current) if score == current => tied.push(id),
_ => {
best = Some(score);
leader = Some(id);
tied.clear();
tied.push(id);
}
}
}
let Some(leader) = leader else {
return Err(DialectError::from(Error::DialectNone));
};
if tied.len() > 1 {
return Err(DialectError::from(Error::DialectTie { candidates: tied }));
}
Ok(leader)
}
}