#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::num::NonZeroUsize;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct NonEmptyBz<T>(T);
impl<T> NonEmptyBz<T> {
pub fn new(bz: T) -> Option<Self>
where
T: AsRef<[u8]>,
{
(!bz.as_ref().is_empty()).then_some(Self(bz))
}
pub const fn get(&self) -> &T {
&self.0
}
pub fn first(&self) -> u8
where
T: AsRef<[u8]>,
{
unsafe { *self.0.as_ref().get_unchecked(0) }
}
pub fn last(&self) -> u8
where
T: AsRef<[u8]>,
{
let slice = self.0.as_ref();
unsafe { *slice.get_unchecked(slice.len() - 1) }
}
pub fn split_first(&self) -> (u8, &[u8])
where
T: AsRef<[u8]>,
{
let first = self.first();
let rest = unsafe { self.0.as_ref().get_unchecked(1..) };
(first, rest)
}
pub fn split_last(&self) -> (u8, &[u8])
where
T: AsRef<[u8]>,
{
let slice = self.0.as_ref();
let len = slice.len();
let last = self.last();
let rest = unsafe { slice.get_unchecked(..len - 1) };
(last, rest)
}
pub const fn as_ref(&self) -> NonEmptyBz<&T> {
NonEmptyBz(&self.0)
}
pub fn as_ref_slice(&self) -> NonEmptyBz<&[u8]>
where
T: AsRef<[u8]>,
{
NonEmptyBz(self.0.as_ref())
}
pub fn len(&self) -> NonZeroUsize
where
T: AsRef<[u8]>,
{
unsafe { NonZeroUsize::new_unchecked(self.0.as_ref().len()) }
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> NonEmptyBz<&T> {
pub fn cloned(&self) -> NonEmptyBz<T>
where
T: Clone,
{
NonEmptyBz(self.0.clone())
}
}
impl<const N: usize> NonEmptyBz<[u8; N]> {
pub const fn from_owned_array(bz: [u8; N]) -> Self {
const { assert!(N != 0) }
Self(bz)
}
}
impl<'a, const N: usize> NonEmptyBz<&'a [u8; N]> {
pub const fn from_borrowed_array(bz: &'a [u8; N]) -> Self {
const { assert!(N != 0) }
Self(bz)
}
pub const fn as_slice(&self) -> NonEmptyBz<&'a [u8]> {
NonEmptyBz(self.0)
}
}
#[cfg(feature = "bytes")]
impl From<NonEmptyBz<&[u8]>> for NonEmptyBz<bytes::Bytes> {
fn from(nebz_slice: NonEmptyBz<&[u8]>) -> Self {
Self(bytes::Bytes::copy_from_slice(nebz_slice.get()))
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
#[case::single_byte_owned_array(NonEmptyBz::from_owned_array([42]), 42, 42, &[], &[])]
#[case::single_byte_borrowed_array(NonEmptyBz::from_borrowed_array(b"a"), b'a', b'a', &[], &[])]
#[case::single_byte_vec(NonEmptyBz::new(vec![0]).unwrap(), 0, 0, &[], &[])]
#[case::single_byte_str(NonEmptyBz::new("z").unwrap(), b'z', b'z', &[], &[])]
#[case::multiple_bytes_owned_array(
NonEmptyBz::from_owned_array([1, 2, 3, 4]),
1,
4,
&[2, 3, 4],
&[1, 2, 3],
)]
#[case::multiple_bytes_borrowed_array(
NonEmptyBz::from_borrowed_array(b"nebz"),
b'n',
b'z',
b"ebz",
b"neb"
)]
#[case::multiple_bytes_vec(
NonEmptyBz::new(vec![255, 0, 128]).unwrap(),
255,
128,
&[0, 128],
&[255, 0],
)]
#[case::multiple_bytes_str(
NonEmptyBz::new("hello").unwrap(),
b'h',
b'o',
b"ello",
b"hell",
)]
fn first_last_works<T>(
#[case] nebz: NonEmptyBz<T>,
#[case] expected_first: u8,
#[case] expected_last: u8,
#[case] expected_split_first_rest: &[u8],
#[case] expected_split_last_rest: &[u8],
) where
T: AsRef<[u8]>,
{
assert_eq!(nebz.first(), expected_first);
assert_eq!(nebz.last(), expected_last);
let (first, rest) = nebz.split_first();
assert_eq!(first, expected_first);
assert_eq!(rest, expected_split_first_rest);
let (last, rest) = nebz.split_last();
assert_eq!(last, expected_last);
assert_eq!(rest, expected_split_last_rest);
}
#[rstest]
#[case::single_byte_owned_array(NonEmptyBz::from_owned_array([42]), NonZeroUsize::MIN)]
#[case::single_byte_borrowed_array(NonEmptyBz::from_borrowed_array(b"a"), NonZeroUsize::MIN)]
#[case::single_byte_vec(NonEmptyBz::new(vec![0]).unwrap(), NonZeroUsize::MIN)]
#[case::single_byte_str(NonEmptyBz::new("z").unwrap(), NonZeroUsize::MIN)]
#[case::multiple_bytes_owned_array(
NonEmptyBz::from_owned_array([1, 2, 3, 4]),
4.try_into().unwrap(),
)]
#[case::multiple_bytes_borrowed_array(
NonEmptyBz::from_borrowed_array(b"nebz"),
4.try_into().unwrap(),
)]
#[case::multiple_bytes_vec(NonEmptyBz::new(vec![255, 0, 128]).unwrap(), 3.try_into().unwrap())]
#[case::multiple_bytes_str(NonEmptyBz::new("hello").unwrap(), 5.try_into().unwrap())]
fn len_works<T>(#[case] nebz: NonEmptyBz<T>, #[case] expected_len: NonZeroUsize)
where
T: AsRef<[u8]>,
{
assert_eq!(nebz.len(), expected_len);
}
}