saorsa_core/messaging/
user_handle.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::hash::{Hash, Hasher};
4
5/// UserHandle is a human-readable, non-network identifier for users in messaging.
6/// It is distinct from four-word network endpoints.
7#[derive(Clone, Eq, Serialize, Deserialize)]
8pub struct UserHandle(String);
9
10impl UserHandle {
11    /// Create a new handle after basic validation
12    pub fn new<S: Into<String>>(s: S) -> Result<Self, String> {
13        let v = s.into().trim().to_string();
14        if v.is_empty() {
15            return Err("handle cannot be empty".to_string());
16        }
17        if v.len() > 64 {
18            return Err("handle too long (max 64)".to_string());
19        }
20        if v.chars().any(|c| c.is_control()) {
21            return Err("handle contains control characters".to_string());
22        }
23        Ok(UserHandle(v))
24    }
25
26    pub fn as_str(&self) -> &str {
27        &self.0
28    }
29}
30
31impl From<&str> for UserHandle {
32    fn from(s: &str) -> Self {
33        UserHandle(s.trim().to_string())
34    }
35}
36
37impl From<String> for UserHandle {
38    fn from(s: String) -> Self {
39        UserHandle(s.trim().to_string())
40    }
41}
42
43impl fmt::Debug for UserHandle {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        write!(f, "UserHandle({})", self.0)
46    }
47}
48
49impl fmt::Display for UserHandle {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "{}", self.0)
52    }
53}
54
55impl PartialEq for UserHandle {
56    fn eq(&self, other: &Self) -> bool {
57        self.0.eq(&other.0)
58    }
59}
60
61impl Hash for UserHandle {
62    fn hash<H: Hasher>(&self, state: &mut H) {
63        self.0.hash(state)
64    }
65}