use std::borrow::Cow;
use std::ops::Deref;
use derive_more::derive::Display;
use thiserror::Error;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ArrayBytesOffsets<'a>(Cow<'a, [usize]>);
impl Deref for ArrayBytesOffsets<'_> {
type Target = [usize];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, Display, Error)]
pub enum ArrayBytesRawOffsetsCreateError {
#[display("offsets length must be greater than zero")]
ZeroLength,
#[display("offsets are not monotonically increasing")]
NotMonotonicallyIncreasing,
}
impl<'a> ArrayBytesOffsets<'a> {
pub fn new(
offsets: impl Into<Cow<'a, [usize]>>,
) -> Result<Self, ArrayBytesRawOffsetsCreateError> {
let offsets = offsets.into();
if offsets.is_empty() {
Err(ArrayBytesRawOffsetsCreateError::ZeroLength)
} else if offsets.windows(2).all(|w| w[1] >= w[0]) {
Ok(Self(offsets))
} else {
Err(ArrayBytesRawOffsetsCreateError::NotMonotonicallyIncreasing)
}
}
#[must_use]
pub unsafe fn new_unchecked(offsets: impl Into<Cow<'a, [usize]>>) -> Self {
let offsets = offsets.into();
debug_assert!(!offsets.is_empty());
debug_assert!(offsets.windows(2).all(|w| w[1] >= w[0]));
Self(offsets)
}
#[must_use]
pub fn into_owned(self) -> ArrayBytesOffsets<'static> {
ArrayBytesOffsets(self.0.into_owned().into())
}
#[must_use]
pub fn last(&self) -> usize {
unsafe {
*self.0.last().unwrap_unchecked()
}
}
}
impl<'a> TryFrom<Cow<'a, [usize]>> for ArrayBytesOffsets<'a> {
type Error = ArrayBytesRawOffsetsCreateError;
fn try_from(value: Cow<'a, [usize]>) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl<'a> TryFrom<&'a [usize]> for ArrayBytesOffsets<'a> {
type Error = ArrayBytesRawOffsetsCreateError;
fn try_from(value: &'a [usize]) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl<'a, const N: usize> TryFrom<&'a [usize; N]> for ArrayBytesOffsets<'a> {
type Error = ArrayBytesRawOffsetsCreateError;
fn try_from(value: &'a [usize; N]) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl TryFrom<Vec<usize>> for ArrayBytesOffsets<'_> {
type Error = ArrayBytesRawOffsetsCreateError;
fn try_from(value: Vec<usize>) -> Result<Self, Self::Error> {
Self::new(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_bytes_offsets() {
let offsets = ArrayBytesOffsets::new(vec![0, 1, 2, 3]).unwrap();
assert_eq!(&*offsets, &[0, 1, 2, 3]);
assert!(ArrayBytesOffsets::new(vec![]).is_err());
assert!(ArrayBytesOffsets::new(vec![0]).is_ok());
assert!(ArrayBytesOffsets::new(vec![10]).is_ok()); assert!(ArrayBytesOffsets::new(vec![0, 1, 1]).is_ok());
assert!(ArrayBytesOffsets::new(vec![0, 1, 0]).is_err());
assert!(ArrayBytesOffsets::try_from(vec![0, 1, 2]).is_ok());
assert!(ArrayBytesOffsets::try_from(vec![0, 1, 0]).is_err());
assert!(ArrayBytesOffsets::try_from([0, 1, 2].as_slice()).is_ok());
assert!(ArrayBytesOffsets::try_from([0, 1, 0].as_slice()).is_err());
assert!(ArrayBytesOffsets::try_from(&[0, 1, 2]).is_ok());
assert!(ArrayBytesOffsets::try_from(&[0, 1, 0]).is_err());
assert!(ArrayBytesOffsets::try_from(Cow::Owned(vec![0, 1, 0])).is_err());
}
}