use super::*; #[derive(Debug, PartialEq, Clone, Copy)] pub struct FeeRate(f64); impl FromStr for FeeRate { type Err = Error; fn from_str(s: &str) -> Result { Self::try_from(f64::from_str(s)?) } } impl TryFrom for FeeRate { type Error = Error; fn try_from(rate: f64) -> Result { if rate.is_sign_negative() | rate.is_nan() | rate.is_infinite() { bail!("invalid fee rate: {rate}") } Ok(Self(rate)) } } impl FeeRate { pub(crate) fn fee(&self, vsize: usize) -> Amount { #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] Amount::from_sat((self.0 * vsize as f64).round() as u64) } } #[cfg(test)] mod tests { use super::*; #[test] fn parse() { assert_eq!("1.1".parse::().unwrap().0, 1.1); assert_eq!("11.19".parse::().unwrap().0, 11.19); assert_eq!("11.1111".parse::().unwrap().0, 11.1111); assert!("-4.2".parse::().is_err()); assert!(FeeRate::try_from(f64::INFINITY).is_err()); assert!(FeeRate::try_from(f64::NAN).is_err()); } #[test] fn fee() { assert_eq!( "2.5".parse::().unwrap().fee(100), Amount::from_sat(250) ); assert_eq!( "2.0".parse::().unwrap().fee(1024), Amount::from_sat(2048) ); assert_eq!( "1.1".parse::().unwrap().fee(100), Amount::from_sat(110) ); assert_eq!( "1.0".parse::().unwrap().fee(123456789), Amount::from_sat(123456789) ); } }