use crate::markers::SlotMarkers;
use crate::traits::SwapBytes;
use ot_tools_io_derive::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(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, Hash, 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, SliceError> {
if trim_start > trim_end {
return Err(SliceError::Trim {
start: trim_start,
end: trim_end,
});
}
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 validate(&self) -> Result<bool, SliceError> {
if self.trim_start > self.trim_end {
return Err(SliceError::Trim {
start: self.trim_start,
end: self.trim_end,
});
}
if !crate::check_loop_point(self.loop_start, self.trim_start, self.trim_end) {
return Err(SliceError::LoopPoint {
value: self.loop_start,
});
}
Ok(true)
}
}
#[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(),
}
}
}
#[derive(Debug)]
pub struct Slices {
pub slices: [Slice; 64],
pub count: u32,
}
impl Default for Slices {
fn default() -> Self {
let slices: [Slice; 64] = from_fn(|_| Slice::default());
Slices { slices, count: 0 }
}
}
impl From<SlotMarkers> for Slices {
fn from(value: SlotMarkers) -> Self {
Self {
slices: value.slices,
count: value.slice_count,
}
}
}
impl From<&SlotMarkers> for Slices {
fn from(value: &SlotMarkers) -> Self {
Self::from(value.clone())
}
}
#[cfg(test)]
mod slices_from {
use crate::slices::SliceError;
use crate::slices::Slices;
use crate::MarkersFile;
#[test]
fn owned() -> Result<(), SliceError> {
let markers = MarkersFile::default().flex_slots[0].clone();
let _ = Slices::from(markers);
Ok(())
}
#[test]
fn borrowed() -> Result<(), SliceError> {
let markers = MarkersFile::default().flex_slots[0].clone();
let _ = Slices::from(&markers);
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::slices::Slice;
use crate::slices::SliceError;
#[test]
fn ok_no_offset_no_loop() {
let valid = Slice {
trim_start: 0,
trim_end: 1000,
loop_start: 0xFFFFFFFF,
};
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(), SliceError::LoopPoint { value: 1000 });
}
#[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(), SliceError::Trim { start, end });
}
#[test]
fn ok_offset_100_no_loop() {
let valid = Slice {
trim_start: 100,
trim_end: 1100,
loop_start: 0xFFFFFFFF,
};
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());
}
}