use crate::prelude::{DbmsResult, Sanitize, Value};
pub struct ClampSanitizer {
pub min: i64,
pub max: i64,
}
impl Sanitize for ClampSanitizer {
fn sanitize(&self, value: Value) -> DbmsResult<Value> {
match value {
Value::Int8(num) => {
let clamped = self.clamp_within(num.0.into(), i8::MIN.into(), i8::MAX.into());
Ok(Value::Int8((clamped as i8).into()))
}
Value::Int16(num) => {
let clamped = self.clamp_within(num.0.into(), i16::MIN.into(), i16::MAX.into());
Ok(Value::Int16((clamped as i16).into()))
}
Value::Int32(num) => {
let clamped = self.clamp_within(num.0.into(), i32::MIN.into(), i32::MAX.into());
Ok(Value::Int32((clamped as i32).into()))
}
Value::Int64(num) => {
let clamped = num.0.clamp(self.min, self.max);
Ok(Value::Int64(clamped.into()))
}
other => Ok(other),
}
}
}
impl ClampSanitizer {
fn clamp_within(&self, num: i64, type_min: i64, type_max: i64) -> i64 {
num.clamp(self.min, self.max).clamp(type_min, type_max)
}
}
pub struct ClampUnsignedSanitizer {
pub min: u64,
pub max: u64,
}
impl Sanitize for ClampUnsignedSanitizer {
fn sanitize(&self, value: Value) -> DbmsResult<Value> {
match value {
Value::Uint8(num) => {
let clamped = self.clamp_within(num.0.into(), u8::MAX.into());
Ok(Value::Uint8((clamped as u8).into()))
}
Value::Uint16(num) => {
let clamped = self.clamp_within(num.0.into(), u16::MAX.into());
Ok(Value::Uint16((clamped as u16).into()))
}
Value::Uint32(num) => {
let clamped = self.clamp_within(num.0.into(), u32::MAX.into());
Ok(Value::Uint32((clamped as u32).into()))
}
Value::Uint64(num) => {
let clamped = num.0.clamp(self.min, self.max);
Ok(Value::Uint64(clamped.into()))
}
other => Ok(other),
}
}
}
impl ClampUnsignedSanitizer {
fn clamp_within(&self, num: u64, type_max: u64) -> u64 {
num.clamp(self.min, self.max).min(type_max)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clamp_sanitizer_i32() {
let sanitizer = ClampSanitizer { min: 0, max: 100 };
let value_in_range = Value::Int32(50.into());
let value_below_range = Value::Int32((-10i32).into());
let value_above_range = Value::Int32(150.into());
let non_integer_value = Value::Text("Not an integer".into());
let sanitized_in_range = sanitizer.sanitize(value_in_range).unwrap();
let sanitized_below_range = sanitizer.sanitize(value_below_range).unwrap();
let sanitized_above_range = sanitizer.sanitize(value_above_range).unwrap();
let sanitized_non_integer = sanitizer.sanitize(non_integer_value).unwrap();
assert_eq!(sanitized_in_range, Value::Int32(50.into()));
assert_eq!(sanitized_below_range, Value::Int32(0.into()));
assert_eq!(sanitized_above_range, Value::Int32(100.into()));
assert_eq!(sanitized_non_integer, Value::Text("Not an integer".into()));
}
#[test]
fn test_clamp_sanitizer_i64() {
let sanitizer = ClampSanitizer { min: 0, max: 100 };
let value_in_range = Value::Int64(50.into());
let value_below_range = Value::Int64((-10i64).into());
let value_above_range = Value::Int64(150.into());
let non_integer_value = Value::Text("Not an integer".into());
let sanitized_in_range = sanitizer.sanitize(value_in_range).unwrap();
let sanitized_below_range = sanitizer.sanitize(value_below_range).unwrap();
let sanitized_above_range = sanitizer.sanitize(value_above_range).unwrap();
let sanitized_non_integer = sanitizer.sanitize(non_integer_value).unwrap();
assert_eq!(sanitized_in_range, Value::Int64(50.into()));
assert_eq!(sanitized_below_range, Value::Int64(0.into()));
assert_eq!(sanitized_above_range, Value::Int64(100.into()));
assert_eq!(sanitized_non_integer, Value::Text("Not an integer".into()));
}
#[test]
fn test_clamp_unsigned_sanitizer_u32() {
let sanitizer = ClampUnsignedSanitizer { min: 0, max: 100 };
let value_in_range = Value::Uint32(50.into());
let value_below_range = Value::Uint32(0.into()); let value_above_range = Value::Uint32(150.into());
let non_integer_value = Value::Text("Not an integer".into());
let sanitized_in_range = sanitizer.sanitize(value_in_range).unwrap();
let sanitized_below_range = sanitizer.sanitize(value_below_range).unwrap();
let sanitized_above_range = sanitizer.sanitize(value_above_range).unwrap();
let sanitized_non_integer = sanitizer.sanitize(non_integer_value).unwrap();
assert_eq!(sanitized_in_range, Value::Uint32(50.into()));
assert_eq!(sanitized_below_range, Value::Uint32(0.into()));
assert_eq!(sanitized_above_range, Value::Uint32(100.into()));
assert_eq!(sanitized_non_integer, Value::Text("Not an integer".into()));
}
#[test]
fn test_clamp_unsigned_sanitizer_u64() {
let sanitizer = ClampUnsignedSanitizer { min: 0, max: 100 };
let value_in_range = Value::Uint64(50.into());
let value_below_range = Value::Uint64(0.into()); let value_above_range = Value::Uint64(150.into());
let non_integer_value = Value::Text("Not an integer".into());
let sanitized_in_range = sanitizer.sanitize(value_in_range).unwrap();
let sanitized_below_range = sanitizer.sanitize(value_below_range).unwrap();
let sanitized_above_range = sanitizer.sanitize(value_above_range).unwrap();
let sanitized_non_integer = sanitizer.sanitize(non_integer_value).unwrap();
assert_eq!(sanitized_in_range, Value::Uint64(50.into()));
assert_eq!(sanitized_below_range, Value::Uint64(0.into()));
assert_eq!(sanitized_above_range, Value::Uint64(100.into()));
assert_eq!(sanitized_non_integer, Value::Text("Not an integer".into()));
}
#[test]
fn test_clamp_sanitizer_i8() {
let sanitizer = ClampSanitizer { min: -10, max: 10 };
assert_eq!(
sanitizer.sanitize(Value::Int8(5.into())).unwrap(),
Value::Int8(5.into())
);
assert_eq!(
sanitizer.sanitize(Value::Int8((-100i8).into())).unwrap(),
Value::Int8((-10i8).into())
);
assert_eq!(
sanitizer.sanitize(Value::Int8(100.into())).unwrap(),
Value::Int8(10.into())
);
}
#[test]
fn test_clamp_sanitizer_i16() {
let sanitizer = ClampSanitizer { min: -10, max: 10 };
assert_eq!(
sanitizer.sanitize(Value::Int16(5.into())).unwrap(),
Value::Int16(5.into())
);
assert_eq!(
sanitizer.sanitize(Value::Int16((-1000i16).into())).unwrap(),
Value::Int16((-10i16).into())
);
assert_eq!(
sanitizer.sanitize(Value::Int16(1000.into())).unwrap(),
Value::Int16(10.into())
);
}
#[test]
fn test_clamp_sanitizer_saturates_bounds_outside_type_range() {
let wide = ClampSanitizer {
min: -1000,
max: 1000,
};
assert_eq!(
wide.sanitize(Value::Int8(i8::MIN.into())).unwrap(),
Value::Int8(i8::MIN.into())
);
assert_eq!(
wide.sanitize(Value::Int8(i8::MAX.into())).unwrap(),
Value::Int8(i8::MAX.into())
);
let above = ClampSanitizer {
min: 1_000,
max: 2_000,
};
assert_eq!(
above.sanitize(Value::Int8(0.into())).unwrap(),
Value::Int8(i8::MAX.into())
);
let below = ClampSanitizer {
min: -100_000,
max: -50_000,
};
assert_eq!(
below.sanitize(Value::Int16(0.into())).unwrap(),
Value::Int16(i16::MIN.into())
);
let above_i32 = ClampSanitizer {
min: i64::from(i32::MAX) + 1,
max: i64::MAX,
};
assert_eq!(
above_i32.sanitize(Value::Int32(0.into())).unwrap(),
Value::Int32(i32::MAX.into())
);
}
#[test]
fn test_clamp_unsigned_sanitizer_u8() {
let sanitizer = ClampUnsignedSanitizer { min: 10, max: 100 };
assert_eq!(
sanitizer.sanitize(Value::Uint8(50.into())).unwrap(),
Value::Uint8(50.into())
);
assert_eq!(
sanitizer.sanitize(Value::Uint8(3.into())).unwrap(),
Value::Uint8(10.into())
);
assert_eq!(
sanitizer.sanitize(Value::Uint8(200.into())).unwrap(),
Value::Uint8(100.into())
);
}
#[test]
fn test_clamp_unsigned_sanitizer_u16() {
let sanitizer = ClampUnsignedSanitizer { min: 10, max: 100 };
assert_eq!(
sanitizer.sanitize(Value::Uint16(50.into())).unwrap(),
Value::Uint16(50.into())
);
assert_eq!(
sanitizer.sanitize(Value::Uint16(3.into())).unwrap(),
Value::Uint16(10.into())
);
assert_eq!(
sanitizer.sanitize(Value::Uint16(1000.into())).unwrap(),
Value::Uint16(100.into())
);
}
#[test]
fn test_clamp_unsigned_sanitizer_raises_values_below_min() {
let sanitizer = ClampUnsignedSanitizer { min: 10, max: 100 };
assert_eq!(
sanitizer.sanitize(Value::Uint32(3.into())).unwrap(),
Value::Uint32(10.into())
);
assert_eq!(
sanitizer.sanitize(Value::Uint64(3.into())).unwrap(),
Value::Uint64(10.into())
);
}
#[test]
fn test_clamp_unsigned_sanitizer_saturates_bounds_outside_type_range() {
let wide = ClampUnsignedSanitizer { min: 0, max: 1000 };
assert_eq!(
wide.sanitize(Value::Uint8(u8::MAX.into())).unwrap(),
Value::Uint8(u8::MAX.into())
);
let above = ClampUnsignedSanitizer {
min: 1_000,
max: 2_000,
};
assert_eq!(
above.sanitize(Value::Uint8(0.into())).unwrap(),
Value::Uint8(u8::MAX.into())
);
let above_u16 = ClampUnsignedSanitizer {
min: 100_000,
max: 200_000,
};
assert_eq!(
above_u16.sanitize(Value::Uint16(0.into())).unwrap(),
Value::Uint16(u16::MAX.into())
);
let above_u32 = ClampUnsignedSanitizer {
min: u64::from(u32::MAX) + 1,
max: u64::MAX,
};
assert_eq!(
above_u32.sanitize(Value::Uint32(0.into())).unwrap(),
Value::Uint32(u32::MAX.into())
);
}
}