use brk_types::{Bitcoin, Cents, CentsSigned, Dollars, Sats, SatsFract, SatsSigned, StoredF32};
use vecdb::{BinaryTransform, UnaryTransform, unlikely};
pub struct SatsToBitcoin;
impl UnaryTransform<Sats, Bitcoin> for SatsToBitcoin {
#[inline(always)]
fn apply(sats: Sats) -> Bitcoin {
Bitcoin::from(sats)
}
}
pub struct SatsSignedToBitcoin;
impl UnaryTransform<SatsSigned, Bitcoin> for SatsSignedToBitcoin {
#[inline(always)]
fn apply(sats: SatsSigned) -> Bitcoin {
Bitcoin::from(sats)
}
}
pub struct AvgSatsToBtc;
impl UnaryTransform<StoredF32, Bitcoin> for AvgSatsToBtc {
#[inline(always)]
fn apply(sats: StoredF32) -> Bitcoin {
Bitcoin::from(f64::from(sats) / Sats::ONE_BTC_U128 as f64)
}
}
pub struct AvgCentsToUsd;
impl UnaryTransform<StoredF32, Dollars> for AvgCentsToUsd {
#[inline(always)]
fn apply(cents: StoredF32) -> Dollars {
Dollars::from(f64::from(cents) / 100.0)
}
}
pub struct SatsToCents;
impl BinaryTransform<Sats, Cents, Cents> for SatsToCents {
#[inline(always)]
fn apply(sats: Sats, price_cents: Cents) -> Cents {
if unlikely(price_cents.is_nan()) {
Cents::NAN
} else {
Cents::from(sats.as_u128() * price_cents.as_u128() / Sats::ONE_BTC_U128)
}
}
}
pub struct CentsUnsignedToDollars;
impl UnaryTransform<Cents, Dollars> for CentsUnsignedToDollars {
#[inline(always)]
fn apply(cents: Cents) -> Dollars {
cents.into()
}
}
pub struct NegCentsUnsignedToDollars;
impl UnaryTransform<Cents, Dollars> for NegCentsUnsignedToDollars {
#[inline(always)]
fn apply(cents: Cents) -> Dollars {
-Dollars::from(cents)
}
}
pub struct CentsSignedToDollars;
impl UnaryTransform<CentsSigned, Dollars> for CentsSignedToDollars {
#[inline(always)]
fn apply(cents: CentsSigned) -> Dollars {
cents.into()
}
}
pub struct CentsUnsignedToSats;
impl UnaryTransform<Cents, Sats> for CentsUnsignedToSats {
#[inline(always)]
fn apply(cents: Cents) -> Sats {
if unlikely(cents.is_nan()) {
panic!("Cents::NAN cannot be converted to whole Sats");
}
let dollars = Dollars::from(cents);
if dollars == Dollars::ZERO {
Sats::ZERO
} else {
Sats::ONE_BTC / dollars
}
}
}
pub struct CentsSubtractToCentsSigned;
impl BinaryTransform<Cents, Cents, CentsSigned> for CentsSubtractToCentsSigned {
#[inline(always)]
fn apply(a: Cents, b: Cents) -> CentsSigned {
if unlikely(a.is_nan() || b.is_nan()) {
panic!("Cents::NAN cannot be converted to CentsSigned");
}
let difference = i128::from(a.inner()) - i128::from(b.inner());
CentsSigned::new(
i64::try_from(difference).expect("Cents subtraction overflowed CentsSigned"),
)
}
}
pub struct CentsTimesTenths<const V: u16>;
impl<const V: u16> UnaryTransform<Cents, Cents> for CentsTimesTenths<V> {
#[inline(always)]
fn apply(c: Cents) -> Cents {
if unlikely(c.is_nan()) {
Cents::NAN
} else {
Cents::from(c.as_u128() * V as u128 / 10)
}
}
}
pub struct DollarsToSatsFract;
impl UnaryTransform<Dollars, SatsFract> for DollarsToSatsFract {
#[inline(always)]
fn apply(usd: Dollars) -> SatsFract {
SatsFract::ONE_BTC / usd
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cents_outputs_propagate_nan() {
assert!(SatsToCents::apply(Sats::ONE_BTC, Cents::NAN).is_nan());
assert!(CentsTimesTenths::<24>::apply(Cents::NAN).is_nan());
}
#[test]
#[should_panic(expected = "Cents::NAN cannot be converted to whole Sats")]
fn whole_sats_reject_nan() {
CentsUnsignedToSats::apply(Cents::NAN);
}
#[test]
#[should_panic(expected = "Cents::NAN cannot be converted to CentsSigned")]
fn signed_cents_reject_nan() {
CentsSubtractToCentsSigned::apply(Cents::NAN, Cents::ZERO);
}
}