use firewood::api;
use firewood::merkle;
use firewood_storage::TrieHash;
use std::fmt;
use crate::revision::{GetRevisionResult, RevisionHandle};
use crate::{
ChangeProofContext, CodeIteratorHandle, CreateIteratorResult, CreateProposalResult, HashKey,
IteratorHandle, KeyRange, NextKeyRange, OwnedBytes, OwnedKeyValueBatch, OwnedKeyValuePair,
ProposalHandle, ProposedChangeProofContext, RangeProofContext, ReconstructedHandle,
VerifiedChangeProofContext,
};
#[derive(Debug)]
#[repr(C, usize)]
pub enum VoidResult {
NullHandlePointer,
Ok,
Err(OwnedBytes),
}
impl From<()> for VoidResult {
fn from((): ()) -> Self {
VoidResult::Ok
}
}
impl<E: fmt::Display> From<Result<(), E>> for VoidResult {
fn from(value: Result<(), E>) -> Self {
match value {
Ok(()) => VoidResult::Ok,
Err(err) => VoidResult::Err(err.to_string().into_bytes().into()),
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum HandleResult {
Ok(Box<crate::DatabaseHandle>),
Err(OwnedBytes),
}
impl<E: fmt::Display> From<Result<crate::DatabaseHandle, E>> for HandleResult {
fn from(value: Result<crate::DatabaseHandle, E>) -> Self {
match value {
Ok(handle) => HandleResult::Ok(Box::new(handle)),
Err(err) => HandleResult::Err(err.to_string().into_bytes().into()),
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum ValueResult {
NullHandlePointer,
RevisionNotFound(HashKey),
None,
Some(OwnedBytes),
Err(OwnedBytes),
}
impl<E: fmt::Display> From<Result<String, E>> for ValueResult {
fn from(value: Result<String, E>) -> Self {
match value {
Ok(data) => ValueResult::Some(data.into_bytes().into()),
Err(err) => ValueResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl From<Result<Option<Box<[u8]>>, api::Error>> for ValueResult {
fn from(value: Result<Option<Box<[u8]>>, api::Error>) -> Self {
match value {
Ok(None) => ValueResult::None,
Err(api::Error::RevisionNotFound { provided }) => ValueResult::RevisionNotFound(
HashKey::from(provided.unwrap_or_else(api::HashKey::empty)),
),
Ok(Some(data)) => ValueResult::Some(data.into()),
Err(err) => ValueResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl From<Result<Option<Box<[u8]>>, firewood::db::DbError>> for ValueResult {
fn from(value: Result<Option<Box<[u8]>>, firewood::db::DbError>) -> Self {
value.map_err(api::Error::from).into()
}
}
impl From<Vec<u8>> for ValueResult {
fn from(value: Vec<u8>) -> Self {
value.into_boxed_slice().into()
}
}
impl From<Box<[u8]>> for ValueResult {
fn from(value: Box<[u8]>) -> Self {
ValueResult::Some(value.into())
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum HashResult {
NullHandlePointer,
None,
Some(HashKey),
Err(OwnedBytes),
}
impl<E: fmt::Display> From<Result<Option<api::HashKey>, E>> for HashResult {
fn from(value: Result<Option<api::HashKey>, E>) -> Self {
match value {
Ok(hash) => hash.into(),
Err(err) => HashResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl From<Option<TrieHash>> for HashResult {
fn from(value: Option<TrieHash>) -> Self {
match value {
Some(hash) => HashResult::Some(hash.into()),
None => HashResult::None,
}
}
}
impl From<Option<Result<HashKey, api::Error>>> for HashResult {
fn from(value: Option<Result<HashKey, api::Error>>) -> Self {
match value {
Some(value) => match value {
Ok(hash) => HashResult::Some(hash),
Err(err) => HashResult::Err(err.to_string().into_bytes().into()),
},
None => HashResult::None,
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum RangeProofResult<'db> {
NullHandlePointer,
RevisionNotFound(HashKey),
EmptyTrie,
Ok(Box<RangeProofContext<'db>>),
Err(OwnedBytes),
}
impl From<Result<api::FrozenRangeProof, api::Error>> for RangeProofResult<'_> {
fn from(value: Result<api::FrozenRangeProof, api::Error>) -> Self {
match value {
Ok(proof) => RangeProofResult::Ok(Box::new(proof.into())),
Err(api::Error::RevisionNotFound { provided }) => RangeProofResult::RevisionNotFound(
HashKey::from(provided.unwrap_or_else(api::HashKey::empty)),
),
Err(api::Error::RangeProofOnEmptyTrie) => RangeProofResult::EmptyTrie,
Err(err) => RangeProofResult::Err(err.to_string().into_bytes().into()),
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum ChangeProofResult {
NullHandlePointer,
StartRevisionNotFound(HashKey),
EndRevisionNotFound(HashKey),
Ok(Box<ChangeProofContext>),
Err(OwnedBytes),
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum VerifiedChangeProofResult {
NullHandlePointer,
Ok(Box<VerifiedChangeProofContext>),
Err(OwnedBytes),
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum ProposedChangeProofResult<'db> {
NullHandlePointer,
Ok(Box<ProposedChangeProofContext<'db>>),
Err(OwnedBytes),
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum NextKeyRangeResult {
NullHandlePointer,
NotPrepared,
None,
Some(NextKeyRange),
Err(OwnedBytes),
}
impl From<Result<Option<KeyRange>, api::Error>> for NextKeyRangeResult {
fn from(value: Result<Option<KeyRange>, api::Error>) -> Self {
match value {
Ok(None) => NextKeyRangeResult::None,
Ok(Some((start_key, end_key))) => NextKeyRangeResult::Some(NextKeyRange {
start_key: start_key.into(),
end_key: end_key.map(Into::into).into(),
}),
Err(api::Error::ProofError(firewood::ProofError::Unverified)) => {
NextKeyRangeResult::NotPrepared
}
Err(err) => NextKeyRangeResult::Err(err.to_string().into_bytes().into()),
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum CodeIteratorResult<'p> {
NullHandlePointer,
Ok {
handle: Box<CodeIteratorHandle<'p>>,
},
Err(OwnedBytes),
}
impl<'a> From<Result<CodeIteratorHandle<'a>, api::Error>> for CodeIteratorResult<'a> {
fn from(value: Result<CodeIteratorHandle<'a>, api::Error>) -> Self {
match value {
Ok(res) => CodeIteratorResult::Ok {
handle: Box::new(res),
},
Err(err) => CodeIteratorResult::Err(err.to_string().into_bytes().into()),
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum ProposalResult<'db> {
NullHandlePointer,
Ok {
handle: Box<ProposalHandle<'db>>,
root_hash: HashKey,
},
Err(OwnedBytes),
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum IteratorResult<'db> {
NullHandlePointer,
Ok {
handle: Box<IteratorHandle<'db>>,
},
Err(OwnedBytes),
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum KeyValueResult {
NullHandlePointer,
None,
Some(OwnedKeyValuePair),
Err(OwnedBytes),
}
impl From<Option<Result<(merkle::Key, merkle::Value), api::Error>>> for KeyValueResult {
fn from(value: Option<Result<(merkle::Key, merkle::Value), api::Error>>) -> Self {
match value {
Some(value) => match value {
Ok(value) => KeyValueResult::Some(value.into()),
Err(err) => KeyValueResult::Err(err.to_string().into_bytes().into()),
},
None => KeyValueResult::None,
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum KeyValueBatchResult {
NullHandlePointer,
Some(OwnedKeyValueBatch),
Err(OwnedBytes),
}
impl From<Result<Vec<(merkle::Key, merkle::Value)>, api::Error>> for KeyValueBatchResult {
fn from(value: Result<Vec<(merkle::Key, merkle::Value)>, api::Error>) -> Self {
match value {
Ok(pairs) => {
let values: Vec<_> = pairs.into_iter().map(Into::into).collect();
KeyValueBatchResult::Some(values.into())
}
Err(err) => KeyValueBatchResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl<'db> From<CreateIteratorResult<'db>> for IteratorResult<'db> {
fn from(value: CreateIteratorResult<'db>) -> Self {
IteratorResult::Ok {
handle: Box::new(value.0),
}
}
}
impl<'db, E: fmt::Display> From<Result<CreateIteratorResult<'db>, E>> for IteratorResult<'db> {
fn from(value: Result<CreateIteratorResult<'db>, E>) -> Self {
match value {
Ok(res) => res.into(),
Err(err) => IteratorResult::Err(err.to_string().into_bytes().into()),
}
}
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum RevisionResult<'db> {
NullHandlePointer,
RevisionNotFound(HashKey),
Ok {
handle: Box<RevisionHandle<'db>>,
root_hash: HashKey,
},
Err(OwnedBytes),
}
#[derive(Debug)]
#[repr(C, usize)]
pub enum ReconstructedResult<'db> {
NullHandlePointer,
Ok {
handle: Box<ReconstructedHandle<'db>>,
},
Err(OwnedBytes),
}
impl<'db> From<GetRevisionResult<'db>> for RevisionResult<'db> {
fn from(value: GetRevisionResult<'db>) -> Self {
RevisionResult::Ok {
handle: Box::new(value.handle),
root_hash: HashKey::from(value.root_hash),
}
}
}
impl<'db> From<Result<GetRevisionResult<'db>, api::Error>> for RevisionResult<'db> {
fn from(value: Result<GetRevisionResult<'db>, api::Error>) -> Self {
match value {
Ok(res) => res.into(),
Err(api::Error::RevisionNotFound { provided }) => RevisionResult::RevisionNotFound(
HashKey::from(provided.unwrap_or_else(api::HashKey::empty)),
),
Err(err) => RevisionResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl<'db, E: fmt::Display> From<Result<CreateProposalResult<'db>, E>> for ProposalResult<'db> {
fn from(value: Result<CreateProposalResult<'db>, E>) -> Self {
match value {
Ok(CreateProposalResult { handle, .. }) => ProposalResult::Ok {
root_hash: handle.hash_key().unwrap_or_default(),
handle: Box::new(handle),
},
Err(err) => ProposalResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl<'db, E: fmt::Display> From<Result<ReconstructedHandle<'db>, E>> for ReconstructedResult<'db> {
fn from(value: Result<ReconstructedHandle<'db>, E>) -> Self {
match value {
Ok(handle) => ReconstructedResult::Ok {
handle: Box::new(handle),
},
Err(err) => ReconstructedResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl From<Result<api::FrozenChangeProof, api::Error>> for ChangeProofResult {
fn from(value: Result<api::FrozenChangeProof, api::Error>) -> Self {
match value {
Ok(proof) => ChangeProofResult::Ok(Box::new(proof.into())),
Err(api::Error::StartRevisionNotFound { provided }) => {
ChangeProofResult::StartRevisionNotFound(HashKey::from(
provided.unwrap_or_else(api::HashKey::empty),
))
}
Err(api::Error::EndRevisionNotFound { provided }) => {
ChangeProofResult::EndRevisionNotFound(HashKey::from(
provided.unwrap_or_else(api::HashKey::empty),
))
}
Err(err) => ChangeProofResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl From<Result<VerifiedChangeProofContext, api::Error>> for VerifiedChangeProofResult {
fn from(value: Result<VerifiedChangeProofContext, api::Error>) -> Self {
match value {
Ok(context) => VerifiedChangeProofResult::Ok(Box::new(context)),
Err(err) => VerifiedChangeProofResult::Err(err.to_string().into_bytes().into()),
}
}
}
impl<'db> From<Result<ProposedChangeProofContext<'db>, api::Error>>
for ProposedChangeProofResult<'db>
{
fn from(value: Result<ProposedChangeProofContext<'db>, api::Error>) -> Self {
match value {
Ok(context) => ProposedChangeProofResult::Ok(Box::new(context)),
Err(err) => ProposedChangeProofResult::Err(err.to_string().into_bytes().into()),
}
}
}
pub(crate) trait NullHandleResult: CResult {
fn null_handle_pointer_error() -> Self;
}
pub(crate) trait CResult: Sized {
#[cfg(panic = "unwind")]
fn from_err(err: impl ToString) -> Self;
#[cfg(panic = "unwind")]
fn from_panic(panic: Box<dyn std::any::Any + Send>) -> Self
where
Self: Sized,
{
Self::from_err(Panic::from(panic))
}
}
macro_rules! impl_null_handle_result {
($($Enum:ty),* $(,)?) => {
$(
impl NullHandleResult for $Enum {
fn null_handle_pointer_error() -> Self {
Self::NullHandlePointer
}
}
)*
};
}
macro_rules! impl_cresult {
($($Enum:ty),* $(,)?) => {
$(
impl CResult for $Enum {
#[cfg(panic = "unwind")]
fn from_err(err: impl ToString) -> Self {
Self::Err(err.to_string().into_bytes().into())
}
}
)*
};
}
impl_null_handle_result!(
VoidResult,
ValueResult,
HashResult,
RangeProofResult<'_>,
ChangeProofResult,
VerifiedChangeProofResult,
ProposedChangeProofResult<'_>,
NextKeyRangeResult,
CodeIteratorResult<'_>,
ProposalResult<'_>,
ReconstructedResult<'_>,
IteratorResult<'_>,
RevisionResult<'_>,
KeyValueBatchResult,
KeyValueResult,
);
impl_cresult!(
VoidResult,
ValueResult,
HashResult,
HandleResult,
RangeProofResult<'_>,
ChangeProofResult,
VerifiedChangeProofResult,
ProposedChangeProofResult<'_>,
NextKeyRangeResult,
CodeIteratorResult<'_>,
ProposalResult<'_>,
ReconstructedResult<'_>,
IteratorResult<'_>,
RevisionResult<'_>,
KeyValueBatchResult,
KeyValueResult,
);
#[cfg(panic = "unwind")]
enum Panic {
Static(&'static str),
Formatted(String),
SendSyncErr(Box<dyn std::error::Error + Send + Sync>),
SendErr(Box<dyn std::error::Error + Send>),
Unknown(#[expect(unused)] Box<dyn std::any::Any + Send>),
}
#[cfg(panic = "unwind")]
impl From<Box<dyn std::any::Any + Send>> for Panic {
fn from(panic: Box<dyn std::any::Any + Send>) -> Self {
macro_rules! downcast {
($Variant:ident($panic:ident)) => {
let $panic = match $panic.downcast() {
Ok(panic) => return Panic::$Variant(*panic),
Err(panic) => panic,
};
};
}
downcast!(Static(panic));
downcast!(Formatted(panic));
downcast!(SendSyncErr(panic));
downcast!(SendErr(panic));
Self::Unknown(panic)
}
}
#[cfg(panic = "unwind")]
impl fmt::Display for Panic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Panic::Static(msg) => f.pad(msg),
Panic::Formatted(msg) => f.pad(msg),
Panic::SendSyncErr(err) => err.fmt(f),
Panic::SendErr(err) => err.fmt(f),
Panic::Unknown(_) => f.pad("unknown panic type recovered"),
}
}
}