positive/
positive.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 25/12/25
5******************************************************************************/
6
7//! Core implementation of the Positive type.
8
9use crate::constants::EPSILON;
10use crate::error::PositiveError;
11use approx::{AbsDiffEq, RelativeEq};
12use num_traits::{FromPrimitive, Pow, ToPrimitive};
13use rust_decimal::{Decimal, MathematicalOps};
14use rust_decimal_macros::dec;
15use serde::de::Visitor;
16use serde::{Deserialize, Deserializer, Serialize, Serializer};
17use std::cmp::{Ordering, PartialEq};
18use std::fmt;
19use std::fmt::Display;
20use std::iter::Sum;
21use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
22use std::str::FromStr;
23
24/// A wrapper type that represents a guaranteed positive decimal value.
25///
26/// This type encapsulates a `Decimal` value and ensures through its API that
27/// the contained value is always positive (greater than or equal to zero).
28#[derive(PartialEq, Clone, Copy, Hash)]
29#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
30pub struct Positive(pub Decimal);
31
32/// Determines if the given type parameter `T` is the `Positive` type.
33#[must_use]
34pub fn is_positive<T: 'static>() -> bool {
35    std::any::TypeId::of::<T>() == std::any::TypeId::of::<Positive>()
36}
37
38impl Positive {
39    // Re-export constants from the constants module for backward compatibility
40    /// A zero value represented as a `Positive` value.
41    pub const ZERO: Positive = crate::constants::ZERO;
42    /// A value of one represented as a `Positive` value.
43    pub const ONE: Positive = crate::constants::ONE;
44    /// A value of two represented as a `Positive` value.
45    pub const TWO: Positive = crate::constants::TWO;
46    /// A value of three represented as a `Positive` value.
47    pub const THREE: Positive = crate::constants::THREE;
48    /// A value of four represented as a `Positive` value.
49    pub const FOUR: Positive = crate::constants::FOUR;
50    /// A value of five represented as a `Positive` value.
51    pub const FIVE: Positive = crate::constants::FIVE;
52    /// A value of six represented as a `Positive` value.
53    pub const SIX: Positive = crate::constants::SIX;
54    /// A value of seven represented as a `Positive` value.
55    pub const SEVEN: Positive = crate::constants::SEVEN;
56    /// A value of eight represented as a `Positive` value.
57    pub const EIGHT: Positive = crate::constants::EIGHT;
58    /// A value of nine represented as a `Positive` value.
59    pub const NINE: Positive = crate::constants::NINE;
60    /// A value of ten represented as a `Positive` value.
61    pub const TEN: Positive = crate::constants::TEN;
62    /// A value of fifteen represented as a `Positive` value.
63    pub const FIFTEEN: Positive = crate::constants::FIFTEEN;
64    /// A value of twenty represented as a `Positive` value.
65    pub const TWENTY: Positive = crate::constants::TWENTY;
66    /// A value of twenty-five represented as a `Positive` value.
67    pub const TWENTY_FIVE: Positive = crate::constants::TWENTY_FIVE;
68    /// A value of thirty represented as a `Positive` value.
69    pub const THIRTY: Positive = crate::constants::THIRTY;
70    /// A value of thirty-five represented as a `Positive` value.
71    pub const THIRTY_FIVE: Positive = crate::constants::THIRTY_FIVE;
72    /// A value of forty represented as a `Positive` value.
73    pub const FORTY: Positive = crate::constants::FORTY;
74    /// A value of forty-five represented as a `Positive` value.
75    pub const FORTY_FIVE: Positive = crate::constants::FORTY_FIVE;
76    /// A value of fifty represented as a `Positive` value.
77    pub const FIFTY: Positive = crate::constants::FIFTY;
78    /// A value of fifty-five represented as a `Positive` value.
79    pub const FIFTY_FIVE: Positive = crate::constants::FIFTY_FIVE;
80    /// A value of sixty represented as a `Positive` value.
81    pub const SIXTY: Positive = crate::constants::SIXTY;
82    /// A value of sixty-five represented as a `Positive` value.
83    pub const SIXTY_FIVE: Positive = crate::constants::SIXTY_FIVE;
84    /// A value of seventy represented as a `Positive` value.
85    pub const SEVENTY: Positive = crate::constants::SEVENTY;
86    /// A value of seventy-five represented as a `Positive` value.
87    pub const SEVENTY_FIVE: Positive = crate::constants::SEVENTY_FIVE;
88    /// A value of eighty represented as a `Positive` value.
89    pub const EIGHTY: Positive = crate::constants::EIGHTY;
90    /// A value of eighty-five represented as a `Positive` value.
91    pub const EIGHTY_FIVE: Positive = crate::constants::EIGHTY_FIVE;
92    /// A value of ninety represented as a `Positive` value.
93    pub const NINETY: Positive = crate::constants::NINETY;
94    /// A value of ninety-five represented as a `Positive` value.
95    pub const NINETY_FIVE: Positive = crate::constants::NINETY_FIVE;
96    /// A value of one hundred represented as a `Positive` value.
97    pub const HUNDRED: Positive = crate::constants::HUNDRED;
98    /// A value of two hundred represented as a `Positive` value.
99    pub const TWO_HUNDRED: Positive = crate::constants::TWO_HUNDRED;
100    /// A value of three hundred represented as a `Positive` value.
101    pub const THREE_HUNDRED: Positive = crate::constants::THREE_HUNDRED;
102    /// A value of four hundred represented as a `Positive` value.
103    pub const FOUR_HUNDRED: Positive = crate::constants::FOUR_HUNDRED;
104    /// A value of five hundred represented as a `Positive` value.
105    pub const FIVE_HUNDRED: Positive = crate::constants::FIVE_HUNDRED;
106    /// A value of six hundred represented as a `Positive` value.
107    pub const SIX_HUNDRED: Positive = crate::constants::SIX_HUNDRED;
108    /// A value of seven hundred represented as a `Positive` value.
109    pub const SEVEN_HUNDRED: Positive = crate::constants::SEVEN_HUNDRED;
110    /// A value of eight hundred represented as a `Positive` value.
111    pub const EIGHT_HUNDRED: Positive = crate::constants::EIGHT_HUNDRED;
112    /// A value of nine hundred represented as a `Positive` value.
113    pub const NINE_HUNDRED: Positive = crate::constants::NINE_HUNDRED;
114    /// A value of one thousand represented as a `Positive` value.
115    pub const THOUSAND: Positive = crate::constants::THOUSAND;
116    /// A value of two thousand represented as a `Positive` value.
117    pub const TWO_THOUSAND: Positive = crate::constants::TWO_THOUSAND;
118    /// A value of three thousand represented as a `Positive` value.
119    pub const THREE_THOUSAND: Positive = crate::constants::THREE_THOUSAND;
120    /// A value of four thousand represented as a `Positive` value.
121    pub const FOUR_THOUSAND: Positive = crate::constants::FOUR_THOUSAND;
122    /// A value of five thousand represented as a `Positive` value.
123    pub const FIVE_THOUSAND: Positive = crate::constants::FIVE_THOUSAND;
124    /// A value of six thousand represented as a `Positive` value.
125    pub const SIX_THOUSAND: Positive = crate::constants::SIX_THOUSAND;
126    /// A value of seven thousand represented as a `Positive` value.
127    pub const SEVEN_THOUSAND: Positive = crate::constants::SEVEN_THOUSAND;
128    /// A value of eight thousand represented as a `Positive` value.
129    pub const EIGHT_THOUSAND: Positive = crate::constants::EIGHT_THOUSAND;
130    /// A value of nine thousand represented as a `Positive` value.
131    pub const NINE_THOUSAND: Positive = crate::constants::NINE_THOUSAND;
132    /// A value of ten thousand represented as a `Positive` value.
133    pub const TEN_THOUSAND: Positive = crate::constants::TEN_THOUSAND;
134    /// The mathematical constant π (pi) represented as a `Positive` value.
135    pub const PI: Positive = crate::constants::PI;
136    /// The mathematical constant e (Euler's number) represented as a `Positive` value.
137    pub const E: Positive = crate::constants::E;
138    /// Represents the maximum positive value possible (effectively infinity).
139    pub const INFINITY: Positive = crate::constants::INFINITY;
140
141    /// Creates a new `Positive` value from a 64-bit floating-point number.
142    pub fn new(value: f64) -> Result<Self, PositiveError> {
143        let dec = Decimal::from_f64(value);
144        match dec {
145            Some(value) if value >= Decimal::ZERO => Ok(Positive(value)),
146            Some(value) => Err(PositiveError::OutOfBounds {
147                value: value.to_f64().unwrap_or(0.0),
148                min: 0.0,
149                max: f64::MAX,
150            }),
151            None => Err(PositiveError::ConversionError {
152                from_type: "f64".to_string(),
153                to_type: "Positive".to_string(),
154                reason: "failed to parse Decimal".to_string(),
155            }),
156        }
157    }
158
159    /// Creates a new `Positive` value directly from a `Decimal`.
160    pub fn new_decimal(value: Decimal) -> Result<Self, PositiveError> {
161        if value >= Decimal::ZERO {
162            Ok(Positive(value))
163        } else {
164            Err(PositiveError::OutOfBounds {
165                value: value.to_f64().unwrap_or(0.0),
166                min: 0.0,
167                max: f64::INFINITY,
168            })
169        }
170    }
171
172    /// Returns the inner `Decimal` value.
173    #[must_use]
174    pub fn value(&self) -> Decimal {
175        self.0
176    }
177
178    /// Returns the inner `Decimal` value (alias for `value()`).
179    #[must_use]
180    pub fn to_dec(&self) -> Decimal {
181        self.0
182    }
183
184    /// Returns the inner `Decimal` ref.
185    #[must_use]
186    pub fn to_dec_ref(&self) -> &Decimal {
187        &self.0
188    }
189
190    /// Converts the value to a 64-bit floating-point number.
191    ///
192    /// # Panics
193    ///
194    /// This method will panic if the conversion fails. Use `to_f64_checked()`
195    /// or `to_f64_lossy()` for non-panicking alternatives.
196    #[must_use]
197    pub fn to_f64(&self) -> f64 {
198        self.0
199            .to_f64()
200            .expect("Decimal to f64 conversion failed - value out of range")
201    }
202
203    /// Converts the value to f64, returning None if conversion fails.
204    #[must_use]
205    pub fn to_f64_checked(&self) -> Option<f64> {
206        self.0.to_f64()
207    }
208
209    /// Converts the value to f64 with lossy conversion (returns 0.0 on failure).
210    #[must_use]
211    pub fn to_f64_lossy(&self) -> f64 {
212        self.0.to_f64().unwrap_or(0.0)
213    }
214
215    /// Converts the value to a 64-bit signed integer.
216    ///
217    /// # Panics
218    ///
219    /// This method will panic if the conversion fails. Use `to_i64_checked()`
220    /// for a non-panicking alternative.
221    #[must_use]
222    pub fn to_i64(&self) -> i64 {
223        self.0
224            .to_i64()
225            .expect("Decimal to i64 conversion failed - value out of range")
226    }
227
228    /// Converts the value to i64, returning None if conversion fails.
229    #[must_use]
230    pub fn to_i64_checked(&self) -> Option<i64> {
231        self.0.to_i64()
232    }
233
234    /// Converts the inner value to a `u64`.
235    ///
236    /// # Panics
237    ///
238    /// This method will panic if the conversion fails. Use `to_u64_checked()`
239    /// for a non-panicking alternative.
240    #[must_use]
241    pub fn to_u64(&self) -> u64 {
242        self.0
243            .to_u64()
244            .expect("Decimal to u64 conversion failed - value out of range")
245    }
246
247    /// Converts the value to u64, returning None if conversion fails.
248    #[must_use]
249    pub fn to_u64_checked(&self) -> Option<u64> {
250        self.0.to_u64()
251    }
252
253    /// Converts the value to a usize.
254    ///
255    /// # Panics
256    ///
257    /// This method will panic if the conversion fails. Use `to_usize_checked()`
258    /// for a non-panicking alternative.
259    #[must_use]
260    pub fn to_usize(&self) -> usize {
261        self.0
262            .to_usize()
263            .expect("Decimal to usize conversion failed - value out of range")
264    }
265
266    /// Converts the value to usize, returning None if conversion fails.
267    #[must_use]
268    pub fn to_usize_checked(&self) -> Option<usize> {
269        self.0.to_usize()
270    }
271
272    /// Returns the maximum of two `Positive` values.
273    #[must_use]
274    pub fn max(self, other: Positive) -> Positive {
275        if self.0 > other.0 { self } else { other }
276    }
277
278    /// Returns the minimum of two `Positive` values.
279    #[must_use]
280    pub fn min(self, other: Positive) -> Positive {
281        if self.0 < other.0 { self } else { other }
282    }
283
284    /// Rounds the value down to the nearest integer.
285    #[must_use]
286    pub fn floor(&self) -> Positive {
287        Positive(self.0.floor())
288    }
289
290    /// Raises this value to an integer power.
291    #[must_use]
292    pub fn powi(&self, n: i64) -> Positive {
293        Positive(self.0.powi(n))
294    }
295
296    /// Computes the result of raising the current value to the power of the given exponent.
297    #[must_use]
298    pub fn pow(&self, n: Positive) -> Positive {
299        Positive(self.0.pow(n.to_dec()))
300    }
301
302    /// Raises the current value to the power of `n` using unsigned integer exponentiation.
303    #[must_use]
304    pub fn powu(&self, n: u64) -> Positive {
305        Positive(self.0.powu(n))
306    }
307
308    /// Raises this value to a decimal power.
309    #[must_use]
310    pub fn powd(&self, p0: Decimal) -> Positive {
311        Positive(self.0.powd(p0))
312    }
313
314    /// Rounds the value to the nearest integer.
315    #[must_use]
316    pub fn round(&self) -> Positive {
317        Positive(self.0.round())
318    }
319
320    /// Rounds the current value to a "nice" number, based on its magnitude.
321    #[must_use]
322    pub fn round_to_nice_number(&self) -> Positive {
323        let magnitude = self.log10().floor();
324        let ten_pow = Positive::TEN.pow(magnitude);
325        let normalized = self / &ten_pow;
326        let nice_number = if normalized < dec!(1.5) {
327            Positive::ONE
328        } else if normalized < pos_or_panic!(3.0) {
329            Positive::TWO
330        } else if normalized < pos_or_panic!(7.0) {
331            pos_or_panic!(5.0)
332        } else {
333            Positive::TEN
334        };
335        nice_number * pos_or_panic!(10.0).powu(magnitude.to_u64())
336    }
337
338    /// Calculates the square root of the value.
339    ///
340    /// # Panics
341    ///
342    /// This method will panic if the square root calculation fails.
343    /// Use `sqrt_checked()` for a non-panicking alternative.
344    #[must_use]
345    pub fn sqrt(&self) -> Positive {
346        Positive(self.0.sqrt().expect("Square root calculation failed"))
347    }
348
349    /// Calculates the square root, returning an error if it fails.
350    pub fn sqrt_checked(&self) -> Result<Positive, PositiveError> {
351        self.0.sqrt().map(Positive).ok_or_else(|| {
352            PositiveError::arithmetic_error("sqrt", "square root calculation failed")
353        })
354    }
355
356    /// Calculates the natural logarithm of the value.
357    #[must_use]
358    pub fn ln(&self) -> Positive {
359        Positive(self.0.ln())
360    }
361
362    /// Rounds the value to a specified number of decimal places.
363    #[must_use]
364    pub fn round_to(&self, decimal_places: u32) -> Positive {
365        Positive(self.0.round_dp(decimal_places))
366    }
367
368    /// Formats the value with a fixed number of decimal places.
369    #[must_use]
370    pub fn format_fixed_places(&self, decimal_places: u32) -> String {
371        let rounded = self.round_to(decimal_places).to_f64();
372        format!("{:.1$}", rounded, decimal_places as usize)
373    }
374
375    /// Calculates the exponential function e^x for this value.
376    #[must_use]
377    pub fn exp(&self) -> Positive {
378        Positive(self.0.exp())
379    }
380
381    /// Clamps the value between a minimum and maximum.
382    #[must_use]
383    pub fn clamp(&self, min: Positive, max: Positive) -> Positive {
384        if self < &min {
385            min
386        } else if self > &max {
387            max
388        } else {
389            *self
390        }
391    }
392
393    /// Checks if the value is exactly zero.
394    #[must_use]
395    pub fn is_zero(&self) -> bool {
396        self.0.is_zero()
397    }
398
399    /// Returns the smallest integer greater than or equal to the value.
400    #[must_use]
401    pub fn ceiling(&self) -> Positive {
402        Positive(self.to_dec().ceil())
403    }
404
405    /// Computes the base-10 logarithm of the value.
406    #[must_use]
407    pub fn log10(&self) -> Positive {
408        Positive(self.0.log10())
409    }
410
411    /// Subtracts a decimal value, returning zero if the result would be negative.
412    #[must_use]
413    pub fn sub_or_zero(&self, other: &Decimal) -> Positive {
414        if &self.0 > other {
415            Positive(self.0 - other)
416        } else {
417            Positive(Decimal::ZERO)
418        }
419    }
420
421    /// Subtracts a decimal value, returning None if the result would be negative.
422    #[must_use]
423    pub fn sub_or_none(&self, other: &Decimal) -> Option<Positive> {
424        if &self.0 >= other {
425            Some(Positive(self.0 - other))
426        } else {
427            None
428        }
429    }
430
431    /// Checked subtraction that returns Result instead of panicking.
432    pub fn checked_sub(&self, rhs: &Self) -> Result<Self, PositiveError> {
433        Positive::new_decimal(self.0 - rhs.0)
434    }
435
436    /// Saturating subtraction that returns ZERO instead of negative.
437    #[must_use]
438    pub fn saturating_sub(&self, rhs: &Self) -> Self {
439        if self.0 > rhs.0 {
440            Positive(self.0 - rhs.0)
441        } else {
442            Positive::ZERO
443        }
444    }
445
446    /// Checked division that returns Result instead of panicking.
447    pub fn checked_div(&self, rhs: &Self) -> Result<Self, PositiveError> {
448        if rhs.is_zero() {
449            Err(PositiveError::arithmetic_error(
450                "division",
451                "division by zero",
452            ))
453        } else {
454            Ok(Positive(self.0 / rhs.0))
455        }
456    }
457
458    /// Checks whether the value is a multiple of another f64 value.
459    #[must_use]
460    pub fn is_multiple(&self, other: f64) -> bool {
461        let value = self.to_f64();
462        if !value.is_finite() {
463            return false;
464        }
465        let remainder = value % other;
466        remainder.abs() < f64::EPSILON || (other - remainder.abs()).abs() < f64::EPSILON
467    }
468
469    /// Checks whether the value is a multiple of another Positive value.
470    #[must_use]
471    pub fn is_multiple_of(&self, other: &Positive) -> bool {
472        if other.is_zero() {
473            return false;
474        }
475        let remainder = self.0 % other.0;
476        remainder.abs() < EPSILON
477    }
478
479    /// Creates a new `Positive` value without checking if the value is non-negative.
480    ///
481    /// # Safety
482    ///
483    /// The caller must ensure that `value >= 0`. Using this with a negative value
484    /// will violate the invariant of the `Positive` type and may cause undefined
485    /// behavior in code that relies on the positivity guarantee.
486    ///
487    /// # Example
488    ///
489    /// ```rust
490    /// use positive::Positive;
491    /// use rust_decimal_macros::dec;
492    ///
493    /// // SAFETY: We know 5.0 is positive
494    /// let value = unsafe { Positive::new_unchecked(dec!(5.0)) };
495    /// assert_eq!(value.to_f64(), 5.0);
496    /// ```
497    #[must_use]
498    pub const unsafe fn new_unchecked(value: Decimal) -> Self {
499        Positive(value)
500    }
501}
502
503impl From<Positive> for Decimal {
504    fn from(value: Positive) -> Self {
505        value.0
506    }
507}
508
509impl PartialEq<&Positive> for Positive {
510    fn eq(&self, other: &&Positive) -> bool {
511        self == *other
512    }
513}
514
515impl From<Positive> for u64 {
516    fn from(pos_u64: Positive) -> Self {
517        pos_u64.0.to_u64().unwrap_or(0)
518    }
519}
520
521impl From<&Positive> for f64 {
522    fn from(value: &Positive) -> Self {
523        value.0.to_f64().unwrap_or(0.0)
524    }
525}
526
527impl From<Positive> for f64 {
528    fn from(value: Positive) -> Self {
529        value.0.to_f64().unwrap_or(0.0)
530    }
531}
532
533impl From<Positive> for usize {
534    fn from(value: Positive) -> Self {
535        value.0.to_f64().unwrap_or(0.0) as usize
536    }
537}
538
539impl PartialEq<&Positive> for f64 {
540    fn eq(&self, other: &&Positive) -> bool {
541        self == &other.0.to_f64().unwrap_or(0.0)
542    }
543}
544
545impl PartialOrd<&Positive> for f64 {
546    fn partial_cmp(&self, other: &&Positive) -> Option<Ordering> {
547        self.partial_cmp(&other.0.to_f64().unwrap_or(0.0))
548    }
549}
550
551impl PartialEq<Positive> for f64 {
552    fn eq(&self, other: &Positive) -> bool {
553        self == &other.0.to_f64().unwrap_or(0.0)
554    }
555}
556
557impl PartialOrd<Positive> for f64 {
558    fn partial_cmp(&self, other: &Positive) -> Option<Ordering> {
559        self.partial_cmp(&other.0.to_f64().unwrap_or(0.0))
560    }
561}
562
563impl Mul<Positive> for f64 {
564    type Output = f64;
565    fn mul(self, rhs: Positive) -> Self::Output {
566        self * rhs.to_f64()
567    }
568}
569
570impl Div<Positive> for f64 {
571    type Output = f64;
572    fn div(self, rhs: Positive) -> Self::Output {
573        self / rhs.to_f64()
574    }
575}
576
577impl Sub<Positive> for f64 {
578    type Output = f64;
579    fn sub(self, rhs: Positive) -> Self::Output {
580        self - rhs.to_f64()
581    }
582}
583
584impl Add<Positive> for f64 {
585    type Output = f64;
586    fn add(self, rhs: Positive) -> Self::Output {
587        self + rhs.to_f64()
588    }
589}
590
591impl FromStr for Positive {
592    type Err = String;
593    fn from_str(s: &str) -> Result<Self, Self::Err> {
594        match s.parse::<Decimal>() {
595            Ok(value) if value >= Decimal::ZERO => Ok(Positive(value)),
596            Ok(value) => Err(format!("Value must be positive, got {value}")),
597            Err(e) => Err(format!("Failed to parse as Decimal: {e}")),
598        }
599    }
600}
601
602impl TryFrom<f64> for Positive {
603    type Error = PositiveError;
604
605    /// Attempts to convert an f64 to a Positive value.
606    ///
607    /// # Errors
608    ///
609    /// Returns `PositiveError` if the value is negative, NaN, or cannot be converted to Decimal.
610    fn try_from(value: f64) -> Result<Self, Self::Error> {
611        Positive::new(value)
612    }
613}
614
615impl TryFrom<usize> for Positive {
616    type Error = PositiveError;
617
618    /// Attempts to convert a usize to a Positive value.
619    ///
620    /// # Errors
621    ///
622    /// Returns `PositiveError` if the value cannot be converted to Decimal.
623    fn try_from(value: usize) -> Result<Self, Self::Error> {
624        Positive::new(value as f64)
625    }
626}
627
628impl TryFrom<Decimal> for Positive {
629    type Error = PositiveError;
630
631    /// Attempts to convert a Decimal to a Positive value.
632    ///
633    /// # Errors
634    ///
635    /// Returns `PositiveError` if the value is negative.
636    fn try_from(value: Decimal) -> Result<Self, Self::Error> {
637        Positive::new_decimal(value)
638    }
639}
640
641impl TryFrom<&Decimal> for Positive {
642    type Error = PositiveError;
643
644    /// Attempts to convert a &Decimal to a Positive value.
645    ///
646    /// # Errors
647    ///
648    /// Returns `PositiveError` if the value is negative.
649    fn try_from(value: &Decimal) -> Result<Self, Self::Error> {
650        Positive::new_decimal(*value)
651    }
652}
653
654impl TryFrom<i64> for Positive {
655    type Error = PositiveError;
656
657    /// Attempts to convert an i64 to a Positive value.
658    ///
659    /// # Errors
660    ///
661    /// Returns `PositiveError` if the value is negative.
662    fn try_from(value: i64) -> Result<Self, Self::Error> {
663        Positive::new_decimal(Decimal::from(value))
664    }
665}
666
667impl TryFrom<u64> for Positive {
668    type Error = PositiveError;
669
670    /// Attempts to convert a u64 to a Positive value.
671    ///
672    /// # Errors
673    ///
674    /// This conversion is infallible for u64 since all values are non-negative.
675    fn try_from(value: u64) -> Result<Self, Self::Error> {
676        Positive::new_decimal(Decimal::from(value))
677    }
678}
679
680impl From<&Positive> for Positive {
681    fn from(value: &Positive) -> Self {
682        Positive(value.0)
683    }
684}
685
686impl Mul<f64> for Positive {
687    type Output = Positive;
688    fn mul(self, rhs: f64) -> Positive {
689        Positive::new(self.to_f64() * rhs).expect("Multiplication result must be positive")
690    }
691}
692
693impl Div<f64> for Positive {
694    type Output = Positive;
695    fn div(self, rhs: f64) -> Positive {
696        Positive::new(self.to_f64() / rhs).expect("Division result must be positive")
697    }
698}
699
700impl Div<f64> for &Positive {
701    type Output = Positive;
702    fn div(self, rhs: f64) -> Positive {
703        Positive::new(self.to_f64() / rhs).expect("Division result must be positive")
704    }
705}
706
707impl Sub<f64> for Positive {
708    type Output = Positive;
709    fn sub(self, rhs: f64) -> Self::Output {
710        Positive::new(self.to_f64() - rhs).expect("Subtraction result must be positive")
711    }
712}
713
714impl Add<f64> for Positive {
715    type Output = Positive;
716    fn add(self, rhs: f64) -> Self::Output {
717        Positive::new(self.to_f64() + rhs).expect("Addition result must be positive")
718    }
719}
720
721impl PartialOrd<f64> for Positive {
722    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
723        self.0.to_f64().unwrap_or(0.0).partial_cmp(other)
724    }
725}
726
727impl PartialEq<f64> for &Positive {
728    fn eq(&self, other: &f64) -> bool {
729        self.0.to_f64().unwrap_or(0.0) == *other
730    }
731}
732
733impl PartialOrd<f64> for &Positive {
734    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
735        self.0.to_f64().unwrap_or(0.0).partial_cmp(other)
736    }
737}
738
739impl PartialEq<f64> for Positive {
740    fn eq(&self, other: &f64) -> bool {
741        self.to_f64() == *other
742    }
743}
744
745impl Display for Positive {
746    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
747        if *self == Positive::INFINITY {
748            write!(f, "{}", f64::MAX)
749        } else if self.0.scale() == 0 {
750            match self.0.to_i64() {
751                Some(val) => write!(f, "{val}"),
752                None => write!(f, "{}", self.0),
753            }
754        } else if let Some(precision) = f.precision() {
755            write!(f, "{:.1$}", self.0, precision)
756        } else {
757            let s = self.0.to_string();
758            let trimmed = s.trim_end_matches('0').trim_end_matches('.');
759            write!(f, "{trimmed}")
760        }
761    }
762}
763
764impl fmt::Debug for Positive {
765    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
766        if *self == Positive::INFINITY {
767            write!(f, "{}", f64::MAX)
768        } else if self.0.scale() == 0 {
769            match self.0.to_i64() {
770                Some(val) => write!(f, "{val}"),
771                None => write!(f, "{}", self.0),
772            }
773        } else {
774            write!(f, "{}", self.0)
775        }
776    }
777}
778
779impl PartialEq<Decimal> for Positive {
780    fn eq(&self, other: &Decimal) -> bool {
781        (self.0 - *other).abs() <= EPSILON * Decimal::from(100)
782    }
783}
784
785impl Serialize for Positive {
786    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
787    where
788        S: Serializer,
789    {
790        if *self == Positive::INFINITY {
791            return serializer.serialize_f64(f64::MAX);
792        }
793        if self.0.scale() == 0 {
794            serializer.serialize_i64(
795                self.0
796                    .to_i64()
797                    .ok_or_else(|| serde::ser::Error::custom("Failed to convert to i64"))?,
798            )
799        } else {
800            serializer.serialize_f64(
801                self.0
802                    .to_f64()
803                    .ok_or_else(|| serde::ser::Error::custom("Failed to convert to f64"))?,
804            )
805        }
806    }
807}
808
809impl<'de> Deserialize<'de> for Positive {
810    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
811    where
812        D: Deserializer<'de>,
813    {
814        struct PositiveVisitor;
815
816        impl Visitor<'_> for PositiveVisitor {
817            type Value = Positive;
818
819            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
820                formatter.write_str("a positive number")
821            }
822
823            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
824            where
825                E: serde::de::Error,
826            {
827                Err(serde::de::Error::custom(format!(
828                    "Invalid string: '{value}'. Expected a positive number."
829                )))
830            }
831
832            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
833            where
834                E: serde::de::Error,
835            {
836                if value < 0 {
837                    Err(serde::de::Error::custom("Expected a non-negative integer"))
838                } else {
839                    Positive::new_decimal(Decimal::from(value)).map_err(serde::de::Error::custom)
840                }
841            }
842
843            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
844            where
845                E: serde::de::Error,
846            {
847                Positive::new_decimal(Decimal::from(value)).map_err(serde::de::Error::custom)
848            }
849
850            fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
851            where
852                E: serde::de::Error,
853            {
854                if value.is_infinite() && value.is_sign_positive() {
855                    return Ok(Positive::INFINITY);
856                }
857                if value == f64::MAX {
858                    return Ok(Positive::INFINITY);
859                }
860                let decimal = Decimal::from_f64(value)
861                    .ok_or_else(|| serde::de::Error::custom("Failed to convert f64 to Decimal"))?;
862                if value < 0.0 {
863                    Err(serde::de::Error::custom("Expected a non-negative float"))
864                } else {
865                    Positive::new_decimal(decimal).map_err(serde::de::Error::custom)
866                }
867            }
868        }
869
870        deserializer.deserialize_any(PositiveVisitor)
871    }
872}
873
874impl Add for Positive {
875    type Output = Positive;
876    fn add(self, other: Positive) -> Positive {
877        Positive(self.0 + other.0)
878    }
879}
880
881impl Sub for Positive {
882    type Output = Positive;
883    fn sub(self, rhs: Self) -> Self::Output {
884        let result = self.0 - rhs.0;
885        if result < Decimal::ZERO {
886            panic!("Resulting value must be positive");
887        } else {
888            Positive(result)
889        }
890    }
891}
892
893impl Div for Positive {
894    type Output = Positive;
895    fn div(self, other: Positive) -> Self::Output {
896        Positive(self.0 / other.0)
897    }
898}
899
900impl Div for &Positive {
901    type Output = Positive;
902    fn div(self, other: &Positive) -> Self::Output {
903        Positive(self.0 / other.0)
904    }
905}
906
907impl Add<Decimal> for Positive {
908    type Output = Positive;
909    fn add(self, rhs: Decimal) -> Positive {
910        Positive(self.0 + rhs)
911    }
912}
913
914impl Add<&Decimal> for Positive {
915    type Output = Positive;
916    fn add(self, rhs: &Decimal) -> Self::Output {
917        Positive::new_decimal(self.0 + rhs).expect("Addition result must be positive")
918    }
919}
920
921impl Sub<Decimal> for Positive {
922    type Output = Positive;
923    fn sub(self, rhs: Decimal) -> Positive {
924        Positive::new_decimal(self.0 - rhs).expect("Resulting value must be positive")
925    }
926}
927
928impl Sub<&Decimal> for Positive {
929    type Output = Positive;
930    fn sub(self, rhs: &Decimal) -> Self::Output {
931        Positive::new_decimal(self.0 - rhs).expect("Resulting value must be positive")
932    }
933}
934
935impl AddAssign for Positive {
936    fn add_assign(&mut self, other: Positive) {
937        self.0 += other.0;
938    }
939}
940
941impl AddAssign<Decimal> for Positive {
942    fn add_assign(&mut self, rhs: Decimal) {
943        self.0 += rhs;
944    }
945}
946
947impl MulAssign<Decimal> for Positive {
948    fn mul_assign(&mut self, rhs: Decimal) {
949        self.0 *= rhs;
950    }
951}
952
953impl Div<Decimal> for Positive {
954    type Output = Positive;
955    fn div(self, rhs: Decimal) -> Positive {
956        Positive(self.0 / rhs)
957    }
958}
959
960impl Div<&Decimal> for Positive {
961    type Output = Positive;
962    fn div(self, rhs: &Decimal) -> Self::Output {
963        Positive::new_decimal(self.0 / rhs).expect("Division result must be positive")
964    }
965}
966
967impl PartialOrd<Decimal> for Positive {
968    fn partial_cmp(&self, other: &Decimal) -> Option<Ordering> {
969        self.0.partial_cmp(other)
970    }
971}
972
973impl PartialOrd for Positive {
974    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
975        Some(self.cmp(other))
976    }
977
978    fn le(&self, other: &Self) -> bool {
979        self.0 <= other.0
980    }
981
982    fn ge(&self, other: &Self) -> bool {
983        self.0 >= other.0
984    }
985}
986
987impl Eq for Positive {}
988
989impl Ord for Positive {
990    fn cmp(&self, other: &Self) -> Ordering {
991        self.0.partial_cmp(&other.0).unwrap_or(Ordering::Equal)
992    }
993}
994
995impl Neg for Positive {
996    type Output = Self;
997    fn neg(self) -> Self::Output {
998        panic!("Cannot negate a Positive value!");
999    }
1000}
1001
1002impl Mul for Positive {
1003    type Output = Positive;
1004    fn mul(self, other: Positive) -> Positive {
1005        Positive(self.0 * other.0)
1006    }
1007}
1008
1009impl Mul<Decimal> for Positive {
1010    type Output = Positive;
1011    fn mul(self, rhs: Decimal) -> Positive {
1012        Positive(self.0 * rhs)
1013    }
1014}
1015
1016impl Mul<Positive> for Decimal {
1017    type Output = Decimal;
1018    fn mul(self, rhs: Positive) -> Decimal {
1019        self * rhs.0
1020    }
1021}
1022
1023impl Div<Positive> for Decimal {
1024    type Output = Decimal;
1025    fn div(self, rhs: Positive) -> Decimal {
1026        self / rhs.0
1027    }
1028}
1029
1030impl Sub<Positive> for Decimal {
1031    type Output = Decimal;
1032    fn sub(self, rhs: Positive) -> Decimal {
1033        self - rhs.0
1034    }
1035}
1036
1037impl Sub<&Positive> for Decimal {
1038    type Output = Decimal;
1039    fn sub(self, rhs: &Positive) -> Decimal {
1040        self - rhs.0
1041    }
1042}
1043
1044impl Add<Positive> for Decimal {
1045    type Output = Decimal;
1046    fn add(self, rhs: Positive) -> Decimal {
1047        self + rhs.0
1048    }
1049}
1050
1051impl Add<&Positive> for Decimal {
1052    type Output = Decimal;
1053    fn add(self, rhs: &Positive) -> Decimal {
1054        self + rhs.0
1055    }
1056}
1057
1058impl std::ops::AddAssign<Positive> for Decimal {
1059    fn add_assign(&mut self, rhs: Positive) {
1060        *self += rhs.0;
1061    }
1062}
1063
1064impl std::ops::AddAssign<&Positive> for Decimal {
1065    fn add_assign(&mut self, rhs: &Positive) {
1066        *self += rhs.0;
1067    }
1068}
1069
1070impl std::ops::MulAssign<Positive> for Decimal {
1071    fn mul_assign(&mut self, rhs: Positive) {
1072        *self *= rhs.0;
1073    }
1074}
1075
1076impl std::ops::MulAssign<&Positive> for Decimal {
1077    fn mul_assign(&mut self, rhs: &Positive) {
1078        *self *= rhs.0;
1079    }
1080}
1081
1082impl PartialEq<Positive> for Decimal {
1083    fn eq(&self, other: &Positive) -> bool {
1084        *self == other.0
1085    }
1086}
1087
1088impl From<&Positive> for Decimal {
1089    fn from(pos: &Positive) -> Self {
1090        pos.0
1091    }
1092}
1093
1094impl Default for Positive {
1095    fn default() -> Self {
1096        Positive::ZERO
1097    }
1098}
1099
1100impl AbsDiffEq for Positive {
1101    type Epsilon = Decimal;
1102
1103    fn default_epsilon() -> Self::Epsilon {
1104        EPSILON
1105    }
1106
1107    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
1108        (self.0 - other.0).abs() <= epsilon
1109    }
1110}
1111
1112impl RelativeEq for Positive {
1113    fn default_max_relative() -> Self::Epsilon {
1114        EPSILON * Decimal::from(100)
1115    }
1116
1117    fn relative_eq(
1118        &self,
1119        other: &Self,
1120        epsilon: Self::Epsilon,
1121        max_relative: Self::Epsilon,
1122    ) -> bool {
1123        let abs_diff = (self.0 - other.0).abs();
1124        let largest = self.0.abs().max(other.0.abs());
1125        abs_diff <= epsilon || abs_diff <= max_relative * largest
1126    }
1127}
1128
1129impl Sum for Positive {
1130    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
1131        let sum = iter.fold(Decimal::ZERO, |acc, x| acc + x.value());
1132        Positive::new_decimal(sum).unwrap_or(Positive::ZERO)
1133    }
1134}
1135
1136impl<'a> Sum<&'a Positive> for Positive {
1137    fn sum<I: Iterator<Item = &'a Positive>>(iter: I) -> Self {
1138        let sum = iter.fold(Decimal::ZERO, |acc, x| acc + x.value());
1139        Positive::new_decimal(sum).unwrap_or(Positive::ZERO)
1140    }
1141}