use ratatui::{
style::{Color, Style},
widgets::Cell,
};
pub use vta_sdk::display_name::{DisplayName, NameBook, NameSource, shorten_did};
use crate::render::{DIM, RESET, YELLOW};
pub const NAME_HEADER: &str = "Name";
pub const UNNAMED: &str = "\u{2014}";
#[must_use]
pub fn did_cell(did: &str) -> Cell<'static> {
Cell::from(shorten_did(did)).style(Style::default().fg(Color::DarkGray))
}
#[must_use]
pub fn name_cell(book: &NameBook, did: &str) -> Cell<'static> {
match book.get(did) {
Some(n) if n.is_trusted() => Cell::from(n.name.clone()),
Some(_) => Cell::from(book.name_of(did).unwrap_or_default())
.style(Style::default().fg(Color::Yellow)),
None => Cell::from(UNNAMED).style(Style::default().fg(Color::DarkGray)),
}
}
#[must_use]
pub fn named_did_cell(book: &NameBook, did: &str) -> Cell<'static> {
match book.name_of(did) {
Some(name) => {
let style = if book.get(did).is_some_and(DisplayName::is_trusted) {
Style::default()
} else {
Style::default().fg(Color::Yellow)
};
Cell::from(name).style(style)
}
None => did_cell(did),
}
}
#[must_use]
pub fn name_or_did(book: &NameBook, did: &str) -> String {
book.name_of(did).unwrap_or_else(|| shorten_did(did))
}
#[must_use]
pub fn inline(book: &NameBook, did: &str) -> String {
match book.name_of(did) {
Some(name) => {
let short = shorten_did(did);
if book.get(did).is_some_and(DisplayName::is_trusted) {
format!("{name} {DIM}({short}){RESET}")
} else {
format!("{YELLOW}{name}{RESET} {DIM}({short}){RESET}")
}
}
None => shorten_did(did),
}
}
#[must_use]
pub fn full_display_pairs(book: &NameBook, did: &str) -> Vec<(&'static str, String)> {
match book.name_of(did) {
Some(name) => vec![("Name", name), ("DID", did.to_string())],
None => vec![("DID", did.to_string())],
}
}
pub fn book_from_acl(book: &mut NameBook, entries: &[vta_sdk::client::AclEntryResponse]) {
for entry in entries {
book.insert_opt(&entry.did, entry.label.as_deref(), NameSource::AclLabel);
}
}
pub fn book_from_contexts(book: &mut NameBook, contexts: &[vta_sdk::contexts::ContextRecord]) {
for ctx in contexts {
if let Some(did) = &ctx.did {
book.insert(did, DisplayName::new(&ctx.name, NameSource::ContextName));
}
}
}
pub fn book_from_webvh_servers(book: &mut NameBook, servers: &[vta_sdk::webvh::WebvhServerRecord]) {
for s in servers {
book.insert_opt(&s.did, s.label.as_deref(), NameSource::ServerLabel);
}
}
#[cfg(test)]
mod tests {
use super::*;
const DID: &str = "did:webvh:QmScidAbCdEfGhIj:example.com:ops";
#[test]
fn full_display_keeps_the_did_whole() {
let mut book = NameBook::new();
book.insert(DID, DisplayName::new("ops", NameSource::AclLabel));
let pairs = full_display_pairs(&book, DID);
assert_eq!(pairs[0], ("Name", "ops".to_string()));
assert_eq!(
pairs[1],
("DID", DID.to_string()),
"full display must never abbreviate — that is what it is for"
);
}
#[test]
fn unnamed_entries_get_no_name_line() {
let book = NameBook::new();
let pairs = full_display_pairs(&book, DID);
assert_eq!(pairs.len(), 1);
assert_eq!(pairs[0].0, "DID");
}
#[test]
fn inline_marks_an_unverified_claim() {
let mut book = NameBook::new();
book.insert(
DID,
DisplayName::new(
"mybank.com/@treasury",
NameSource::AgentName { verified: false },
),
);
let out = inline(&book, DID);
assert!(out.contains("unverified"));
assert!(out.contains(YELLOW), "an unchecked claim must stand out");
}
#[test]
fn inline_falls_back_to_the_shortened_did() {
let book = NameBook::new();
assert_eq!(inline(&book, DID), shorten_did(DID));
}
}