use super::{GrammarError, RoleModel, grammar};
use crate::config_edit::brazen::{ProviderRow, row_names};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum PickError {
#[error("{0}")]
Grammar(GrammarError),
#[error(
"brazen's table has no provider row `{provider}` — pick a live row here, \
or add the row to brazen's own config"
)]
UnknownProvider { provider: String },
#[error("provider row `{provider}` cannot serve a role: {why} — pick a tool-capable row")]
Incapable { provider: String, why: String },
#[error(
"`{model}` is not a plain model id — yog writes it as a block value in \
providers.yaml; use the Config editors for anything else"
)]
NotAnId { model: String },
}
pub fn role_fault(providers: &[String], role: &RoleModel) -> Option<String> {
grammar::is_unknown_row(&role.provider, providers).then(|| {
PickError::UnknownProvider {
provider: role.provider.clone(),
}
.to_string()
})
}
fn is_plain_id(model: &str) -> bool {
!model.is_empty() && !model.contains([' ', '\t', ':', '#'])
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pick {
pub role: String,
pub provider: String,
pub model: String,
}
impl Pick {
pub fn of(role: &str, provider: &str, model: &str) -> Self {
Self {
role: role.to_owned(),
provider: provider.to_owned(),
model: model.to_owned(),
}
}
}
pub fn plan(providers_yaml: &str, rows: &[ProviderRow], pick: &Pick) -> Result<String, PickError> {
if grammar::is_unknown_row(&pick.provider, &row_names(rows)) {
return Err(PickError::UnknownProvider {
provider: pick.provider.clone(),
});
}
if let Some(why) = rows
.iter()
.find(|row| row.name == pick.provider)
.and_then(ProviderRow::tools_blocked)
{
return Err(PickError::Incapable {
provider: pick.provider.clone(),
why,
});
}
if !is_plain_id(&pick.model) {
return Err(PickError::NotAnId {
model: pick.model.clone(),
});
}
grammar::set_role_model(providers_yaml, &pick.role, &pick.provider, &pick.model)
.map_err(PickError::Grammar)
}