use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct RangeSpec {
pub start: u64,
pub end: u64,
}
impl RangeSpec {
pub fn new(start: u64, len: u64) -> Self {
RangeSpec {
start,
end: start.saturating_add(len),
}
}
pub fn full(len: u64) -> Self {
RangeSpec { start: 0, end: len }
}
pub fn len(&self) -> u64 {
self.end.saturating_sub(self.start)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clamp(&self, file_size: u64) -> Option<RangeSpec> {
if self.start >= file_size {
return None;
}
Some(RangeSpec {
start: self.start,
end: self.end.min(file_size),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_range() {
let r = RangeSpec::new(100, 50);
assert_eq!((r.start, r.end, r.len()), (100, 150, 50));
}
#[test]
fn clamp_limits_end() {
let r = RangeSpec::new(0, 1000).clamp(100).unwrap();
assert_eq!((r.start, r.end), (0, 100));
}
#[test]
fn clamp_rejects_past_end() {
assert!(RangeSpec::new(100, 10).clamp(99).is_none());
}
#[test]
fn clamp_rejects_zero_file() {
assert!(RangeSpec::full(0).clamp(0).is_none());
}
}