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),*);
}
};
}
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)
}
}
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)
}
}
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)
}
}
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!();
}
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)
}
}
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!();
}
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)
}
}
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!();
}
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)
}
}