use core::fmt::{Debug, Display, Formatter};
use std::ffi::CStr;
use std::fmt;
use crate::numeric_support::convert::from_primitive_helper;
pub use crate::numeric_support::error::Error;
use crate::{direct_function_call, pg_sys, varsize, PgMemoryContexts};
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct Numeric<const P: u32, const S: u32>(pub(crate) AnyNumeric);
pub struct AnyNumeric {
pub(crate) inner: pg_sys::Numeric,
pub(crate) need_pfree: bool,
}
impl Clone for AnyNumeric {
fn clone(&self) -> Self {
unsafe {
let copy = PgMemoryContexts::CurrentMemoryContext
.copy_ptr_into(self.inner, varsize(self.inner.cast()));
AnyNumeric { inner: copy, need_pfree: true }
}
}
}
impl Drop for AnyNumeric {
fn drop(&mut self) {
if self.need_pfree {
unsafe {
pg_sys::pfree(self.inner.cast());
}
}
}
}
impl Display for AnyNumeric {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
let numeric_out = unsafe {
direct_function_call::<&CStr>(pg_sys::numeric_out, vec![self.as_datum()]).unwrap()
};
let s = numeric_out.to_str().expect("numeric_out is not a valid UTF8 string");
fmt.pad(s)
}
}
impl Debug for AnyNumeric {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
write!(fmt, "AnyNumeric(inner: {self}, need_pfree:{})", self.need_pfree)
}
}
impl<const P: u32, const S: u32> Display for Numeric<P, S> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(fmt, "{}", self.0)
}
}
#[inline(always)]
pub(crate) const fn make_typmod(precision: u32, scale: u32) -> i32 {
let (precision, scale) = (precision as i32, scale as i32);
match (precision, scale) {
(0, 0) => -1,
(p, s) => ((p << 16) | s) + pg_sys::VARHDRSZ as i32,
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum Sign {
Negative,
Positive,
Zero,
NaN,
}
impl AnyNumeric {
pub fn sign(&self) -> Sign {
if self.is_nan() {
Sign::NaN
} else {
let zero: AnyNumeric = 0.try_into().unwrap();
if self < &zero {
Sign::Negative
} else if self > &zero {
Sign::Positive
} else {
Sign::Zero
}
}
}
pub fn is_nan(&self) -> bool {
unsafe { pg_sys::numeric_is_nan(self.inner) }
}
pub fn abs(&self) -> Self {
unsafe { direct_function_call(pg_sys::numeric_abs, vec![self.as_datum()]).unwrap() }
}
pub fn log(&self, base: AnyNumeric) -> Self {
unsafe {
direct_function_call(pg_sys::numeric_log, vec![self.as_datum(), base.as_datum()])
.unwrap()
}
}
pub fn exp(x: &AnyNumeric) -> Self {
unsafe { direct_function_call(pg_sys::numeric_exp, vec![x.as_datum()]).unwrap() }
}
pub fn sqrt(&self) -> Self {
unsafe { direct_function_call(pg_sys::numeric_sqrt, vec![self.as_datum()]).unwrap() }
}
pub fn ceil(&self) -> Self {
unsafe { direct_function_call(pg_sys::numeric_ceil, vec![self.as_datum()]).unwrap() }
}
pub fn floor(&self) -> Self {
unsafe { direct_function_call(pg_sys::numeric_floor, vec![self.as_datum()]).unwrap() }
}
#[cfg(not(any(feature = "pg11", feature = "pg12")))]
pub fn gcd(&self, n: &AnyNumeric) -> AnyNumeric {
unsafe {
direct_function_call(pg_sys::numeric_gcd, vec![self.as_datum(), n.as_datum()]).unwrap()
}
}
pub fn normalize(&self) -> &str {
unsafe {
let s = pg_sys::numeric_normalize(self.inner.cast());
let cstr = CStr::from_ptr(s);
let normalized = cstr.to_str().unwrap();
normalized
}
}
#[inline]
pub fn rescale<const P: u32, const S: u32>(self) -> Result<Numeric<P, S>, Error> {
Numeric::try_from(self)
}
#[inline]
pub(crate) fn as_datum(&self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(self.inner))
}
#[inline]
pub(crate) fn copy(&self) -> AnyNumeric {
AnyNumeric { inner: self.inner, need_pfree: false }
}
}
impl<const P: u32, const S: u32> Numeric<P, S> {
#[inline]
pub fn as_anynumeric(&self) -> &AnyNumeric {
&self.0
}
#[inline]
pub fn rescale<const NEW_P: u32, const NEW_S: u32>(
self,
) -> Result<Numeric<NEW_P, NEW_S>, Error> {
from_primitive_helper::<_, NEW_P, NEW_S>(self, pg_sys::numeric)
}
}