vta_cli_common/
display.rs1use ratatui::{
23 style::{Color, Style},
24 widgets::Cell,
25};
26
27pub use vta_sdk::display_name::{DisplayName, NameBook, NameSource, shorten_did};
28
29use crate::render::{DIM, RESET, YELLOW};
30
31pub const NAME_HEADER: &str = "Name";
33
34pub const UNNAMED: &str = "\u{2014}";
37
38#[must_use]
40pub fn did_cell(did: &str) -> Cell<'static> {
41 Cell::from(shorten_did(did)).style(Style::default().fg(Color::DarkGray))
42}
43
44#[must_use]
49pub fn name_cell(book: &NameBook, did: &str) -> Cell<'static> {
50 match book.get(did) {
51 Some(n) if n.is_trusted() => Cell::from(n.name.clone()),
52 Some(_) => Cell::from(book.name_of(did).unwrap_or_default())
53 .style(Style::default().fg(Color::Yellow)),
54 None => Cell::from(UNNAMED).style(Style::default().fg(Color::DarkGray)),
55 }
56}
57
58#[must_use]
63pub fn named_did_cell(book: &NameBook, did: &str) -> Cell<'static> {
64 match book.name_of(did) {
65 Some(name) => {
66 let style = if book.get(did).is_some_and(DisplayName::is_trusted) {
67 Style::default()
68 } else {
69 Style::default().fg(Color::Yellow)
70 };
71 Cell::from(name).style(style)
72 }
73 None => did_cell(did),
74 }
75}
76
77#[must_use]
82pub fn name_or_did(book: &NameBook, did: &str) -> String {
83 book.name_of(did).unwrap_or_else(|| shorten_did(did))
84}
85
86#[must_use]
92pub fn inline(book: &NameBook, did: &str) -> String {
93 match book.name_of(did) {
94 Some(name) => {
95 let short = shorten_did(did);
96 if book.get(did).is_some_and(DisplayName::is_trusted) {
97 format!("{name} {DIM}({short}){RESET}")
98 } else {
99 format!("{YELLOW}{name}{RESET} {DIM}({short}){RESET}")
100 }
101 }
102 None => shorten_did(did),
103 }
104}
105
106#[must_use]
113pub fn full_display_pairs(book: &NameBook, did: &str) -> Vec<(&'static str, String)> {
114 match book.name_of(did) {
115 Some(name) => vec![("Name", name), ("DID", did.to_string())],
116 None => vec![("DID", did.to_string())],
117 }
118}
119
120pub fn book_from_acl(book: &mut NameBook, entries: &[vta_sdk::client::AclEntryResponse]) {
133 for entry in entries {
134 book.insert_opt(&entry.did, entry.label.as_deref(), NameSource::AclLabel);
135 }
136}
137
138pub fn book_from_contexts(book: &mut NameBook, contexts: &[vta_sdk::contexts::ContextRecord]) {
140 for ctx in contexts {
141 if let Some(did) = &ctx.did {
142 book.insert(did, DisplayName::new(&ctx.name, NameSource::ContextName));
143 }
144 }
145}
146
147pub fn book_from_webvh_servers(book: &mut NameBook, servers: &[vta_sdk::webvh::WebvhServerRecord]) {
149 for s in servers {
150 book.insert_opt(&s.did, s.label.as_deref(), NameSource::ServerLabel);
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 const DID: &str = "did:webvh:QmScidAbCdEfGhIj:example.com:ops";
159
160 #[test]
161 fn full_display_keeps_the_did_whole() {
162 let mut book = NameBook::new();
163 book.insert(DID, DisplayName::new("ops", NameSource::AclLabel));
164 let pairs = full_display_pairs(&book, DID);
165 assert_eq!(pairs[0], ("Name", "ops".to_string()));
166 assert_eq!(
167 pairs[1],
168 ("DID", DID.to_string()),
169 "full display must never abbreviate — that is what it is for"
170 );
171 }
172
173 #[test]
174 fn unnamed_entries_get_no_name_line() {
175 let book = NameBook::new();
176 let pairs = full_display_pairs(&book, DID);
177 assert_eq!(pairs.len(), 1);
178 assert_eq!(pairs[0].0, "DID");
179 }
180
181 #[test]
182 fn inline_marks_an_unverified_claim() {
183 let mut book = NameBook::new();
184 book.insert(
185 DID,
186 DisplayName::new(
187 "mybank.com/@treasury",
188 NameSource::AgentName { verified: false },
189 ),
190 );
191 let out = inline(&book, DID);
192 assert!(out.contains("unverified"));
193 assert!(out.contains(YELLOW), "an unchecked claim must stand out");
194 }
195
196 #[test]
197 fn inline_falls_back_to_the_shortened_did() {
198 let book = NameBook::new();
199 assert_eq!(inline(&book, DID), shorten_did(DID));
200 }
201}