#![forbid(unsafe_code)]
use std::{ops::Range, path::Path};
use rangemap::RangeSet;
mod kithara {
pub(crate) use kithara_test_macros::mock;
}
use crate::StorageResult;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum OpenMode {
#[default]
Auto,
ReadWrite,
ReadOnly,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WaitOutcome {
Ready,
Eof,
Interrupted,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResourceStatus {
Active,
Committed { final_len: Option<u64> },
Failed(String),
Cancelled,
}
#[kithara::mock(api = ResourceMock)]
pub trait ResourceExt: Send + Sync + 'static {
fn commit(&self, final_len: Option<u64>) -> StorageResult<()>;
fn contains_range(&self, _range: Range<u64>) -> bool {
false
}
fn fail(&self, reason: String);
fn is_empty(&self) -> bool {
self.len() == Some(0)
}
fn len(&self) -> Option<u64>;
fn next_gap(&self, _from: u64, _limit: u64) -> Option<Range<u64>> {
None
}
fn path(&self) -> Option<&Path>;
fn reactivate(&self) -> StorageResult<()>;
fn read_at(&self, offset: u64, buf: &mut [u8]) -> StorageResult<usize>;
fn read_into(&self, buf: &mut Vec<u8>) -> StorageResult<usize> {
let Some(len) = self.len() else {
let mut probe = [0u8; 1];
let _ = self.read_at(0, &mut probe)?;
return Ok(0);
};
if len == 0 {
buf.clear();
return Ok(0);
}
let len_usize = usize::try_from(len).unwrap_or(usize::MAX);
buf.resize(len_usize, 0);
let n = self.read_at(0, buf)?;
buf.truncate(n);
Ok(n)
}
fn status(&self) -> ResourceStatus;
fn wait_range(&self, range: Range<u64>) -> StorageResult<WaitOutcome>;
fn write_all(&self, data: &[u8]) -> StorageResult<()> {
self.write_at(0, data)?;
self.commit(Some(data.len() as u64))
}
fn write_at(&self, offset: u64, data: &[u8]) -> StorageResult<()>;
}
pub(crate) fn range_covered_by(available: &RangeSet<u64>, range: &Range<u64>) -> bool {
if range.is_empty() {
return true;
}
!available.gaps(range).any(|_| true)
}