use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use crate::{Decimal, one, serde::canonical_str, to_canonical_string, zero};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DecimalConstraintError {
type_name: &'static str,
expected: &'static str,
value: Decimal,
}
impl DecimalConstraintError {
#[cfg(not(feature = "bigdecimal"))]
const fn new(type_name: &'static str, expected: &'static str, value: &Decimal) -> Self {
Self {
type_name,
expected,
value: *value,
}
}
#[cfg(feature = "bigdecimal")]
fn new(type_name: &'static str, expected: &'static str, value: &Decimal) -> Self {
Self {
type_name,
expected,
value: value.clone(),
}
}
#[must_use]
pub const fn type_name(&self) -> &'static str {
self.type_name
}
#[must_use]
pub const fn expected(&self) -> &'static str {
self.expected
}
#[must_use]
pub const fn value(&self) -> &Decimal {
&self.value
}
}
impl fmt::Display for DecimalConstraintError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} expected {}, got {}",
self.type_name, self.expected, self.value
)
}
}
impl std::error::Error for DecimalConstraintError {}
fn is_non_negative(value: &Decimal) -> bool {
let zero = zero();
value >= &zero
}
fn is_positive(value: &Decimal) -> bool {
let zero = zero();
value > &zero
}
fn is_ratio(value: &Decimal) -> bool {
let zero = zero();
let one = one();
value >= &zero && value <= &one
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
#[cfg_attr(not(feature = "bigdecimal"), derive(Copy))]
pub struct NonNegativeDecimal(Decimal);
impl NonNegativeDecimal {
const EXPECTED: &'static str = "a decimal greater than or equal to 0";
pub fn new(value: Decimal) -> Result<Self, DecimalConstraintError> {
if is_non_negative(&value) {
Ok(Self(value))
} else {
Err(DecimalConstraintError::new(
"NonNegativeDecimal",
Self::EXPECTED,
&value,
))
}
}
#[must_use]
pub const fn as_decimal(&self) -> &Decimal {
&self.0
}
#[must_use]
#[cfg(not(feature = "bigdecimal"))]
pub const fn into_inner(self) -> Decimal {
self.0
}
#[must_use]
#[cfg(feature = "bigdecimal")]
pub fn into_inner(self) -> Decimal {
self.0
}
}
impl AsRef<Decimal> for NonNegativeDecimal {
fn as_ref(&self) -> &Decimal {
self.as_decimal()
}
}
impl TryFrom<Decimal> for NonNegativeDecimal {
type Error = DecimalConstraintError;
fn try_from(value: Decimal) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<NonNegativeDecimal> for Decimal {
fn from(value: NonNegativeDecimal) -> Self {
value.into_inner()
}
}
impl fmt::Display for NonNegativeDecimal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&to_canonical_string(&self.0))
}
}
impl Serialize for NonNegativeDecimal {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
canonical_str::serialize(&self.0, serializer)
}
}
impl<'de> Deserialize<'de> for NonNegativeDecimal {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = canonical_str::deserialize(deserializer)?;
Self::new(value).map_err(de::Error::custom)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
#[cfg_attr(not(feature = "bigdecimal"), derive(Copy))]
pub struct PositiveDecimal(Decimal);
impl PositiveDecimal {
const EXPECTED: &'static str = "a decimal greater than 0";
pub fn new(value: Decimal) -> Result<Self, DecimalConstraintError> {
if is_positive(&value) {
Ok(Self(value))
} else {
Err(DecimalConstraintError::new(
"PositiveDecimal",
Self::EXPECTED,
&value,
))
}
}
#[must_use]
pub const fn as_decimal(&self) -> &Decimal {
&self.0
}
#[must_use]
#[cfg(not(feature = "bigdecimal"))]
pub const fn into_inner(self) -> Decimal {
self.0
}
#[must_use]
#[cfg(feature = "bigdecimal")]
pub fn into_inner(self) -> Decimal {
self.0
}
}
impl AsRef<Decimal> for PositiveDecimal {
fn as_ref(&self) -> &Decimal {
self.as_decimal()
}
}
impl TryFrom<Decimal> for PositiveDecimal {
type Error = DecimalConstraintError;
fn try_from(value: Decimal) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<PositiveDecimal> for Decimal {
fn from(value: PositiveDecimal) -> Self {
value.into_inner()
}
}
impl From<PositiveDecimal> for NonNegativeDecimal {
fn from(value: PositiveDecimal) -> Self {
Self(value.into_inner())
}
}
impl fmt::Display for PositiveDecimal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&to_canonical_string(&self.0))
}
}
impl Serialize for PositiveDecimal {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
canonical_str::serialize(&self.0, serializer)
}
}
impl<'de> Deserialize<'de> for PositiveDecimal {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = canonical_str::deserialize(deserializer)?;
Self::new(value).map_err(de::Error::custom)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
#[cfg_attr(not(feature = "bigdecimal"), derive(Copy))]
pub struct Ratio(Decimal);
impl Ratio {
const EXPECTED: &'static str = "a decimal between 0 and 1 inclusive";
pub fn new(value: Decimal) -> Result<Self, DecimalConstraintError> {
if is_ratio(&value) {
Ok(Self(value))
} else {
Err(DecimalConstraintError::new("Ratio", Self::EXPECTED, &value))
}
}
#[must_use]
pub const fn as_decimal(&self) -> &Decimal {
&self.0
}
#[must_use]
#[cfg(not(feature = "bigdecimal"))]
pub const fn into_inner(self) -> Decimal {
self.0
}
#[must_use]
#[cfg(feature = "bigdecimal")]
pub fn into_inner(self) -> Decimal {
self.0
}
}
impl AsRef<Decimal> for Ratio {
fn as_ref(&self) -> &Decimal {
self.as_decimal()
}
}
impl TryFrom<Decimal> for Ratio {
type Error = DecimalConstraintError;
fn try_from(value: Decimal) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<Ratio> for Decimal {
fn from(value: Ratio) -> Self {
value.into_inner()
}
}
impl From<Ratio> for NonNegativeDecimal {
fn from(value: Ratio) -> Self {
Self(value.into_inner())
}
}
impl fmt::Display for Ratio {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&to_canonical_string(&self.0))
}
}
impl Serialize for Ratio {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
canonical_str::serialize(&self.0, serializer)
}
}
impl<'de> Deserialize<'de> for Ratio {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = canonical_str::deserialize(deserializer)?;
Self::new(value).map_err(de::Error::custom)
}
}