use anyhow::{format_err, Error};
use fidl_fuchsia_bluetooth as fidl;
#[cfg(target_os = "fuchsia")]
use fuchsia_inspect_contrib::log::WriteInspect;
use std::fmt;
use std::str::FromStr;
fn parse_hex_identifier(hex_repr: &str) -> Result<u64, Error> {
if hex_repr.len() > 16 {
return Err(format_err!("Id string representation is longer than 16 characters"));
}
match u64::from_str_radix(hex_repr, 16) {
Ok(id) => Ok(id),
Err(_) => Err(format_err!("Id string representation is not valid hexadecimal")),
}
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct PeerId(pub u64);
impl PeerId {
pub fn random() -> PeerId {
PeerId(rand::random())
}
}
impl fmt::Display for PeerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:016x}", self.0)
}
}
impl FromStr for PeerId {
type Err = anyhow::Error;
fn from_str(src: &str) -> Result<Self, Self::Err> {
parse_hex_identifier(src).map(|n| PeerId(n))
}
}
impl From<fidl::PeerId> for PeerId {
fn from(src: fidl::PeerId) -> PeerId {
PeerId(src.value)
}
}
impl Into<fidl::PeerId> for PeerId {
fn into(self) -> fidl::PeerId {
fidl::PeerId { value: self.0 }
}
}
#[cfg(target_os = "fuchsia")]
impl WriteInspect for PeerId {
fn write_inspect(
&self,
writer: &fuchsia_inspect::Node,
key: impl Into<fuchsia_inspect::StringReference>,
) {
writer.record_string(key, self.to_string());
}
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct HostId(pub u64);
impl fmt::Display for HostId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:016x}", self.0)
}
}
impl FromStr for HostId {
type Err = anyhow::Error;
fn from_str(src: &str) -> Result<HostId, Error> {
parse_hex_identifier(&src).map(|r| HostId(r))
}
}
impl From<fidl::HostId> for HostId {
fn from(src: fidl::HostId) -> HostId {
HostId(src.value)
}
}
impl Into<fidl::HostId> for HostId {
fn into(self) -> fidl::HostId {
fidl::HostId { value: self.0 }
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn peerid_to_string() {
let testcases = vec![
(PeerId(0), "0000000000000000"),
(PeerId(1234567890), "00000000499602d2"),
(PeerId(123123777771778888), "01b56c6c6d7db348"),
(PeerId(2000037777717788818), "1bc18fc31e3b0092"),
(PeerId(u64::MAX), "ffffffffffffffff"),
];
for (id, expected) in testcases {
assert_eq!(expected, id.to_string());
}
}
#[test]
fn id_to_string() {
let testcases = vec![
(HostId(0), "0000000000000000"),
(HostId(1234567890), "00000000499602d2"),
(HostId(123123777771778888), "01b56c6c6d7db348"),
(HostId(2000037777717788818), "1bc18fc31e3b0092"),
(HostId(u64::MAX), "ffffffffffffffff"),
];
for (id, expected) in testcases {
assert_eq!(expected, id.to_string());
}
}
#[test]
fn peerid_from_string() {
let testcases = vec![
("ffffffffffffffff", Ok(PeerId(18446744073709551615))),
("0000000000000000", Ok(PeerId(0))),
("10", Ok(PeerId(16))),
("fe12ffdda3b89002", Ok(PeerId(18307976762614124546))),
("klinvalidstr", Err(())),
("90000111122223333", Err(())),
];
for (input, expected) in testcases {
assert_eq!(expected, input.parse::<PeerId>().map_err(|_| ()))
}
}
#[test]
fn id_from_string() {
let testcases = vec![
("ffffffffffffffff", Ok(HostId(18446744073709551615))),
("0000000000000000", Ok(HostId(0))),
("10", Ok(HostId(16))),
("fe12ffdda3b89002", Ok(HostId(18307976762614124546))),
("klinvalidstr", Err(())),
("90000111122223333", Err(())),
];
for (input, expected) in testcases {
assert_eq!(expected, input.parse::<HostId>().map_err(|_| ()))
}
}
proptest! {
#[test]
fn peerid_string_roundtrip(n in prop::num::u64::ANY) {
let peer_id = PeerId(n);
assert_eq!(Ok(peer_id), peer_id.to_string().parse::<PeerId>().map_err(|_| ()));
}
#[test]
fn peerid_fidl_roundtrip(n in prop::num::u64::ANY) {
let peer_id = PeerId(n);
let fidl_id: fidl::PeerId = peer_id.into();
assert_eq!(peer_id, PeerId::from(fidl_id));
}
#[test]
fn peerid_into_fidl(n in prop::num::u64::ANY) {
let peer_id = PeerId(n);
let fidl_p_id: fidl::PeerId = peer_id.into();
assert_eq!(n, fidl_p_id.value);
}
#[test]
fn id_into_fidl(n in prop::num::u64::ANY) {
let id = HostId(n);
let fidl_id: fidl::HostId = id.into();
assert_eq!(n, fidl_id.value);
}
#[test]
fn peer_id_from_fidl(n in prop::num::u64::ANY) {
let fidl_p_id = fidl::PeerId { value: n };
let peer_id: PeerId = fidl_p_id.into();
assert_eq!(PeerId(n), peer_id);
}
#[test]
fn id_from_fidl(n in prop::num::u64::ANY) {
let fidl_id = fidl::HostId { value: n };
let id: HostId = fidl_id.into();
assert_eq!(HostId(n), id);
}
}
}