use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use pi_result::{ClassifyErrorKind, ErrorKind, RawResult};
#[derive(Debug, pi_result::thiserror::Error)]
#[non_exhaustive]
pub enum ReadGrowthLimitError {
#[error(
"read growth final length overflows usize: current length {current_len}, \
maximum additional bytes {max_additional_bytes}"
)]
FinalLengthOverflow {
current_len: usize,
max_additional_bytes: usize,
},
}
impl ClassifyErrorKind for ReadGrowthLimitError {
fn classify_error_kind(&self) -> ErrorKind {
ErrorKind::InvalidInput
}
}
pub struct ReadGrowthLimit {
max_additional_bytes: usize,
}
impl ReadGrowthLimit {
#[must_use]
pub fn new(max_additional_bytes: usize) -> Self {
Self {
max_additional_bytes,
}
}
#[must_use]
pub fn max_additional_bytes(&self) -> usize {
self.max_additional_bytes
}
pub fn checked_final_len(&self, current_len: usize) -> RawResult<usize, ReadGrowthLimitError> {
current_len.checked_add(self.max_additional_bytes).ok_or(
ReadGrowthLimitError::FinalLengthOverflow {
current_len,
max_additional_bytes: self.max_additional_bytes,
},
)
}
}
impl Clone for ReadGrowthLimit {
fn clone(&self) -> Self {
Self::new(self.max_additional_bytes)
}
}
impl fmt::Debug for ReadGrowthLimit {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ReadGrowthLimit")
.field("max_additional_bytes", &self.max_additional_bytes)
.finish()
}
}
impl fmt::Display for ReadGrowthLimit {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"maximum additional bytes: {}",
self.max_additional_bytes
)
}
}
impl PartialEq for ReadGrowthLimit {
fn eq(&self, other: &Self) -> bool {
self.max_additional_bytes == other.max_additional_bytes
}
}
impl Eq for ReadGrowthLimit {}
impl PartialOrd for ReadGrowthLimit {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ReadGrowthLimit {
fn cmp(&self, other: &Self) -> Ordering {
self.max_additional_bytes
.cmp(&other.max_additional_bytes)
}
}
impl Hash for ReadGrowthLimit {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.max_additional_bytes.hash(state);
}
}
#[non_exhaustive]
pub enum ReadGrowthOutcome {
EndOfFile {
appended_bytes: usize,
},
LimitReached {
appended_bytes: usize,
},
}
impl ReadGrowthOutcome {
#[must_use]
pub fn appended_bytes(&self) -> usize {
match self {
Self::EndOfFile { appended_bytes }
| Self::LimitReached { appended_bytes } => *appended_bytes,
}
}
#[must_use]
pub fn is_end_of_file(&self) -> bool {
matches!(self, Self::EndOfFile { .. })
}
#[must_use]
pub fn is_limit_reached(&self) -> bool {
matches!(self, Self::LimitReached { .. })
}
}
impl Clone for ReadGrowthOutcome {
fn clone(&self) -> Self {
match self {
Self::EndOfFile { appended_bytes } => Self::EndOfFile {
appended_bytes: *appended_bytes,
},
Self::LimitReached { appended_bytes } => Self::LimitReached {
appended_bytes: *appended_bytes,
},
}
}
}
impl fmt::Debug for ReadGrowthOutcome {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EndOfFile { appended_bytes } => formatter
.debug_struct("EndOfFile")
.field("appended_bytes", appended_bytes)
.finish(),
Self::LimitReached { appended_bytes } => formatter
.debug_struct("LimitReached")
.field("appended_bytes", appended_bytes)
.finish(),
}
}
}
impl fmt::Display for ReadGrowthOutcome {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EndOfFile { appended_bytes } => write!(
formatter,
"end of file after appending {appended_bytes} bytes"
),
Self::LimitReached { appended_bytes } => write!(
formatter,
"growth limit reached after appending {appended_bytes} bytes"
),
}
}
}
impl PartialEq for ReadGrowthOutcome {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::EndOfFile {
appended_bytes: left,
},
Self::EndOfFile {
appended_bytes: right,
},
)
| (
Self::LimitReached {
appended_bytes: left,
},
Self::LimitReached {
appended_bytes: right,
},
) => left == right,
_ => false,
}
}
}
impl Eq for ReadGrowthOutcome {}
impl PartialOrd for ReadGrowthOutcome {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ReadGrowthOutcome {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(
Self::EndOfFile {
appended_bytes: left,
},
Self::EndOfFile {
appended_bytes: right,
},
)
| (
Self::LimitReached {
appended_bytes: left,
},
Self::LimitReached {
appended_bytes: right,
},
) => left.cmp(right),
(Self::EndOfFile { .. }, Self::LimitReached { .. }) => Ordering::Less,
(Self::LimitReached { .. }, Self::EndOfFile { .. }) => Ordering::Greater,
}
}
}
impl Hash for ReadGrowthOutcome {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
match self {
Self::EndOfFile { appended_bytes } => {
0_u8.hash(state);
appended_bytes.hash(state);
}
Self::LimitReached { appended_bytes } => {
1_u8.hash(state);
appended_bytes.hash(state);
}
}
}
}