use std::convert::Infallible;
use snafu::Snafu;
#[derive(Clone, Copy)]
pub struct NetnodeBytes<'a>(&'a [u8]);
impl<'a> NetnodeBytes<'a> {
#[doc(alias("MAXSPECSIZE"))]
pub const MAX_SIZE: usize = idakit_sys::MAXSPECSIZE;
#[inline]
#[must_use]
pub const fn as_bytes(self) -> &'a [u8] {
self.0
}
fn validate(value: &'a [u8]) -> Result<Self, NetnodeBytesError> {
match value.len() {
0 => Err(NetnodeBytesError::Empty),
len if len > Self::MAX_SIZE => Err(NetnodeBytesError::TooLarge {
len,
cap: Self::MAX_SIZE,
}),
_ => Ok(Self(value)),
}
}
}
impl std::fmt::Debug for NetnodeBytes<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("NetnodeBytes").field(&self.0).finish()
}
}
impl<'a> TryFrom<&'a [u8]> for NetnodeBytes<'a> {
type Error = NetnodeBytesError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
Self::validate(value)
}
}
impl<'a, const N: usize> TryFrom<&'a [u8; N]> for NetnodeBytes<'a> {
type Error = NetnodeBytesError;
fn try_from(value: &'a [u8; N]) -> Result<Self, Self::Error> {
Self::validate(value.as_slice())
}
}
impl<'a> TryFrom<&'a Vec<u8>> for NetnodeBytes<'a> {
type Error = NetnodeBytesError;
fn try_from(value: &'a Vec<u8>) -> Result<Self, Self::Error> {
Self::validate(value.as_slice())
}
}
impl From<Infallible> for NetnodeBytesError {
fn from(never: Infallible) -> Self {
match never {}
}
}
#[derive(Debug, Snafu, PartialEq, Eq)]
pub enum NetnodeBytesError {
#[snafu(display("netnode byte objects cannot be empty"))]
Empty,
#[snafu(display("netnode value is {len} bytes, exceeding the {cap}-byte cap"))]
TooLarge {
len: usize,
cap: usize,
},
}
#[cfg(test)]
mod tests {
use assert2::assert;
use rstest::rstest;
use super::*;
#[test]
fn empty_slice_is_rejected() {
let err = NetnodeBytes::try_from(b"".as_slice()).unwrap_err();
assert!(err == NetnodeBytesError::Empty);
}
#[test]
fn oversized_slice_is_rejected() {
let over = vec![0u8; NetnodeBytes::MAX_SIZE + 1];
let err = NetnodeBytes::try_from(over.as_slice()).unwrap_err();
assert!(
err == NetnodeBytesError::TooLarge {
len: NetnodeBytes::MAX_SIZE + 1,
cap: NetnodeBytes::MAX_SIZE,
}
);
}
#[test]
fn at_cap_is_accepted() {
let at_cap = vec![0u8; NetnodeBytes::MAX_SIZE];
let bytes = NetnodeBytes::try_from(at_cap.as_slice()).unwrap();
assert!(bytes.as_bytes() == at_cap.as_slice());
}
#[test]
fn debug_renders_the_validated_bytes() {
let bytes = NetnodeBytes::try_from(b"hi".as_slice()).unwrap();
assert!(format!("{bytes:?}") == "NetnodeBytes([104, 105])");
}
#[test]
fn accepts_array_and_vec_refs() {
assert!(NetnodeBytes::try_from(b"hi").is_ok());
assert!(NetnodeBytes::try_from(&vec![1u8, 2, 3]).is_ok());
}
#[rstest]
#[case::empty_display(NetnodeBytesError::Empty, "netnode byte objects cannot be empty")]
#[case::too_large_display(
NetnodeBytesError::TooLarge { len: 1025, cap: 1024 },
"netnode value is 1025 bytes, exceeding the 1024-byte cap",
)]
fn error_displays(#[case] err: NetnodeBytesError, #[case] expect: &str) {
assert!(err.to_string() == expect);
}
}