use core::ops::RangeInclusive;
use thiserror::Error;
#[derive(Debug, Clone, Error)]
#[non_exhaustive]
pub enum ValidationError {
#[error("quality {value} out of valid range {valid:?}")]
QualityOutOfRange {
value: f32,
valid: RangeInclusive<f32>,
},
#[error("quality must be finite, got {value}")]
QualityNotFinite {
value: f32,
},
#[error("method {value} out of valid range {valid:?}")]
MethodOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("alpha_quality {value} out of valid range {valid:?}")]
AlphaQualityOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("sns_strength {value} out of valid range {valid:?}")]
SnsStrengthOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("filter_strength {value} out of valid range {valid:?}")]
FilterStrengthOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("filter_sharpness {value} out of valid range {valid:?}")]
FilterSharpnessOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("segments {value} out of valid range {valid:?}")]
SegmentsOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("partition_limit {value} out of valid range {valid:?}")]
PartitionLimitOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("target_psnr {value} out of valid range {valid:?}")]
TargetPsnrOutOfRange {
value: f32,
valid: RangeInclusive<f32>,
},
#[cfg(feature = "target-zensim")]
#[error("target_zensim.target {value} out of valid range {valid:?}")]
TargetZensimOutOfRange {
value: f32,
valid: RangeInclusive<f32>,
},
#[cfg(feature = "target-zensim")]
#[error("target_zensim.max_passes must be >= 1, got {value}")]
TargetZensimMaxPassesZero {
value: u8,
},
#[cfg(feature = "target-zensim")]
#[error("target_zensim.{field} must be finite and >= 0.0, got {value}")]
TargetZensimToleranceInvalid {
field: &'static str,
value: f32,
},
#[error("targets {first} and {second} are mutually exclusive")]
TargetMutuallyExclusive {
first: &'static str,
second: &'static str,
},
#[error("near_lossless {value} out of valid range {valid:?}")]
NearLosslessOutOfRange {
value: u8,
valid: RangeInclusive<u8>,
},
#[error("sharp_yuv.convergence_threshold must be finite and >= 0.0, got {value}")]
SharpYuvConvergenceThresholdInvalid {
value: f32,
},
}
pub const QUALITY_RANGE: RangeInclusive<f32> = 0.0..=100.0;
pub const METHOD_RANGE: RangeInclusive<u8> = 0..=6;
pub const ALPHA_QUALITY_RANGE: RangeInclusive<u8> = 0..=100;
pub const SNS_STRENGTH_RANGE: RangeInclusive<u8> = 0..=100;
pub const FILTER_STRENGTH_RANGE: RangeInclusive<u8> = 0..=100;
pub const FILTER_SHARPNESS_RANGE: RangeInclusive<u8> = 0..=7;
pub const SEGMENTS_RANGE: RangeInclusive<u8> = 1..=4;
pub const PARTITION_LIMIT_RANGE: RangeInclusive<u8> = 0..=100;
pub const TARGET_PSNR_RANGE: RangeInclusive<f32> = 0.0..=80.0;
#[cfg(feature = "target-zensim")]
pub const TARGET_ZENSIM_RANGE: RangeInclusive<f32> = 0.0..=100.0;
pub const NEAR_LOSSLESS_RANGE: RangeInclusive<u8> = 0..=100;
#[inline]
pub(super) fn check_quality(q: f32) -> Result<(), ValidationError> {
if !q.is_finite() {
return Err(ValidationError::QualityNotFinite { value: q });
}
if !QUALITY_RANGE.contains(&q) {
return Err(ValidationError::QualityOutOfRange {
value: q,
valid: QUALITY_RANGE,
});
}
Ok(())
}
#[inline]
pub(super) fn check_method(m: u8) -> Result<(), ValidationError> {
if !METHOD_RANGE.contains(&m) {
return Err(ValidationError::MethodOutOfRange {
value: m,
valid: METHOD_RANGE,
});
}
Ok(())
}
#[inline]
pub(super) fn check_alpha_quality(a: u8) -> Result<(), ValidationError> {
if !ALPHA_QUALITY_RANGE.contains(&a) {
return Err(ValidationError::AlphaQualityOutOfRange {
value: a,
valid: ALPHA_QUALITY_RANGE,
});
}
Ok(())
}
#[inline]
pub(super) fn check_target_psnr(p: f32) -> Result<(), ValidationError> {
if p == 0.0 {
return Ok(());
}
if !p.is_finite() || !TARGET_PSNR_RANGE.contains(&p) {
return Err(ValidationError::TargetPsnrOutOfRange {
value: p,
valid: TARGET_PSNR_RANGE,
});
}
Ok(())
}