use std::{
fmt,
hash::{Hash, Hasher},
ops,
str::FromStr,
};
use zakura_chain::amount::{self, Amount, Constraint, COIN};
use zakura_node_services::BoxError;
#[allow(unused_imports)]
use zakura_chain::amount::MAX_MONEY;
pub const MAX_ZEC_FORMAT_PRECISION: usize = 8;
#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Default)]
#[serde(try_from = "f64")]
#[serde(into = "f64")]
#[serde(bound = "C: Constraint + Clone")]
pub struct Zec<C: Constraint>(Amount<C>);
impl<C: Constraint> Zec<C> {
pub fn lossy_zec(&self) -> f64 {
let zats = self.zatoshis();
let zats = zats as f64;
let coin = COIN as f64;
zats / coin
}
pub fn from_lossy_zec(lossy_zec: f64) -> Result<Self, BoxError> {
let coin = COIN as f64;
let zats = lossy_zec * coin;
if zats != zats.trunc() {
return Err(
"loss of precision parsing ZEC value: floating point had fractional zatoshis"
.into(),
);
}
let zats = zats as i64;
let zats = Amount::try_from(zats)?;
Ok(Self(zats))
}
}
impl<C: Constraint> From<Zec<C>> for f64 {
fn from(zec: Zec<C>) -> f64 {
zec.lossy_zec()
}
}
impl<C: Constraint> TryFrom<f64> for Zec<C> {
type Error = BoxError;
fn try_from(value: f64) -> Result<Self, Self::Error> {
Self::from_lossy_zec(value)
}
}
impl<C: Constraint> fmt::Display for Zec<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let zec = self.lossy_zec();
let decimals = f.precision().unwrap_or(MAX_ZEC_FORMAT_PRECISION);
let string = format!("{zec:.decimals$}");
f.pad_integral(zec >= 0.0, "", &string)
}
}
impl<C: Constraint> FromStr for Zec<C> {
type Err = BoxError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lossy_zec: f64 = s.parse()?;
Self::from_lossy_zec(lossy_zec)
}
}
impl<C: Constraint> std::fmt::Debug for Zec<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Zec<{}>", std::any::type_name::<C>()))
.field("ZEC", &self.to_string())
.field("zat", &self.0)
.finish()
}
}
impl<C: Constraint> ops::Deref for Zec<C> {
type Target = Amount<C>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<C: Constraint> ops::DerefMut for Zec<C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<C: Constraint> From<Amount<C>> for Zec<C> {
fn from(amount: Amount<C>) -> Self {
Self(amount)
}
}
impl<C: Constraint> From<Zec<C>> for Amount<C> {
fn from(zec: Zec<C>) -> Amount<C> {
zec.0
}
}
impl<C: Constraint> From<Zec<C>> for i64 {
fn from(zec: Zec<C>) -> i64 {
zec.0.into()
}
}
impl<C: Constraint> TryFrom<i64> for Zec<C> {
type Error = amount::Error;
fn try_from(value: i64) -> Result<Self, Self::Error> {
Ok(Self(Amount::try_from(value)?))
}
}
impl<C: Constraint> Hash for Zec<C> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<C1: Constraint, C2: Constraint> PartialEq<Zec<C2>> for Zec<C1> {
fn eq(&self, other: &Zec<C2>) -> bool {
self.0.eq(&other.0)
}
}
impl<C: Constraint> PartialEq<i64> for Zec<C> {
fn eq(&self, other: &i64) -> bool {
self.0.eq(other)
}
}
impl<C: Constraint> PartialEq<Zec<C>> for i64 {
fn eq(&self, other: &Zec<C>) -> bool {
self.eq(&other.0)
}
}
impl<C1: Constraint, C2: Constraint> PartialEq<Amount<C2>> for Zec<C1> {
fn eq(&self, other: &Amount<C2>) -> bool {
self.0.eq(other)
}
}
impl<C1: Constraint, C2: Constraint> PartialEq<Zec<C2>> for Amount<C1> {
fn eq(&self, other: &Zec<C2>) -> bool {
self.eq(&other.0)
}
}
impl<C: Constraint> Eq for Zec<C> {}