use std::ops::Mul;
use rust_decimal::{
prelude::{FromPrimitive, ToPrimitive},
Decimal,
};
use subxt::{
tx::{SubmittableExtrinsic, TxProgress},
OnlineClient, PolkadotConfig,
};
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use crate::error::MandalaClientErorr;
pub(crate) type MandalaConfig = PolkadotConfig;
pub(crate) type NodeClient = OnlineClient<MandalaConfig>;
pub(crate) type Extrinsic = SubmittableExtrinsic<MandalaConfig, NodeClient>;
pub(crate) type TransactionProgress = TxProgress<MandalaConfig, NodeClient>;
#[cfg(feature = "tokio")]
pub type ReceiverChannel<Message> = UnboundedReceiver<Message>;
#[cfg(feature = "tokio")]
pub type SenderChannel<Message> = UnboundedSender<Message>;
#[cfg(feature = "tokio")]
pub struct InternalChannels<Message> {
receiver: Option<ReceiverChannel<Message>>,
sender: SenderChannel<Message>,
}
impl<Message> From<SenderChannel<Message>> for InternalChannels<Message> {
fn from(value: SenderChannel<Message>) -> Self {
Self {
receiver: None,
sender: value,
}
}
}
impl<Message> Default for InternalChannels<Message> {
fn default() -> Self {
let (sender, receiver) = tokio::sync::mpsc::unbounded_channel::<Message>();
Self {
receiver: Some(receiver),
sender,
}
}
}
impl<Message> InternalChannels<Message> {
pub fn new() -> Self {
Default::default()
}
pub fn get_receiver(&mut self) -> ReceiverChannel<Message> {
self.receiver.take().expect("should be called only once")
}
#[must_use]
pub fn is_receiver_taken(&self) -> bool {
self.receiver.is_none()
}
pub fn sender(&self) -> &SenderChannel<Message> {
&self.sender
}
}
pub struct MandalaExtrinsics(pub(crate) Extrinsic);
impl MandalaExtrinsics {
pub(crate) fn new(tx: Extrinsic) -> Self {
Self(tx)
}
pub(crate) fn into_inner(self) -> Extrinsic {
self.0
}
}
impl From<Extrinsic> for MandalaExtrinsics {
fn from(value: Extrinsic) -> Self {
Self(value)
}
}
pub struct MandalaTransactionProgress(pub(crate) TransactionProgress);
impl From<TransactionProgress> for MandalaTransactionProgress {
fn from(value: TransactionProgress) -> Self {
Self(value)
}
}
#[derive(Clone)]
pub struct MandalaClient(pub(crate) OnlineClient<MandalaConfig>);
impl From<OnlineClient<MandalaConfig>> for MandalaClient {
fn from(value: OnlineClient<MandalaConfig>) -> Self {
Self(value)
}
}
impl MandalaClient {
pub(crate) fn inner_internal(&self) -> &OnlineClient<MandalaConfig> {
&self.0
}
#[cfg(feature = "subxt")]
pub fn inner(&self) -> &OnlineClient<MandalaConfig> {
&self.0
}
pub async fn new(node_url: &str) -> Result<Self, crate::error::Error> {
let client = OnlineClient::<MandalaConfig>::from_url(node_url)
.await
.map_err(MandalaClientErorr::from)?;
Ok(Self(client))
}
pub async fn dev() -> Result<Self, crate::error::Error> {
let client = OnlineClient::<MandalaConfig>::new()
.await
.map_err(MandalaClientErorr::from)?;
Ok(Self(client))
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Unit {
amount: Decimal,
decimals: u8,
}
pub const GENERIC_SUBSTRATE_DECIMALS: u8 = 12;
impl Unit {
pub fn new(amount: &str, decimals: Option<u8>) -> Result<Self, crate::error::Error> {
let decimals = decimals.unwrap_or(GENERIC_SUBSTRATE_DECIMALS);
let _decimals = Self::calculate_decimals(decimals);
let amount = Self::calculate_amount(amount, _decimals)?;
Ok(Self { amount, decimals })
}
pub fn as_u128(&self) -> u128 {
self.amount
.to_u128()
.expect("valid conversion should not fail")
}
pub fn decimals(&self) -> u8 {
self.decimals
}
fn calculate_decimals(decimals: u8) -> Decimal {
use rust_decimal::prelude::*;
Self::decimals_multiplier().powu(decimals as u64)
}
fn calculate_amount(amount: &str, decimals: Decimal) -> Result<Decimal, crate::error::Error> {
Ok(Decimal::from_str_exact(amount)?.mul(decimals))
}
fn decimals_multiplier() -> Decimal {
let multiplier = 10;
Decimal::from_u8(multiplier).expect("static values must be a valid conversion")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unit() {
let unit = Unit::new("9", None).unwrap();
assert_eq!(unit.as_u128(), 9_000_000_000_000);
}
#[test]
fn test_with_decimals() {
let unit = Unit::new("0.9", None).unwrap();
assert_eq!(unit.as_u128(), 900_000_000_000);
let unit = Unit::new("2.1", None).unwrap();
assert_eq!(unit.as_u128(), 2_100_000_000_000);
let unit = Unit::new("2.01", None).unwrap();
assert_eq!(unit.as_u128(), 2_010_000_000_000);
}
}