nintypes 0.2.11

Nintondo shared types
Documentation
use tokio_postgres::types::{accepts, to_sql_checked, FromSql, ToSql};

use crate::utils::bytes::NinBytes;
use crate::utils::fixed::Fixed128;

use super::amount::Amount;
use super::block::BlockNumber;
use super::inscriptions::{Genesis, InscriptionNumber, Outpoint};

macro_rules! impl_sql_conv {
    ($T:ty as ($($ACCEPTS:ident)|*), from: $FROM:expr, into: $INTO:expr $(,)?) => {
        impl ToSql for $T {
            fn to_sql(&self, ty: &tokio_postgres::types::Type, out: &mut bytes::BytesMut) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
            where
                Self: Sized,
            {
                ($INTO)(self, ty, out)
            }

            accepts!($($ACCEPTS),*);
            to_sql_checked!();
        }
        impl<'a> FromSql<'a> for $T {
            fn from_sql(ty: &tokio_postgres::types::Type, raw: &'a [u8]) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
                ($FROM)(ty, raw)
            }

            accepts!($($ACCEPTS),*);
        }
    };
}

// InscriptionNumber is represented by i64 in pg
impl_sql_conv! {
    InscriptionNumber as (INT8),
    from: |ty, raw|{
        Ok(InscriptionNumber(i64::from_sql(ty, raw)? as _))
    },
    into: |this: &Self, ty, out|{
        (this.0 as i64).to_sql(ty, out)
    }
}

// BlockNumber is represented by i32 in pg
impl_sql_conv! {
    BlockNumber as (INT4),
    from: |ty, raw|{
        Ok(BlockNumber(i32::from_sql(ty, raw)? as _))
    },
    into: |this: &Self, ty, out|{
        (this.0 as i32).to_sql(ty, out)
    }
}

// Amount is represented by i64 in pg
impl_sql_conv! {
    Amount as (INT8),
    from: |ty, raw|{
        Ok(Amount(i64::from_sql(ty, raw)? as _))
    },
    into: |this: &Self, ty, out|{
        (this.0 as i64).to_sql(ty, out)
    }
}

// Fixed128 is represented by TEXT in pg
impl<const PRECISION: u8> ToSql for Fixed128<PRECISION> {
    fn to_sql(&self, ty: &tokio_postgres::types::Type, out: &mut bytes::BytesMut) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
    where
        Self: Sized,
    {
        self.to_string().to_sql(ty, out)
    }

    fn accepts(ty: &tokio_postgres::types::Type) -> bool {
        <String as tokio_postgres::types::FromSql>::accepts(ty)
    }
    to_sql_checked!();
}
// Fixed128 is represented by TEXT in pg
impl<'a, const PRECISION: u8> FromSql<'a> for Fixed128<PRECISION> {
    fn from_sql(ty: &tokio_postgres::types::Type, raw: &'a [u8]) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
        Ok(String::from_sql(ty, raw)?.parse::<Self>()?)
    }

    fn accepts(ty: &tokio_postgres::types::Type) -> bool {
        <String as tokio_postgres::types::ToSql>::accepts(ty)
    }
}

// Outpoint is represented by 36-size BYTEA = Domain(BYTEA) in pg
impl ToSql for Outpoint {
    fn to_sql(&self, ty: &tokio_postgres::types::Type, out: &mut bytes::BytesMut) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
    where
        Self: Sized,
    {
        if <&[u8] as tokio_postgres::types::FromSql>::accepts(ty) {
            return self.as_bytes().to_sql(ty, out);
        }
        let tokio_postgres::types::Kind::Domain(ref t) = ty.kind() else { unreachable!() };
        self.as_bytes().to_sql(t, out)
    }
    fn accepts(ty: &tokio_postgres::types::Type) -> bool {
        if <&[u8] as tokio_postgres::types::FromSql>::accepts(ty) {
            return true;
        }
        if ty.name() != "outpoint" {
            return false;
        }
        let tokio_postgres::types::Kind::Domain(ref t) = ty.kind() else { return false };
        <&[u8] as tokio_postgres::types::ToSql>::accepts(t)
    }
    to_sql_checked!();
}
// Outpoint is represented by 36-size BYTEA = Domain(BYTEA) in pg
impl<'a> FromSql<'a> for Outpoint {
    fn from_sql(ty: &tokio_postgres::types::Type, raw: &'a [u8]) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
        if <&[u8] as tokio_postgres::types::FromSql>::accepts(ty) {
            let bytes = <&[u8]>::from_sql(ty, raw)?;
            let bytes = <[u8; 36]>::try_from(bytes)?;
            return Ok(Outpoint::from_bytes(bytes));
        }

        let tokio_postgres::types::Kind::Domain(ref t) = ty.kind() else { unreachable!() };
        let bytes = <&[u8]>::from_sql(t, raw)?;
        let bytes = <[u8; 36]>::try_from(bytes)?;
        Ok(Outpoint::from_bytes(bytes))
    }

    fn accepts(ty: &tokio_postgres::types::Type) -> bool {
        if <&[u8] as tokio_postgres::types::FromSql>::accepts(ty) {
            return true;
        }
        if ty.name() != "outpoint" {
            return false;
        }
        let tokio_postgres::types::Kind::Domain(ref t) = ty.kind() else { return false };
        <&[u8] as tokio_postgres::types::FromSql>::accepts(t)
    }
}

// Genesis is represented by 36-size BYTEA = Domain(BYTEA) in pg
impl ToSql for Genesis {
    fn to_sql(&self, ty: &tokio_postgres::types::Type, out: &mut bytes::BytesMut) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
    where
        Self: Sized,
    {
        if <&[u8] as tokio_postgres::types::FromSql>::accepts(ty) {
            return self.0.to_sql(ty, out);
        }
        self.0.to_sql(ty, out)
    }
    fn accepts(ty: &tokio_postgres::types::Type) -> bool {
        if <&[u8] as tokio_postgres::types::FromSql>::accepts(ty) {
            return true;
        }
        if ty.name() != "genesis" {
            return false;
        }
        let tokio_postgres::types::Kind::Domain(ref t) = ty.kind() else { return false };
        <&[u8] as tokio_postgres::types::ToSql>::accepts(t)
    }
    to_sql_checked!();
}
// Genesis is represented by 36-size BYTEA = Domain(BYTEA) in pg
impl<'a> FromSql<'a> for Genesis {
    fn from_sql(ty: &tokio_postgres::types::Type, raw: &'a [u8]) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
        Outpoint::from_sql(ty, raw).map(Self)
    }

    fn accepts(ty: &tokio_postgres::types::Type) -> bool {
        if <&[u8] as tokio_postgres::types::FromSql>::accepts(ty) {
            return true;
        }
        if ty.name() != "genesis" {
            return false;
        }
        let tokio_postgres::types::Kind::Domain(ref t) = ty.kind() else { return false };
        <&[u8] as tokio_postgres::types::FromSql>::accepts(t)
    }
}