1use std::borrow::Borrow;
2use std::fmt;
3use std::ops::Deref;
4use std::str::FromStr;
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8use crate::util::is_lower_hex_64;
9
10#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
11pub enum IdentifierError {
12 #[error("invalid blake3 hex (expected 64 lowercase hex chars): {0:?}")]
13 Blake3Hex(String),
14 #[error("invalid node ID hex (expected 64 lowercase hex chars): {0:?}")]
15 NodeIdHex(String),
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub struct Blake3Hex(String);
20
21impl Blake3Hex {
22 pub fn parse(value: impl Into<String>) -> Result<Self, IdentifierError> {
23 let value = value.into();
24 if is_lower_hex_64(&value) {
25 Ok(Self(value))
26 } else {
27 Err(IdentifierError::Blake3Hex(value))
28 }
29 }
30
31 pub fn as_str(&self) -> &str {
32 &self.0
33 }
34
35 pub fn into_string(self) -> String {
36 self.0
37 }
38
39 pub fn from_hash(hash: blake3::Hash) -> Self {
40 Self(hex::encode(hash.as_bytes()))
41 }
42
43 pub fn from_bytes(bytes: &[u8; 32]) -> Self {
44 Self(hex::encode(bytes))
45 }
46}
47
48impl fmt::Display for Blake3Hex {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 self.0.fmt(f)
51 }
52}
53
54impl Deref for Blake3Hex {
55 type Target = str;
56
57 fn deref(&self) -> &Self::Target {
58 self.as_str()
59 }
60}
61
62impl Borrow<str> for Blake3Hex {
63 fn borrow(&self) -> &str {
64 self.as_str()
65 }
66}
67
68impl FromStr for Blake3Hex {
69 type Err = IdentifierError;
70
71 fn from_str(s: &str) -> Result<Self, Self::Err> {
72 Self::parse(s)
73 }
74}
75
76impl TryFrom<String> for Blake3Hex {
77 type Error = IdentifierError;
78
79 fn try_from(value: String) -> Result<Self, Self::Error> {
80 Self::parse(value)
81 }
82}
83
84impl TryFrom<&str> for Blake3Hex {
85 type Error = IdentifierError;
86
87 fn try_from(value: &str) -> Result<Self, Self::Error> {
88 Self::parse(value)
89 }
90}
91
92impl From<Blake3Hex> for String {
93 fn from(value: Blake3Hex) -> Self {
94 value.into_string()
95 }
96}
97
98impl Serialize for Blake3Hex {
99 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
100 where
101 S: Serializer,
102 {
103 serializer.serialize_str(self.as_str())
104 }
105}
106
107impl<'de> Deserialize<'de> for Blake3Hex {
108 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109 where
110 D: Deserializer<'de>,
111 {
112 let value = String::deserialize(deserializer)?;
113 Self::parse(value).map_err(serde::de::Error::custom)
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
118pub struct NodeIdHex(String);
119
120impl NodeIdHex {
121 pub fn parse(value: impl Into<String>) -> Result<Self, IdentifierError> {
122 let value = value.into();
123 if is_lower_hex_64(&value) {
124 Ok(Self(value))
125 } else {
126 Err(IdentifierError::NodeIdHex(value))
127 }
128 }
129
130 pub fn as_str(&self) -> &str {
131 &self.0
132 }
133
134 pub fn into_string(self) -> String {
135 self.0
136 }
137
138 pub fn from_public_key(key: iroh::PublicKey) -> Self {
139 Self(key.to_string())
140 }
141}
142
143impl fmt::Display for NodeIdHex {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 self.0.fmt(f)
146 }
147}
148
149impl Deref for NodeIdHex {
150 type Target = str;
151
152 fn deref(&self) -> &Self::Target {
153 self.as_str()
154 }
155}
156
157impl Borrow<str> for NodeIdHex {
158 fn borrow(&self) -> &str {
159 self.as_str()
160 }
161}
162
163impl FromStr for NodeIdHex {
164 type Err = IdentifierError;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
167 Self::parse(s)
168 }
169}
170
171impl TryFrom<String> for NodeIdHex {
172 type Error = IdentifierError;
173
174 fn try_from(value: String) -> Result<Self, Self::Error> {
175 Self::parse(value)
176 }
177}
178
179impl TryFrom<&str> for NodeIdHex {
180 type Error = IdentifierError;
181
182 fn try_from(value: &str) -> Result<Self, Self::Error> {
183 Self::parse(value)
184 }
185}
186
187impl From<NodeIdHex> for String {
188 fn from(value: NodeIdHex) -> Self {
189 value.into_string()
190 }
191}
192
193impl Serialize for NodeIdHex {
194 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
195 where
196 S: Serializer,
197 {
198 serializer.serialize_str(self.as_str())
199 }
200}
201
202impl<'de> Deserialize<'de> for NodeIdHex {
203 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
204 where
205 D: Deserializer<'de>,
206 {
207 let value = String::deserialize(deserializer)?;
208 Self::parse(value).map_err(serde::de::Error::custom)
209 }
210}