- Byte-exact TCE codec (encoder/decoder, canonical numbers, vectors-tested) - Ed25519 identities with bech32m trust1… addresses - Relay client: challenge/auth handshake, request feed, responses, transparent re-auth on 401 - iced GUI (Material-dark, Solana-style palette): tab shell, approval prompts with native toasts on Linux, address book, history, settings, password change - Portable single-file vault (identity + book + history + settings) - Live 2FA roundtrip examples (send_2fa, await_2fa)
83 lines
2.5 KiB
Rust
83 lines
2.5 KiB
Rust
//! Local address book: memorable labels for trust1… addresses.
|
|
//!
|
|
//! Entries map an address string to a free-form label ("Niko"); persistence
|
|
//! lives in the vault file. The display helper renders known addresses as
|
|
//! `Label (trust1qz8jr…7tx6w)` and unknown ones as just the shortened address.
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct AddressBook {
|
|
entries: BTreeMap<String, String>,
|
|
}
|
|
|
|
impl AddressBook {
|
|
pub fn from_entries(entries: BTreeMap<String, String>) -> AddressBook {
|
|
AddressBook { entries }
|
|
}
|
|
|
|
pub fn entries(&self) -> &BTreeMap<String, String> {
|
|
&self.entries
|
|
}
|
|
|
|
pub fn label_of(&self, addr: &str) -> Option<&str> {
|
|
self.entries.get(addr).map(|s| s.as_str())
|
|
}
|
|
|
|
/// Sets (or clears, when `label` is blank) the label for an address.
|
|
/// Returns true when the map changed.
|
|
pub fn set_label(&mut self, addr: &str, label: &str) -> bool {
|
|
let label = label.trim();
|
|
if label.is_empty() {
|
|
self.entries.remove(addr).is_some()
|
|
} else {
|
|
match self.entries.get(addr) {
|
|
Some(cur) if cur == label => false,
|
|
_ => {
|
|
self.entries.insert(addr.to_string(), label.to_string());
|
|
true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Compact display form: `Niko (trust1qz8jr…7tx6w)` when known, otherwise
|
|
/// just the shortened address.
|
|
pub fn display(&self, addr: &str) -> String {
|
|
match self.label_of(addr) {
|
|
Some(l) => format!("{l} ({})", short_addr(addr)),
|
|
None => short_addr(addr),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Bech32 addresses are long; keep a compact form for the UI.
|
|
pub fn short_addr(a: &str) -> String {
|
|
if a.len() <= 20 {
|
|
a.to_string()
|
|
} else {
|
|
format!("{}…{}", &a[..10], &a[a.len() - 6..])
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn display_known_and_unknown() {
|
|
let mut b = AddressBook::default();
|
|
let addr = "trust1qz8jrkwdnqscwrvptu69z6ntd2dp092s0fk25ayq94qlzsx649dszg7tx6w";
|
|
assert_eq!(b.display(addr), short_addr(addr));
|
|
|
|
assert!(b.set_label(addr, "Niko"));
|
|
assert_eq!(b.display(addr), format!("Niko ({})", short_addr(addr)));
|
|
assert_eq!(b.label_of(addr), Some("Niko"));
|
|
|
|
// Blank label clears the entry.
|
|
b.set_label(addr, " ");
|
|
assert_eq!(b.display(addr), short_addr(addr));
|
|
// Clearing a missing entry reports no change.
|
|
assert!(!b.set_label(addr, ""));
|
|
}
|
|
}
|