use std::num::NonZeroU64;
use num::Integer;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Track(pub u64);
impl Track {
#[must_use]
pub const fn value(self) -> u64 {
self.0
}
}
impl std::fmt::Display for Track {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Track: {:03}", self.0)
}
}
impl From<Track> for u64 {
fn from(track: Track) -> Self {
track.0
}
}
impl From<u64> for Track {
fn from(track: u64) -> Self {
Self(track)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ObjTime {
track: Track,
numerator: u64,
denominator: NonZeroU64,
}
impl ObjTime {
#[must_use]
pub fn new(track: u64, numerator: u64, denominator: u64) -> Option<Self> {
let denominator = NonZeroU64::new(denominator)?;
Some(Self::new_checked(track, numerator, denominator))
}
#[must_use]
pub fn new_checked(track: u64, numerator: u64, denominator: NonZeroU64) -> Self {
let (track, numerator) = if numerator > denominator.get() {
(
track + (numerator / denominator.get()),
numerator % denominator.get(),
)
} else {
(track, numerator)
};
let gcd = numerator.gcd(&denominator.get());
let reduced_denominator = denominator.get() / gcd;
Self {
track: Track(track),
numerator: numerator / gcd,
denominator: NonZeroU64::new(reduced_denominator)
.expect("reduced denominator must be non-zero"),
}
}
#[must_use]
pub const fn start_of(track: Track) -> Self {
Self {
track,
numerator: 0,
denominator: NonZeroU64::MIN,
}
}
#[must_use]
pub const fn track(&self) -> Track {
self.track
}
#[must_use]
pub const fn numerator(&self) -> u64 {
self.numerator
}
#[must_use]
pub const fn denominator(&self) -> NonZeroU64 {
self.denominator
}
#[must_use]
pub const fn denominator_u64(&self) -> u64 {
self.denominator.get()
}
}
impl PartialOrd for ObjTime {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ObjTime {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let self_time_in_track = self.numerator() * other.denominator().get();
let other_time_in_track = other.numerator() * self.denominator().get();
self.track()
.cmp(&other.track())
.then(self_time_in_track.cmp(&other_time_in_track))
}
}
impl std::fmt::Display for ObjTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ObjTime: {}, {} / {}",
self.track(),
self.numerator(),
self.denominator().get()
)
}
}