1use anyhow::{Error, format_err};
6use fidl_fuchsia_bluetooth as fidl;
7#[cfg(target_os = "fuchsia")]
8use fuchsia_inspect_contrib::log::WriteInspect;
9use std::fmt;
10use std::str::FromStr;
11
12fn parse_hex_identifier(hex_repr: &str) -> Result<u64, Error> {
15 if hex_repr.len() > 16 {
16 return Err(format_err!("Id string representation is longer than 16 characters"));
17 }
18 match u64::from_str_radix(hex_repr, 16) {
19 Ok(id) => Ok(id),
20 Err(_) => Err(format_err!("Id string representation is not valid hexadecimal")),
21 }
22}
23
24#[derive(Copy, Clone, Eq, Hash, PartialEq)]
29pub struct PeerId(pub u64);
30
31impl PeerId {
32 pub fn random() -> PeerId {
33 PeerId(rand::random())
34 }
35}
36
37impl fmt::Debug for PeerId {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_tuple("PeerId").field(&format_args!("0x{}", self)).finish()
40 }
41}
42
43impl fmt::Display for PeerId {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{:016x}", self.0)
47 }
48}
49
50impl FromStr for PeerId {
51 type Err = anyhow::Error;
52
53 fn from_str(src: &str) -> Result<Self, Self::Err> {
54 parse_hex_identifier(src).map(|n| PeerId(n))
55 }
56}
57
58impl From<fidl::PeerId> for PeerId {
59 fn from(src: fidl::PeerId) -> PeerId {
60 PeerId(src.value)
61 }
62}
63
64impl Into<fidl::PeerId> for PeerId {
65 fn into(self) -> fidl::PeerId {
66 fidl::PeerId { value: self.0 }
67 }
68}
69
70#[cfg(target_os = "fuchsia")]
71impl WriteInspect for PeerId {
72 fn write_inspect<'a>(
73 &self,
74 writer: &fuchsia_inspect::Node,
75 key: impl Into<std::borrow::Cow<'a, str>>,
76 ) {
77 writer.record_string(key, self.to_string());
78 }
79}
80
81#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
84pub struct HostId(pub u64);
85
86impl fmt::Display for HostId {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(f, "{:016x}", self.0)
90 }
91}
92
93impl FromStr for HostId {
94 type Err = anyhow::Error;
95
96 fn from_str(src: &str) -> Result<HostId, Error> {
99 parse_hex_identifier(&src).map(|r| HostId(r))
100 }
101}
102
103impl From<fidl::HostId> for HostId {
104 fn from(src: fidl::HostId) -> HostId {
105 HostId(src.value)
106 }
107}
108
109impl Into<fidl::HostId> for HostId {
110 fn into(self) -> fidl::HostId {
111 fidl::HostId { value: self.0 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use proptest::prelude::*;
119
120 #[test]
121 fn peer_id_debug_impl() {
122 let id = PeerId(2000037777717788818);
123 let debug = format!("{:?}", id);
124 assert_eq!(debug, "PeerId(0x1bc18fc31e3b0092)");
125
126 let small_id = PeerId(123456);
127 let padded_debug = format!("{:?}", small_id);
128 assert_eq!(padded_debug, "PeerId(0x000000000001e240)");
129 }
130
131 #[test]
132 fn peerid_to_string() {
133 let testcases = vec![
134 (PeerId(0), "0000000000000000"),
136 (PeerId(1234567890), "00000000499602d2"),
138 (PeerId(123123777771778888), "01b56c6c6d7db348"),
140 (PeerId(2000037777717788818), "1bc18fc31e3b0092"),
142 (PeerId(u64::MAX), "ffffffffffffffff"),
144 ];
145
146 for (id, expected) in testcases {
147 assert_eq!(expected, id.to_string());
148 }
149 }
150
151 #[test]
152 fn id_to_string() {
153 let testcases = vec![
154 (HostId(0), "0000000000000000"),
156 (HostId(1234567890), "00000000499602d2"),
158 (HostId(123123777771778888), "01b56c6c6d7db348"),
160 (HostId(2000037777717788818), "1bc18fc31e3b0092"),
162 (HostId(u64::MAX), "ffffffffffffffff"),
164 ];
165
166 for (id, expected) in testcases {
167 assert_eq!(expected, id.to_string());
168 }
169 }
170
171 #[test]
172 fn peerid_from_string() {
173 let testcases = vec![
174 ("ffffffffffffffff", Ok(PeerId(18446744073709551615))),
176 ("0000000000000000", Ok(PeerId(0))),
178 ("10", Ok(PeerId(16))),
182 ("fe12ffdda3b89002", Ok(PeerId(18307976762614124546))),
184 ("klinvalidstr", Err(())),
186 ("90000111122223333", Err(())),
188 ];
189
190 for (input, expected) in testcases {
191 assert_eq!(expected, input.parse::<PeerId>().map_err(|_| ()))
192 }
193 }
194
195 #[test]
196 fn id_from_string() {
197 let testcases = vec![
198 ("ffffffffffffffff", Ok(HostId(18446744073709551615))),
200 ("0000000000000000", Ok(HostId(0))),
202 ("10", Ok(HostId(16))),
206 ("fe12ffdda3b89002", Ok(HostId(18307976762614124546))),
208 ("klinvalidstr", Err(())),
210 ("90000111122223333", Err(())),
212 ];
213
214 for (input, expected) in testcases {
215 assert_eq!(expected, input.parse::<HostId>().map_err(|_| ()))
216 }
217 }
218
219 proptest! {
220 #[test]
221 fn peerid_string_roundtrip(n in prop::num::u64::ANY) {
222 let peer_id = PeerId(n);
223 assert_eq!(Ok(peer_id), peer_id.to_string().parse::<PeerId>().map_err(|_| ()));
224 }
225
226 #[test]
227 fn peerid_fidl_roundtrip(n in prop::num::u64::ANY) {
228 let peer_id = PeerId(n);
229 let fidl_id: fidl::PeerId = peer_id.into();
230 assert_eq!(peer_id, PeerId::from(fidl_id));
231 }
232
233 #[test]
234 fn peerid_into_fidl(n in prop::num::u64::ANY) {
235 let peer_id = PeerId(n);
236 let fidl_p_id: fidl::PeerId = peer_id.into();
237 assert_eq!(n, fidl_p_id.value);
238 }
239
240 #[test]
241 fn id_into_fidl(n in prop::num::u64::ANY) {
242 let id = HostId(n);
243 let fidl_id: fidl::HostId = id.into();
244 assert_eq!(n, fidl_id.value);
245 }
246
247 #[test]
248 fn peer_id_from_fidl(n in prop::num::u64::ANY) {
249 let fidl_p_id = fidl::PeerId { value: n };
250 let peer_id: PeerId = fidl_p_id.into();
251 assert_eq!(PeerId(n), peer_id);
252 }
253
254 #[test]
255 fn id_from_fidl(n in prop::num::u64::ANY) {
256 let fidl_id = fidl::HostId { value: n };
257 let id: HostId = fidl_id.into();
258 assert_eq!(HostId(n), id);
259 }
260 }
261}