use crate::traits::SwapBytes;
use crate::{Defaults, IsDefault, OtToolsIoError};
use ot_tools_io_derive::{
ArrayDefaults, AsMutDerive, AsRefDerive, BoxedArrayDefaults, IsDefaultCheck,
};
use serde::{Deserialize, Serialize};
use std::array::from_fn;
use thiserror::Error;
#[derive(Debug, Error, PartialEq)]
pub enum SliceError {
#[error("invalid slice loop point: {value}")]
LoopPoint { value: u32 },
#[error("invalid slice trim: start={start} end={end}")]
Trim { start: u32, end: u32 },
}
#[derive(
Copy,
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
ArrayDefaults,
BoxedArrayDefaults,
AsMutDerive,
AsRefDerive,
IsDefaultCheck,
)]
pub struct Slice {
pub trim_start: u32,
pub trim_end: u32,
pub loop_start: u32,
}
pub const SLICE_LOOP_POINT_DISABLED: u32 = 0xFFFFFFFF;
impl Slice {
pub fn new(
trim_start: u32,
trim_end: u32,
loop_start: Option<u32>,
) -> Result<Self, OtToolsIoError> {
let loop_point = loop_start.unwrap_or(SLICE_LOOP_POINT_DISABLED);
let x = Self {
trim_start,
trim_end,
loop_start: loop_point,
};
x.validate()?;
Ok(x)
}
pub fn deinit(&mut self) {
self.trim_start = 0;
self.trim_end = 0;
self.loop_start = 0;
}
pub fn validate(&self) -> Result<(), OtToolsIoError> {
if self.is_default() {
return Ok(());
}
if self.trim_start > self.trim_end {
return Err(SliceError::Trim {
start: self.trim_start,
end: self.trim_end,
}
.into());
}
if !crate::loop_point_is_in_trim_range(self.loop_start, self.trim_start, self.trim_end)
&& self.loop_start != SLICE_LOOP_POINT_DISABLED
{
return Err(SliceError::LoopPoint {
value: self.loop_start,
}
.into());
}
Ok(())
}
}
#[allow(clippy::derivable_impls)]
impl Default for Slice {
fn default() -> Self {
Self {
trim_start: 0,
trim_end: 0,
loop_start: 0,
}
}
}
impl SwapBytes for Slice {
fn swap_bytes(self) -> Self {
Self {
trim_start: self.trim_start.swap_bytes(),
trim_end: self.trim_end.swap_bytes(),
loop_start: self.loop_start.swap_bytes(),
}
}
}
#[cfg(test)]
mod deinit {
use crate::slices::Slice;
use crate::{IsDefault, OtToolsIoError};
#[test]
fn ok() -> Result<(), OtToolsIoError> {
let mut x = Slice::new(100, 200, Some(150))?;
x.deinit();
assert!(x.is_default());
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::slices::Slice;
use crate::slices::SliceError;
use crate::slices::SLICE_LOOP_POINT_DISABLED;
use crate::OtToolsIoError;
#[test]
fn ok_no_offset_no_loop() {
let valid = Slice {
trim_start: 0,
trim_end: 1000,
loop_start: SLICE_LOOP_POINT_DISABLED,
};
let s = Slice::new(0, 1000, None);
assert!(s.is_ok());
assert_eq!(valid, s.unwrap());
}
#[test]
fn ok_loop_point() {
let valid = Slice {
trim_start: 0,
trim_end: 1000,
loop_start: 0,
};
let s = Slice::new(0, 1000, Some(0));
assert!(s.is_ok());
assert_eq!(valid, s.unwrap());
}
#[test]
fn err_loop_end() {
let s = Slice::new(0, 1000, Some(1000));
assert!(s.is_err());
assert_eq!(
s.unwrap_err().to_string(),
OtToolsIoError::Slice(SliceError::LoopPoint { value: 1000 }).to_string()
);
}
#[test]
fn err_trim() {
let (start, end) = (1001, 1000);
let s = Slice::new(start, end, None);
assert!(s.is_err());
assert_eq!(
s.unwrap_err().to_string(),
OtToolsIoError::Slice(SliceError::Trim { start, end }).to_string()
);
}
#[test]
fn ok_offset_100_no_loop() {
let valid = Slice {
trim_start: 100,
trim_end: 1100,
loop_start: SLICE_LOOP_POINT_DISABLED,
};
let s = Slice::new(100, 1100, None);
assert!(s.is_ok());
assert_eq!(valid, s.unwrap());
}
#[test]
fn ok_offset_100_with_loop_200() {
let valid = Slice {
trim_start: 100,
trim_end: 1100,
loop_start: 200,
};
let s = Slice::new(100, 1100, Some(200));
assert!(s.is_ok());
assert_eq!(valid, s.unwrap());
}
}