1use anyhow::{format_err, Error};
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, Debug, 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::Display for PeerId {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(f, "{:016x}", self.0)
41 }
42}
43
44impl FromStr for PeerId {
45 type Err = anyhow::Error;
46
47 fn from_str(src: &str) -> Result<Self, Self::Err> {
48 parse_hex_identifier(src).map(|n| PeerId(n))
49 }
50}
51
52impl From<fidl::PeerId> for PeerId {
53 fn from(src: fidl::PeerId) -> PeerId {
54 PeerId(src.value)
55 }
56}
57
58impl Into<fidl::PeerId> for PeerId {
59 fn into(self) -> fidl::PeerId {
60 fidl::PeerId { value: self.0 }
61 }
62}
63
64#[cfg(target_os = "fuchsia")]
65impl WriteInspect for PeerId {
66 fn write_inspect<'a>(
67 &self,
68 writer: &fuchsia_inspect::Node,
69 key: impl Into<std::borrow::Cow<'a, str>>,
70 ) {
71 writer.record_string(key, self.to_string());
72 }
73}
74
75#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
78pub struct HostId(pub u64);
79
80impl fmt::Display for HostId {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{:016x}", self.0)
84 }
85}
86
87impl FromStr for HostId {
88 type Err = anyhow::Error;
89
90 fn from_str(src: &str) -> Result<HostId, Error> {
93 parse_hex_identifier(&src).map(|r| HostId(r))
94 }
95}
96
97impl From<fidl::HostId> for HostId {
98 fn from(src: fidl::HostId) -> HostId {
99 HostId(src.value)
100 }
101}
102
103impl Into<fidl::HostId> for HostId {
104 fn into(self) -> fidl::HostId {
105 fidl::HostId { value: self.0 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use proptest::prelude::*;
113
114 #[test]
115 fn peerid_to_string() {
116 let testcases = vec![
117 (PeerId(0), "0000000000000000"),
119 (PeerId(1234567890), "00000000499602d2"),
121 (PeerId(123123777771778888), "01b56c6c6d7db348"),
123 (PeerId(2000037777717788818), "1bc18fc31e3b0092"),
125 (PeerId(u64::MAX), "ffffffffffffffff"),
127 ];
128
129 for (id, expected) in testcases {
130 assert_eq!(expected, id.to_string());
131 }
132 }
133
134 #[test]
135 fn id_to_string() {
136 let testcases = vec![
137 (HostId(0), "0000000000000000"),
139 (HostId(1234567890), "00000000499602d2"),
141 (HostId(123123777771778888), "01b56c6c6d7db348"),
143 (HostId(2000037777717788818), "1bc18fc31e3b0092"),
145 (HostId(u64::MAX), "ffffffffffffffff"),
147 ];
148
149 for (id, expected) in testcases {
150 assert_eq!(expected, id.to_string());
151 }
152 }
153
154 #[test]
155 fn peerid_from_string() {
156 let testcases = vec![
157 ("ffffffffffffffff", Ok(PeerId(18446744073709551615))),
159 ("0000000000000000", Ok(PeerId(0))),
161 ("10", Ok(PeerId(16))),
165 ("fe12ffdda3b89002", Ok(PeerId(18307976762614124546))),
167 ("klinvalidstr", Err(())),
169 ("90000111122223333", Err(())),
171 ];
172
173 for (input, expected) in testcases {
174 assert_eq!(expected, input.parse::<PeerId>().map_err(|_| ()))
175 }
176 }
177
178 #[test]
179 fn id_from_string() {
180 let testcases = vec![
181 ("ffffffffffffffff", Ok(HostId(18446744073709551615))),
183 ("0000000000000000", Ok(HostId(0))),
185 ("10", Ok(HostId(16))),
189 ("fe12ffdda3b89002", Ok(HostId(18307976762614124546))),
191 ("klinvalidstr", Err(())),
193 ("90000111122223333", Err(())),
195 ];
196
197 for (input, expected) in testcases {
198 assert_eq!(expected, input.parse::<HostId>().map_err(|_| ()))
199 }
200 }
201
202 proptest! {
203 #[test]
204 fn peerid_string_roundtrip(n in prop::num::u64::ANY) {
205 let peer_id = PeerId(n);
206 assert_eq!(Ok(peer_id), peer_id.to_string().parse::<PeerId>().map_err(|_| ()));
207 }
208
209 #[test]
210 fn peerid_fidl_roundtrip(n in prop::num::u64::ANY) {
211 let peer_id = PeerId(n);
212 let fidl_id: fidl::PeerId = peer_id.into();
213 assert_eq!(peer_id, PeerId::from(fidl_id));
214 }
215
216 #[test]
217 fn peerid_into_fidl(n in prop::num::u64::ANY) {
218 let peer_id = PeerId(n);
219 let fidl_p_id: fidl::PeerId = peer_id.into();
220 assert_eq!(n, fidl_p_id.value);
221 }
222
223 #[test]
224 fn id_into_fidl(n in prop::num::u64::ANY) {
225 let id = HostId(n);
226 let fidl_id: fidl::HostId = id.into();
227 assert_eq!(n, fidl_id.value);
228 }
229
230 #[test]
231 fn peer_id_from_fidl(n in prop::num::u64::ANY) {
232 let fidl_p_id = fidl::PeerId { value: n };
233 let peer_id: PeerId = fidl_p_id.into();
234 assert_eq!(PeerId(n), peer_id);
235 }
236
237 #[test]
238 fn id_from_fidl(n in prop::num::u64::ANY) {
239 let fidl_id = fidl::HostId { value: n };
240 let id: HostId = fidl_id.into();
241 assert_eq!(HostId(n), id);
242 }
243 }
244}