use libpkgx::types::PackageReq;
use rusqlite::Connection;
#[derive(Debug)]
pub enum WhichError {
CmdNotFound(String),
MultipleProjects(String, Vec<String>),
DbError(rusqlite::Error),
}
impl std::fmt::Display for WhichError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WhichError::CmdNotFound(cmd) => write!(f, "cmd not found: {}", cmd),
WhichError::MultipleProjects(cmd, projects) => {
write!(f, "multiple projects found for {}: {:?}", cmd, projects)
}
WhichError::DbError(err) => write!(f, "db error: {}", err),
}
}
}
impl std::error::Error for WhichError {}
pub async fn which(
cmd: &String,
conn: &Connection,
pkgs: &[PackageReq],
) -> Result<String, WhichError> {
let candidates =
libpkgx::pantry_db::projects_for_symbol(cmd, conn).map_err(WhichError::DbError)?;
if candidates.len() == 1 {
Ok(candidates[0].clone())
} else if candidates.is_empty() {
Err(WhichError::CmdNotFound(cmd.clone()))
} else {
let selected_pkgs = candidates
.clone()
.into_iter()
.filter(|candidate| {
pkgs.iter().any(|pkg| {
let PackageReq { project, .. } = pkg;
project == candidate
})
})
.collect::<Vec<String>>();
if selected_pkgs.len() == 1 {
Ok(selected_pkgs[0].clone())
} else {
Err(WhichError::MultipleProjects(cmd.clone(), candidates))
}
}
}