auths_cli/ux/ident.rs
1//! Product-facing rendering of identifiers for human-readable output.
2//!
3//! Identifiers are canonically `did:keri:<prefix>` (a W3C DID with a
4//! self-addressing prefix). That canonical form is correct for `--json`, files,
5//! bundles, trailers, and anything a machine consumes — it MUST never change.
6//!
7//! In **human-facing TEXT** output, however, the first-run surface should speak
8//! the product's own vocabulary, not the underlying protocol's. This module maps
9//! the canonical DID to a product handle (`auths:<prefix>`) for display only. The
10//! prefix is preserved verbatim, so the handle is still unambiguous and a user
11//! who needs the canonical form can recover it (or read `--json`).
12//!
13//! Use [`product_id`] anywhere a `did:keri:` would otherwise be printed to a
14//! person; never use it where the value is parsed, stored, or transmitted.
15
16/// The canonical DID method prefix carried by every identity string.
17const DID_METHOD_PREFIX: &str = "did:keri:";
18
19/// The product-facing scheme shown to people in place of the method prefix.
20const PRODUCT_PREFIX: &str = "auths:";
21
22/// Render an identifier in product-facing form for human TEXT output.
23///
24/// `did:keri:<prefix>` becomes `auths:<prefix>`. Any string that is not a
25/// `did:keri:` identifier (already a product handle, an unexpected shape, etc.)
26/// is returned unchanged, so this is always safe to wrap an arbitrary id in.
27///
28/// This is a DISPLAY transform only. It is never the inverse of any parser:
29/// nothing in the codebase reads `auths:` back; the canonical `did:keri:` form
30/// remains the single source of truth for `--json`, storage, and the wire.
31///
32/// Args:
33/// * `did`: An identifier, canonically `did:keri:<prefix>`.
34///
35/// Usage:
36/// ```ignore
37/// let shown = product_id(&result.identity_did); // "auths:EPme…"
38/// println!(" Identity: {shown}");
39/// ```
40pub fn product_id(did: &str) -> String {
41 match did.strip_prefix(DID_METHOD_PREFIX) {
42 Some(prefix) => format!("{PRODUCT_PREFIX}{prefix}"),
43 None => did.to_string(),
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn maps_canonical_did_to_product_handle() {
53 assert_eq!(
54 product_id("did:keri:EPmeUXn8Gbk_4u0ilbMnA46y6Q_5X1nRIpjPxCDmejml"),
55 "auths:EPmeUXn8Gbk_4u0ilbMnA46y6Q_5X1nRIpjPxCDmejml"
56 );
57 }
58
59 #[test]
60 fn preserves_the_prefix_verbatim() {
61 let prefix = "EBb6li302WtvHTrhg4FUmFuiWjrQEb-2mOD1TYrVd6ge";
62 let shown = product_id(&format!("did:keri:{prefix}"));
63 assert!(shown.ends_with(prefix), "prefix must be preserved exactly");
64 }
65
66 #[test]
67 fn passes_through_non_did_strings_unchanged() {
68 assert_eq!(
69 product_id("auths:already-a-handle"),
70 "auths:already-a-handle"
71 );
72 assert_eq!(product_id(""), "");
73 assert_eq!(product_id("did:key:zDnaeh"), "did:key:zDnaeh");
74 }
75
76 #[test]
77 fn display_form_carries_no_protocol_method_token() {
78 // The product handle must not surface the DID method token to a person.
79 let shown = product_id("did:keri:EPmeUXn8Gbk_4u0ilbMnA46y6Q");
80 assert!(!shown.contains("did:keri:"));
81 assert!(shown.starts_with("auths:"));
82 }
83}