Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

Commit

Permalink
Unboilerplatify code for defining currencies (#142)
Browse files Browse the repository at this point in the history
* unboilerplatify code for defining currencies.
   This changes code to use a macro which enables easily adding new variants of currencies over time and excludes the possibility of mismatching variants in impls. Also new macro statically ensures that byte string identifiers ascipted to variants conforms their respective names.

* add tests for `CurrencyId` (from #139)

* add `TryFrom<&[u8]>` implementation for `CurrencyId` where ownership aren't required

Co-authored-by: Anthony Mikhailov <[email protected]>
Co-authored-by: Alexander Koz <[email protected]>
  • Loading branch information
3 people authored Nov 26, 2021
1 parent 4a1e3c5 commit 7b380fc
Showing 1 changed file with 124 additions and 30 deletions.
154 changes: 124 additions & 30 deletions primitives/src/currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,146 @@ use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};

// Currencies id.
#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum CurrencyId {
// Relaychain's currency.
KSM,
// Our native currency.
PONT,
}
pub type CurrencyConversionError = ();

/// Implement currencies.
impl CurrencyId {
pub fn decimals(&self) -> u8 {
match self {
Self::KSM => 12,
Self::PONT => 10,
}
#[allow(dead_code)]
const fn const_slice_eq(a: &[u8], b: &[u8]) -> bool {
let len = a.len();
if b.len() != len {
return false;
}

pub fn symbol(&self) -> Vec<u8> {
match self {
Self::KSM => b"KSM".to_vec(),
Self::PONT => b"PONT".to_vec(),
let mut i = 0;
while i < len {
if a[i] != b[i] {
return false;
}
i += 1;
}
true
}

/// Try from.
impl TryFrom<Vec<u8>> for CurrencyId {
type Error = ();
macro_rules! static_assert {
($cond:expr) => {
#[deny(const_err)]
const _: [(); 1] = [(); $cond as usize];
};
}

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value == b"PONT".to_vec() {
return Ok(CurrencyId::PONT);
macro_rules! def_currencies {
(
$(#[$ty_attr:meta])*
$vis:vis enum $ty_name:ident {
$(
$(#[$attr:meta])*
$name:ident($str:literal, $decimals:expr)
),*
$(,)?
}
) => {
$(#[$ty_attr])*
$vis enum $ty_name {
$(
$(#[$attr])*
$name,
)*
}

impl $ty_name {
pub fn decimals(&self) -> u8 {
match self {
$(Self::$name => $decimals,)*
}
}

if value == b"KSM".to_vec() {
return Ok(CurrencyId::KSM);
pub fn symbol(&self) -> Vec<u8> {
match self {
$(Self::$name => $str.to_vec(),)*
}
}
}

Err(())
impl TryFrom<Vec<u8>> for $ty_name {
type Error = CurrencyConversionError;

fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
match &v[..] {
$($str => Ok(Self::$name),)*
_ => Err(Self::Error::default()),
}
}
}

impl TryFrom<&'_ [u8]> for $ty_name {
type Error = CurrencyConversionError;

fn try_from(v: &'_ [u8]) -> Result<Self, Self::Error> {
match v {
$($str => Ok(Self::$name),)*
_ => Err(Self::Error::default()),
}
}
}

$(static_assert!(const_slice_eq(stringify!($name).as_bytes(), $str));)*
};
}

def_currencies! {
/// Currencies id.
#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum CurrencyId {
/// Relaychain's currency.
KSM(b"KSM", 12),
/// Our native currency.
PONT(b"PONT", 10),
}
}

impl Default for CurrencyId {
fn default() -> Self {
// PONT should be default currency.
CurrencyId::PONT
}
}

#[cfg(test)]
mod tests {
use super::{CurrencyId, TryFrom};

#[test]
/// Test default currency.
fn default() {
assert_eq!(CurrencyId::default(), CurrencyId::PONT);
}

#[test]
/// Test currencies decimals.
fn decimals() {
assert_eq!(CurrencyId::PONT.decimals(), 10);
assert_eq!(CurrencyId::KSM.decimals(), 12);
}

#[test]
/// Test currencies symbols.
fn symbols() {
assert_eq!(CurrencyId::PONT.symbol(), b"PONT");
assert_eq!(CurrencyId::KSM.symbol(), b"KSM");
}

#[test]
/// Test try from Vec<u8>.
fn try_from_vec() {
assert_eq!(CurrencyId::try_from(b"PONT".to_vec()), Ok(CurrencyId::PONT));
assert_eq!(CurrencyId::try_from(b"KSM".to_vec()), Ok(CurrencyId::KSM));
assert!(CurrencyId::try_from(b"UNKNOWN".to_vec()).is_err());
}

#[test]
/// Test try from &[u8].
fn try_from_slice() {
assert_eq!(CurrencyId::try_from(b"PONT".as_ref()), Ok(CurrencyId::PONT));
assert_eq!(CurrencyId::try_from(b"KSM".as_ref()), Ok(CurrencyId::KSM));
assert!(CurrencyId::try_from(b"UNKNOWN".as_ref()).is_err());
}
}

0 comments on commit 7b380fc

Please sign in to comment.