pub(crate) enum ResolveError {
Missing,
Ambiguous(Vec<String>),
}
pub(crate) fn resolve_one<'a, T: Copy>(
request: &str,
indexed: impl Iterator<Item = (Option<&'a str>, T)> + Clone,
) -> Result<T, ResolveError> {
for (id, value) in indexed.clone() {
if id == Some(request) {
return Ok(value);
}
}
let mut suffix = Vec::new();
for (id, value) in indexed {
if let Some(id) = id {
if id.ends_with(request) {
suffix.push((id, value));
}
}
}
match suffix.as_slice() {
[] => Err(ResolveError::Missing),
[(_, value)] => Ok(*value),
_ => Err(ResolveError::Ambiguous(
suffix.into_iter().map(|(id, _)| id.to_owned()).collect(),
)),
}
}
pub(crate) fn miss_id_message(
arena: &str,
request: &str,
entry_count: u64,
all_ids: &[String],
) -> String {
const SHOWN: usize = 10;
let lower = request.to_lowercase();
let mut label = "close ids";
let mut names: Vec<&str> = all_ids
.iter()
.filter(|id| id.to_lowercase().contains(&lower))
.take(SHOWN)
.map(String::as_str)
.collect();
if names.is_empty() {
label = "ids include";
names = all_ids.iter().take(SHOWN).map(String::as_str).collect();
}
if names.is_empty() {
format!(
"no record in {arena} ({entry_count} entries) has string id {request:?}; \
none of the entries carry a JSON-string `id` field"
)
} else {
format!(
"no record in {arena} ({entry_count} entries) has id {request:?}; {label}: {}; \
pass a longer suffix or the full id",
names.join(", ")
)
}
}
pub(crate) fn ambiguous_message(request: &str, arena: &str, matches: &[String]) -> String {
const SHOWN: usize = 10;
let shown: Vec<&str> = matches.iter().take(SHOWN).map(String::as_str).collect();
format!(
"ambiguous id suffix {request:?} in {arena}; matches: {}; pass a longer \
suffix or the full id",
shown.join(", ")
)
}