105 lines
2.6 KiB
Rust
105 lines
2.6 KiB
Rust
use super::errors::ValidationError;
|
|
|
|
pub(crate) fn validate_range<T, R>(
|
|
value: T,
|
|
range: R,
|
|
message: impl Fn() -> String,
|
|
) -> Result<(), ValidationError>
|
|
where
|
|
T: PartialOrd,
|
|
R: std::ops::RangeBounds<T>,
|
|
{
|
|
if !range.contains(&value) {
|
|
return Err(ValidationError::value(message()));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn validate_ascii_padded(
|
|
value: &[u8],
|
|
length: usize,
|
|
padding: u8,
|
|
field: &str,
|
|
) -> Result<(), ValidationError> {
|
|
if value.len() != length {
|
|
return Err(ValidationError::value(format!(
|
|
"{} must be exactly {} bytes",
|
|
field, length
|
|
)));
|
|
}
|
|
|
|
for &byte in value {
|
|
if !((0x20..=0x7E).contains(&byte) || byte == padding) {
|
|
return Err(ValidationError::value(format!(
|
|
"Invalid ASCII character: {:#04x}",
|
|
byte
|
|
)));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn validate_bitmask(
|
|
value: u8,
|
|
allowed_bits: u8,
|
|
message: impl Fn() -> String,
|
|
) -> Result<(), ValidationError> {
|
|
if value & !allowed_bits != 0 {
|
|
return Err(ValidationError::value(message()));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn validate_range_accepts_bounds() {
|
|
assert!(validate_range(0u8, 0..=10, || "".into()).is_ok());
|
|
assert!(validate_range(10u8, 0..=10, || "".into()).is_ok());
|
|
assert!(validate_range(5u8, 0..=10, || "".into()).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn validate_range_rejects_out_of_range() {
|
|
let err = validate_range(11u8, 0..=10, || "bad value".into()).unwrap_err();
|
|
|
|
assert!(err.to_string().contains("bad value"));
|
|
}
|
|
|
|
#[test]
|
|
fn validate_ascii_padded_accepts_ascii_and_padding() {
|
|
assert!(validate_ascii_padded(b"ABC ", 4, b' ', "name",).is_ok());
|
|
|
|
assert!(validate_ascii_padded(&[b'A', 0, 0, 0], 4, 0, "password",).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn validate_ascii_padded_rejects_wrong_length() {
|
|
assert!(validate_ascii_padded(b"ABC", 4, b' ', "name",).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn validate_ascii_padded_rejects_non_ascii() {
|
|
assert!(validate_ascii_padded(&[0xFF, b'A', b'B', b'C'], 4, b' ', "name",).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn validate_bitmask_accepts_valid_bits() {
|
|
assert!(validate_bitmask(0b0101, 0b1111, || "".into(),).is_ok());
|
|
|
|
assert!(validate_bitmask(0, 0b1111, || "".into(),).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn validate_bitmask_rejects_invalid_bits() {
|
|
let err = validate_bitmask(0b10000, 0b01111, || "invalid mask".into()).unwrap_err();
|
|
|
|
assert!(err.to_string().contains("invalid mask"));
|
|
}
|
|
}
|