#![expect(
clippy::inline_always,
reason = "Just unification helpers of 1-2 lines of casting types"
)]
#![expect(
dead_code,
reason = "Not all converters are used bidirectionally, however, keeping them is a good thing"
)]
#![expect(
clippy::cast_sign_loss,
reason = "This is the one file where we map the signed database types to the working types"
)]
#![expect(
clippy::cast_possible_wrap,
reason = "We will not approach the item count where i64 and usize casting will cause issues
on relevant platforms"
)]
use miden_crypto::Word;
use miden_crypto::utils::Deserializable;
use miden_protocol::Felt;
use miden_protocol::account::{StorageSlotName, StorageSlotType};
use miden_protocol::block::{BlockHeader, BlockNumber};
use miden_protocol::note::NoteTag;
use crate::db::models::queries::{BlockHeaderCommitment, NetworkAccountType};
#[derive(Debug, thiserror::Error)]
#[error("failed to convert from database type {from_type} into {into_type}")]
pub struct DatabaseTypeConversionError {
source: Box<dyn std::error::Error + Send + Sync>,
from_type: &'static str,
into_type: &'static str,
}
pub trait SqlTypeConvert: Sized {
type Raw: Sized;
fn to_raw_sql(self) -> Self::Raw;
fn from_raw_sql(_raw: Self::Raw) -> Result<Self, DatabaseTypeConversionError>;
fn map_err<E: std::error::Error + Send + Sync + 'static>(
source: E,
) -> DatabaseTypeConversionError {
DatabaseTypeConversionError {
source: Box::new(source),
from_type: std::any::type_name::<Self::Raw>(),
into_type: std::any::type_name::<Self>(),
}
}
}
impl SqlTypeConvert for BlockHeaderCommitment {
type Raw = Vec<u8>;
fn from_raw_sql(
raw: Self::Raw,
) -> Result<Self, crate::db::models::conv::DatabaseTypeConversionError> {
let inner =
<Word as Deserializable>::read_from_bytes(raw.as_slice()).map_err(Self::map_err)?;
Ok(BlockHeaderCommitment(inner))
}
fn to_raw_sql(self) -> Self::Raw {
self.0.as_bytes().to_vec()
}
}
impl SqlTypeConvert for BlockHeader {
type Raw = Vec<u8>;
fn from_raw_sql(raw: Self::Raw) -> Result<Self, DatabaseTypeConversionError> {
<Self as Deserializable>::read_from_bytes(raw.as_slice()).map_err(Self::map_err)
}
fn to_raw_sql(self) -> Self::Raw {
miden_crypto::utils::Serializable::to_bytes(&self)
}
}
impl SqlTypeConvert for NetworkAccountType {
type Raw = i32;
fn to_raw_sql(self) -> Self::Raw {
match self {
NetworkAccountType::None => 0,
NetworkAccountType::Network => 1,
}
}
fn from_raw_sql(raw: Self::Raw) -> Result<Self, DatabaseTypeConversionError> {
#[derive(Debug, thiserror::Error)]
#[error("invalid network account type value {0}")]
struct ValueError(i32);
match raw {
0 => Ok(Self::None),
1 => Ok(Self::Network),
other => Err(Self::map_err(ValueError(other))),
}
}
}
impl SqlTypeConvert for BlockNumber {
type Raw = i64;
fn from_raw_sql(raw: Self::Raw) -> Result<Self, DatabaseTypeConversionError> {
u32::try_from(raw).map(BlockNumber::from).map_err(Self::map_err)
}
fn to_raw_sql(self) -> Self::Raw {
i64::from(self.as_u32())
}
}
impl SqlTypeConvert for NoteTag {
type Raw = i32;
#[inline(always)]
fn from_raw_sql(raw: Self::Raw) -> Result<Self, DatabaseTypeConversionError> {
#[expect(clippy::cast_sign_loss)]
Ok(NoteTag::new(raw as u32))
}
#[inline(always)]
fn to_raw_sql(self) -> Self::Raw {
self.as_u32() as i32
}
}
impl SqlTypeConvert for StorageSlotType {
type Raw = i32;
#[inline(always)]
fn from_raw_sql(raw: Self::Raw) -> Result<Self, DatabaseTypeConversionError> {
#[derive(Debug, thiserror::Error)]
#[error("invalid storage slot type value {0}")]
struct ValueError(i32);
Ok(match raw {
0 => StorageSlotType::Value,
1 => StorageSlotType::Map,
invalid => {
return Err(Self::map_err(ValueError(invalid)));
},
})
}
#[inline(always)]
fn to_raw_sql(self) -> Self::Raw {
match self {
StorageSlotType::Value => 0,
StorageSlotType::Map => 1,
}
}
}
impl SqlTypeConvert for StorageSlotName {
type Raw = String;
fn from_raw_sql(raw: Self::Raw) -> Result<Self, DatabaseTypeConversionError> {
StorageSlotName::new(raw).map_err(Self::map_err)
}
fn to_raw_sql(self) -> Self::Raw {
String::from(self)
}
}
#[inline(always)]
pub(crate) fn raw_sql_to_nullifier_prefix(raw: i32) -> u16 {
debug_assert!(raw >= 0);
raw as u16
}
#[inline(always)]
pub(crate) fn nullifier_prefix_to_raw_sql(prefix: u16) -> i32 {
i32::from(prefix)
}
#[inline(always)]
pub(crate) fn raw_sql_to_nonce(raw: i64) -> Felt {
debug_assert!(raw >= 0);
Felt::new(raw as u64)
}
#[inline(always)]
pub(crate) fn nonce_to_raw_sql(nonce: Felt) -> i64 {
nonce.as_canonical_u64() as i64
}
#[inline(always)]
pub(crate) fn raw_sql_to_fungible_delta(raw: i64) -> i64 {
raw
}
#[inline(always)]
pub(crate) fn fungible_delta_to_raw_sql(delta: i64) -> i64 {
delta
}
#[inline(always)]
#[expect(clippy::cast_sign_loss)]
pub(crate) fn raw_sql_to_note_type(raw: i32) -> u8 {
raw as u8
}
#[inline(always)]
pub(crate) fn note_type_to_raw_sql(note_type: u8) -> i32 {
i32::from(note_type)
}
#[inline(always)]
pub(crate) fn raw_sql_to_idx(raw: i32) -> usize {
raw as usize
}
#[inline(always)]
pub(crate) fn idx_to_raw_sql(idx: usize) -> i32 {
idx as i32
}