use serde::{Deserialize, Serialize};
use std::{fmt, str::FromStr};
use crate::{Error, Result};
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Hash,
)]
pub enum Asset {
ADA, ALGO, ANT, ATOM, BAL, BAT, BCH, COMP, CRV, DAI, DASH, DOT, EOS, FIL, FLOW, GNO, ICX, KAVA, KEEP, KNC, KSM, LINK, LSK, NANO, OMG, OXT, PAXG, QTUM, REPV2, SC, SNX, STORJ, TBTC, TRX, UNI, USDC, USDT, WAVE, WAVES, XETC, XETH, XLTC, XMLN, XREP, XTZ, XXBT, XXDG, XXLM, XXMR, XXRP, XZEC, YFI, ZEC, CHF, ZAUD, ZCAD, ZEUR, ZGBP, ZJPY, ZUSD, KFEE, #[serde(other)]
Unknown,
}
impl fmt::Display for Asset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Unknown => write!(f, "<unknown asset>"),
_ => write!(f, "{:?}", self),
}
}
}
impl FromStr for Asset {
type Err = Error;
fn from_str(asset: &str) -> Result<Self> {
let asset = match asset.to_ascii_uppercase().as_str() {
"ADA" => Self::ADA,
"ALGO" => Self::ALGO,
"ANT" => Self::ANT,
"ATOM" => Self::ATOM,
"BAL" => Self::BAL,
"BAT" => Self::BAT,
"BCH" => Self::BCH,
"COMP" => Self::COMP,
"CRV" => Self::CRV,
"DAI" => Self::DAI,
"DASH" => Self::DASH,
"DOT" => Self::DOT,
"EOS" => Self::EOS,
"FIL" => Self::FIL,
"FLOW" => Self::FLOW,
"GNO" => Self::GNO,
"ICX" => Self::ICX,
"KAVA" => Self::KAVA,
"KEEP" => Self::KEEP,
"KNC" => Self::KNC,
"KSM" => Self::KSM,
"LINK" => Self::LINK,
"LSK" => Self::LSK,
"NANO" => Self::NANO,
"OMG" => Self::OMG,
"OXT" => Self::OXT,
"PAXG" => Self::PAXG,
"QTUM" => Self::QTUM,
"REPV2" => Self::REPV2,
"SC" => Self::SC,
"SNX" => Self::SNX,
"STORJ" => Self::STORJ,
"TBTC" => Self::TBTC,
"TRX" => Self::TRX,
"UNI" => Self::UNI,
"USDC" => Self::USDC,
"USDT" => Self::USDT,
"WAVE" => Self::WAVE,
"WAVES" => Self::WAVES,
"XETC" => Self::XETC,
"XETH" => Self::XETH,
"XLTC" => Self::XLTC,
"XMLN" => Self::XMLN,
"XREP" => Self::XREP,
"XTZ" => Self::XTZ,
"XXBT" => Self::XXBT,
"XXDG" => Self::XXDG,
"XXLM" => Self::XXLM,
"XXMR" => Self::XXMR,
"XXRP" => Self::XXRP,
"XZEC" => Self::XZEC,
"YFI" => Self::YFI,
"ZEC" => Self::ZEC,
"CHF" => Self::CHF,
"ZAUD" => Self::ZAUD,
"ZCAD" => Self::ZCAD,
"ZEUR" => Self::ZEUR,
"ZGBP" => Self::ZGBP,
"ZJPY" => Self::ZJPY,
"ZUSD" => Self::ZUSD,
"KFEE" => Self::KFEE,
_ => return Err(Error::unknown_asset(asset)),
};
Ok(asset)
}
}
impl Asset {
pub fn pair(self, other: Self) -> String {
format!("{}{}", self, other)
}
pub fn is_crypto(self) -> bool {
!(self.is_fiat() || self.is_kraken_credit() || self.is_unknown())
}
pub fn is_fiat(self) -> bool {
matches!(
self,
Self::ZAUD
| Self::ZCAD
| Self::ZEUR
| Self::ZGBP
| Self::ZJPY
| Self::ZUSD
| Self::CHF
)
}
pub fn is_kraken_credit(self) -> bool {
self == Self::KFEE
}
pub fn is_unknown(self) -> bool {
self == Self::Unknown
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{api, Client, Response};
use anyhow::{bail, Result};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::{collections::HashMap, iter};
#[derive(Debug, Deserialize)]
struct AssetPair<T> {
base: T,
quote: T,
}
#[test]
fn asset_pairs() -> Result<()> {
let client = Client::default();
type AssetPairs = HashMap<String, AssetPair<Asset>>;
let api = api::public::asset_pairs();
let resp: Response<AssetPairs> = client.send(api)?;
assert!(resp.is_success());
assert!(resp.result.is_some());
Ok(())
}
#[test]
fn asset_from_str() -> Result<()> {
let client = Client::default();
type AssetPairs = HashMap<String, AssetPair<String>>;
let api = api::public::asset_pairs();
let resp: Response<AssetPairs> = client.send(api)?;
assert!(resp.is_success());
if let Some(asset_pairs) = resp.result {
for asset_pair in asset_pairs.values() {
let base = asset_pair.base.parse::<Asset>()?;
assert!(!base.is_unknown());
let quote = asset_pair.quote.parse::<Asset>()?;
assert!(!quote.is_unknown());
}
} else {
bail!("No asset pairs in response result");
}
Ok(())
}
#[test]
fn asset_deserialize() -> Result<()> {
let xbt: Asset = serde_json::from_str(r#""XXBT""#)?;
assert_eq!(Asset::XXBT, xbt);
assert!(xbt.is_crypto());
assert!(!(xbt.is_fiat() || xbt.is_kraken_credit() || xbt.is_unknown()));
let eur: Asset = serde_json::from_str(r#""ZEUR""#)?;
assert_eq!(Asset::ZEUR, eur);
assert!(eur.is_fiat());
assert!(
!(eur.is_crypto() || eur.is_kraken_credit() || eur.is_unknown())
);
let kfee: Asset = serde_json::from_str(r#""KFEE""#)?;
assert_eq!(Asset::KFEE, kfee);
assert!(kfee.is_kraken_credit());
assert!(!(kfee.is_crypto() || kfee.is_fiat() || kfee.is_unknown()));
let mut rng = thread_rng();
let unknown = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.take(4)
.collect::<String>()
.to_ascii_uppercase();
println!("Deserializing unknown asset: {}", unknown);
let unknown: Asset =
serde_json::from_str(&format!(r#""{}""#, unknown))?;
assert_eq!(Asset::Unknown, unknown);
assert!(unknown.is_unknown());
assert!(
!(unknown.is_crypto()
|| unknown.is_kraken_credit()
|| unknown.is_fiat())
);
Ok(())
}
}