use crate::binary::VAR_HEADER_SIZE;
use crate::var::NUMERIC_MAX_PRECISION;
use std::fmt;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Typmod(i32);
impl Typmod {
#[inline]
pub unsafe fn new_unchecked(precision: i32, scale: i32) -> Self {
debug_assert!(precision >= 1 && precision <= NUMERIC_MAX_PRECISION);
debug_assert!(scale >= 0 && scale <= precision);
Typmod(((precision << 16) | scale) + VAR_HEADER_SIZE)
}
#[inline]
pub unsafe fn with_precision_unchecked(precision: i32) -> Self {
debug_assert!(precision >= 1 && precision <= NUMERIC_MAX_PRECISION);
Typmod((precision << 16) + VAR_HEADER_SIZE)
}
#[inline]
pub fn new(precision: i32, scale: i32) -> Option<Self> {
if precision < 1 || precision > NUMERIC_MAX_PRECISION || scale < 0 || scale > precision {
None
} else {
Some(unsafe { Self::new_unchecked(precision, scale) })
}
}
#[inline]
pub fn with_precision(precision: i32) -> Option<Self> {
if precision < 1 || precision > NUMERIC_MAX_PRECISION {
None
} else {
Some(unsafe { Self::with_precision_unchecked(precision) })
}
}
#[inline]
pub unsafe fn from_value_unchecked(value: i32) -> Self {
Typmod(value)
}
#[inline]
pub const fn value(self) -> i32 {
self.0
}
#[inline]
pub const fn extract(self) -> (i32, i32) {
let t = self.0 - VAR_HEADER_SIZE;
((t >> 16) & 0xFFFF, t & 0xFFFF)
}
}
impl Default for Typmod {
#[inline]
fn default() -> Self {
Typmod(-1)
}
}
impl fmt::Display for Typmod {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let (precision, scale) = self.extract();
write!(f, "({}, {})", precision, scale)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn typmod() {
let t = Typmod::new(10, 5).unwrap();
assert_eq!(t.value(), 655369);
assert_eq!(
unsafe { Typmod::from_value_unchecked(655369) }.extract(),
(10, 5)
);
assert_eq!(t.extract(), (10, 5));
assert_eq!(t.to_string(), "(10, 5)");
let t = Typmod::with_precision(10).unwrap();
assert_eq!(t.value(), 655364);
assert_eq!(
unsafe { Typmod::from_value_unchecked(655364) }.extract(),
(10, 0)
);
assert_eq!(t.extract(), (10, 0));
assert_eq!(t.to_string(), "(10, 0)");
assert_eq!(Typmod::new(0, 0), None);
assert_eq!(Typmod::new(NUMERIC_MAX_PRECISION + 1, 0), None);
assert_eq!(Typmod::new(10, -1), None);
assert_eq!(Typmod::new(10, 11), None);
assert_eq!(Typmod::with_precision(0), None);
assert_eq!(Typmod::with_precision(NUMERIC_MAX_PRECISION + 1), None);
}
}