Skip to main content

brk_types/
feerate.rs

1use std::{
2    cmp::Ordering,
3    ops::{Add, AddAssign, Div},
4};
5
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8use vecdb::{CheckedSub, Formattable, Pco};
9
10use super::{Sats, VSize};
11
12/// Fee rate in sats/vB
13#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Pco, JsonSchema)]
14pub struct FeeRate(f64);
15
16impl FeeRate {
17    pub const MIN: Self = Self(0.1);
18
19    pub fn new(fr: f64) -> Self {
20        Self(fr)
21    }
22}
23
24impl From<(Sats, VSize)> for FeeRate {
25    #[inline]
26    fn from((sats, vsize): (Sats, VSize)) -> Self {
27        if sats.is_zero() {
28            return Self(0.0);
29        }
30        let sats = u64::from(sats);
31        let vsize = u64::from(vsize);
32        if vsize == 0 {
33            return Self(f64::NAN);
34        }
35        Self((sats * 1000).div_ceil(vsize) as f64 / 1000.0)
36    }
37}
38
39impl From<f64> for FeeRate {
40    #[inline]
41    fn from(value: f64) -> Self {
42        Self(value)
43    }
44}
45impl From<FeeRate> for f64 {
46    #[inline]
47    fn from(value: FeeRate) -> Self {
48        value.0
49    }
50}
51
52impl Add for FeeRate {
53    type Output = Self;
54    fn add(self, rhs: Self) -> Self::Output {
55        Self(self.0 + rhs.0)
56    }
57}
58
59impl AddAssign for FeeRate {
60    fn add_assign(&mut self, rhs: Self) {
61        *self = *self + rhs
62    }
63}
64
65impl Div<usize> for FeeRate {
66    type Output = Self;
67    fn div(self, rhs: usize) -> Self::Output {
68        if rhs == 0 {
69            Self(f64::NAN)
70        } else {
71            Self(self.0 / rhs as f64)
72        }
73    }
74}
75
76impl From<usize> for FeeRate {
77    #[inline]
78    fn from(value: usize) -> Self {
79        Self(value as f64)
80    }
81}
82
83impl PartialEq for FeeRate {
84    fn eq(&self, other: &Self) -> bool {
85        match (self.0.is_nan(), other.0.is_nan()) {
86            (true, true) => true,
87            (true, false) => false,
88            (false, true) => false,
89            (false, false) => self.0 == other.0,
90        }
91    }
92}
93
94impl Eq for FeeRate {}
95
96#[allow(clippy::derive_ord_xor_partial_ord)]
97impl PartialOrd for FeeRate {
98    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
99        Some(self.cmp(other))
100    }
101}
102
103#[allow(clippy::derive_ord_xor_partial_ord)]
104impl Ord for FeeRate {
105    fn cmp(&self, other: &Self) -> Ordering {
106        match (self.0.is_nan(), other.0.is_nan()) {
107            (true, true) => Ordering::Equal,
108            (true, false) => Ordering::Less,
109            (false, true) => Ordering::Greater,
110            (false, false) => self.0.partial_cmp(&other.0).unwrap(),
111        }
112    }
113}
114
115impl CheckedSub for FeeRate {
116    #[inline]
117    fn checked_sub(self, rhs: Self) -> Option<Self> {
118        let result = self.0 - rhs.0;
119        if result.is_nan() {
120            None
121        } else {
122            Some(Self(result))
123        }
124    }
125}
126
127impl std::fmt::Display for FeeRate {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        let mut buf = ryu::Buffer::new();
130        let str = buf.format(self.0);
131        f.write_str(str)
132    }
133}
134
135impl Formattable for FeeRate {
136    #[inline(always)]
137    fn write_to(&self, buf: &mut Vec<u8>) {
138        if self.0.is_finite() {
139            let mut b = ryu::Buffer::new();
140            buf.extend_from_slice(b.format(self.0).as_bytes());
141        }
142    }
143
144    #[inline(always)]
145    fn fmt_json(&self, buf: &mut Vec<u8>) {
146        if self.0.is_finite() {
147            self.write_to(buf);
148        } else {
149            buf.extend_from_slice(b"null");
150        }
151    }
152}