use vta_sdk::client::VtaClient;
use vta_sdk::display_name::{DisplayName, NameBook, NameSource, shorten_did};
#[must_use]
pub fn principal_did(principal: &str) -> &str {
principal.split('#').next().unwrap_or(principal)
}
pub fn book_from_contexts(book: &mut NameBook, contexts: &[vta_sdk::client::ContextResponse]) {
for ctx in contexts {
if let Some(did) = &ctx.did {
book.insert(did, DisplayName::new(&ctx.name, NameSource::ContextName));
}
}
}
pub async fn book_from_vta(client: &VtaClient) -> NameBook {
let mut book = NameBook::new();
if let Ok(acl) = client.list_acl(None).await {
for entry in &acl.entries {
book.insert_opt(&entry.did, entry.label.as_deref(), NameSource::AclLabel);
}
}
if let Ok(contexts) = client.list_contexts().await {
book_from_contexts(&mut book, &contexts.contexts);
}
book
}
pub async fn resolve_agent_names_into<'a>(
book: &mut NameBook,
principals: impl IntoIterator<Item = &'a str>,
enabled: bool,
) {
if !enabled {
return;
}
let dids: Vec<&str> = principals.into_iter().map(principal_did).collect();
vta_sdk::display_name::agent_name::fill_book(book, dids).await;
}
#[must_use]
pub fn inline(book: &NameBook, principal: &str) -> String {
match book.name_of(principal_did(principal)) {
Some(name) => format!("{name} ({})", shorten_did(principal)),
None => shorten_did(principal),
}
}
#[must_use]
pub fn name_line(book: &NameBook, principal: &str) -> Option<String> {
book.name_of(principal_did(principal))
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
const DID: &str = "did:webvh:QmScidAbCdEfGhIj:example.com:ops";
#[test]
fn a_fragment_is_not_part_of_the_subject() {
assert_eq!(principal_did(&format!("{DID}#key-0")), DID);
assert_eq!(principal_did(DID), DID);
}
#[test]
fn a_key_is_named_by_the_did_that_publishes_it() {
let mut book = NameBook::new();
book.insert(DID, DisplayName::new("ops", NameSource::ContextName));
assert_eq!(name_line(&book, &format!("{DID}#key-0")).unwrap(), "ops");
assert_eq!(name_line(&book, &format!("{DID}#key-7")).unwrap(), "ops");
}
#[test]
fn inline_keeps_the_did_beside_the_name() {
let mut book = NameBook::new();
book.insert(DID, DisplayName::new("ops", NameSource::ContextName));
let out = inline(&book, DID);
assert!(out.starts_with("ops ("));
assert!(out.contains("example.com"));
}
#[test]
fn inline_falls_back_to_the_shortened_did() {
assert_eq!(inline(&NameBook::new(), DID), shorten_did(DID));
}
#[test]
fn an_unchecked_claim_stays_tagged() {
let mut book = NameBook::new();
book.insert(
DID,
DisplayName::new(
"mybank.com/@treasury",
NameSource::AgentName { verified: false },
),
);
assert!(
inline(&book, DID).contains("unverified"),
"a self-asserted name must never render as a plain one"
);
}
#[test]
fn a_context_names_its_own_did_only() {
let mut book = NameBook::new();
book_from_contexts(
&mut book,
&[serde_json::from_value(serde_json::json!({
"id": "ctx-1",
"name": "did-git-sign",
"did": DID,
"description": null,
"base_path": "/",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
}))
.unwrap()],
);
assert_eq!(name_line(&book, DID).unwrap(), "did-git-sign");
}
}