use crate::materialize::World;
const MIN_SHORT: usize = 4;
pub fn short_id(world: &World, id: &str) -> String {
let mut len = MIN_SHORT;
while len < id.len() {
let prefix = &id[..len];
let collisions = world
.tasks
.keys()
.filter(|other| other.as_str() != id && other.starts_with(prefix))
.count();
if collisions == 0 {
return format!("lv-{prefix}");
}
len += 2;
}
format!("lv-{id}")
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PrefixError {
NotFound(String),
Ambiguous(String, Vec<String>),
}
impl std::fmt::Display for PrefixError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PrefixError::NotFound(p) => write!(f, "no task matches '{p}'"),
PrefixError::Ambiguous(p, ids) => {
write!(f, "'{p}' is ambiguous: matches {}", ids.join(", "))
}
}
}
}
impl std::error::Error for PrefixError {}
pub fn resolve_prefix<'w>(world: &'w World, input: &str) -> Result<&'w str, PrefixError> {
let prefix = input.strip_prefix("lv-").unwrap_or(input);
let mut matches: Vec<&str> = world
.tasks
.keys()
.filter(|id| id.starts_with(prefix))
.map(String::as_str)
.collect();
match matches.len() {
0 => Err(PrefixError::NotFound(input.to_string())),
1 => Ok(matches.remove(0)),
_ => Err(PrefixError::Ambiguous(
input.to_string(),
matches.iter().map(|id| short_id_of(id)).collect(),
)),
}
}
fn short_id_of(id: &str) -> String {
format!("lv-{}", &id[..id.len().min(8)])
}