use super::fault::*;
use super::{AuthTokenError, CallingError, HandlingError};
use crate::ContractTypeSpec;
use crate::call::CallFault;
use crate::sys::{SysErr, SyscallErrorCode, SyscallNo};
use crate::xdr::*;
use core::error::Error;
use core::fmt::{self, Display};
#[cfg(not(target_arch = "riscv32"))]
use thiserror::Error;
#[derive(Debug, PartialEq, ReadXDR, WriteXDR, ConstXDRSize)]
#[cfg_attr(not(target_arch = "riscv32"), derive(Error))]
#[derive(ContractTypeSpec)]
#[thruput_macros::exn(version = "1.0.0", wire_crate = "sdk")]
#[repr(i32)]
#[non_exhaustive]
pub enum LibraryExn {
#[cfg_attr(not(target_arch = "riscv32"), error(transparent))]
Syscall(SyscallExn) = 0,
#[cfg_attr(
not(target_arch = "riscv32"),
error("Handling a contract call: {0}")
)]
Handling(HandlingError) = 1,
#[cfg_attr(
not(target_arch = "riscv32"),
error("Making a contract call: {0}")
)]
Calling(CallingError) = 2,
#[cfg_attr(
not(target_arch = "riscv32"),
error("Called contract reverted: {0}")
)]
Called(#[cfg_attr(not(target_arch = "riscv32"), source)] FaultString) = 3,
#[cfg_attr(
not(target_arch = "riscv32"),
error("Encoding auth token: {0}")
)]
AuthToken(AuthTokenError) = 4,
#[cfg_attr(
not(target_arch = "riscv32"),
error("bytestring_get_sized: expected {expected}, found {found}")
)]
BytestringWrongSize { expected: u32, found: u32 } = 5,
#[cfg_attr(
not(target_arch = "riscv32"),
error("non-negative int went negative")
)]
NonNegativeIntUnderflow = 6,
#[cfg_attr(
not(target_arch = "riscv32"),
error("non-negative int overflow")
)]
NonNegativeIntOverflow = 7,
#[cfg_attr(
not(target_arch = "riscv32"),
error("storage value failed to (de)serialize")
)]
StorageSerialization = 8,
#[cfg_attr(
not(target_arch = "riscv32"),
error("storage index out of bounds: len={len}, access={access}")
)]
StorageIndexOutOfBounds { len: u64, access: u64 } = 9,
#[cfg_attr(
not(target_arch = "riscv32"),
error("storage keypath component is too long to encode")
)]
StorageKeypathOverflow = 10,
#[cfg_attr(
not(target_arch = "riscv32"),
error(
"retrocausal queue entry: expected {expected} bytes, \
found {found}"
)
)]
RetrocausalQueueEntryWrongSize { expected: u32, found: u32 } = 11,
#[cfg_attr(
not(target_arch = "riscv32"),
error(
"capacity constraint: set_capacity conflicts with a prior \
write this transaction"
)
)]
CapacityConstraintAlreadyWritten = 12,
#[cfg_attr(
not(target_arch = "riscv32"),
error("no entrypoint on this contract matches the invoked method")
)]
UnknownMethod = 13,
}
impl From<HandlingError> for LibraryExn {
fn from(e: HandlingError) -> Self {
Self::Handling(e)
}
}
impl From<CallingError> for LibraryExn {
fn from(e: CallingError) -> Self {
Self::Calling(e)
}
}
impl From<AuthTokenError> for LibraryExn {
fn from(e: AuthTokenError) -> Self {
Self::AuthToken(e)
}
}
impl crate::re_throw::ReThrow for LibraryExn {
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
fn re_throw(self) -> ! {
library_throw(self)
}
}
impl LibraryExn {
pub fn as_syscall_exn(&self) -> Option<&SyscallExn> {
if let LibraryExn::Syscall(se) = self {
Some(se)
} else {
None
}
}
pub fn is_storage_missing(&self) -> bool {
match self.as_syscall_exn() {
Some(se) => {
se.has_code(
SyscallErrorCode::kSyscallErrorCodeStorageKeyNotExist,
) || se.has_code(
SyscallErrorCode::kSyscallErrorCodeNoRetrocausalQueueEntry,
)
}
None => false,
}
}
}
pub trait NoneIfMissing<T> {
fn none_if_missing(self) -> Option<T>;
}
impl<T> NoneIfMissing<T> for Result<T, LibraryExn> {
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
fn none_if_missing(self) -> Option<T> {
use crate::re_throw::ReThrow;
match self {
Ok(v) => Some(v),
Err(e) if e.is_storage_missing() => None,
Err(e) => e.re_throw(),
}
}
}
impl crate::__contract_spec::ContractTypeName for LibraryExn {
const NAME: crate::__contract_spec::ContractSpecIdView<'static> =
crate::__contract_spec::ContractSpecIdView {
crate_name: ::core::env!("CARGO_PKG_NAME"),
major: crate::exn::EXN_ABI_MAJOR,
module_path: ::core::module_path!(),
declared: "LibraryExn",
};
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn library_throw<E: Into<LibraryExn> + core::fmt::Debug>(e: E) -> ! {
super::abort_encode::library_abort(&e.into())
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn client_library_throw<E: Into<LibraryExn> + core::fmt::Debug>(
e: E,
) -> ! {
super::abort_encode::client_library_abort(&e.into())
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn syscall_throw(callno: SyscallNo, errno: SysErr) -> ! {
library_throw(syscall_exn(callno, errno))
}
pub fn syscall_exn(callno: SyscallNo, errno: SysErr) -> LibraryExn {
LibraryExn::Syscall(SyscallExn::new(callno, errno))
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn nnint_overflow() -> ! {
library_throw(LibraryExn::NonNegativeIntOverflow)
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn nnint_underflow() -> ! {
library_throw(LibraryExn::NonNegativeIntUnderflow)
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn decoding_args_throw(e: &CallFault) -> ! {
library_throw(HandlingError::DecodingArgs(FaultString::from_display(e)))
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn encoding_success_throw<E: Error>(e: E) -> ! {
library_throw(HandlingError::EncodingSuccess(FaultString::from_display(e)))
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn encoding_exception_throw<E: Error>(e: E) -> ! {
library_throw(HandlingError::EncodingException(FaultString::from_display(
e,
)))
}
#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
pub fn encoding_event_throw<E: Display>(e: E) -> ! {
library_throw(HandlingError::EncodingEvent(FaultString::from_display(e)))
}
#[derive(Debug, Clone, Copy, ReadXDR, WriteXDR, PartialEq, ConstXDRSize)]
#[cfg_attr(not(target_arch = "riscv32"), derive(Error))]
#[derive(ContractTypeSpec)]
pub struct SyscallExn {
callno: SyscallNo,
errno: SysErr,
}
impl SyscallExn {
pub fn new(callno: SyscallNo, errno: SysErr) -> Self {
Self { callno, errno }
}
pub fn callno(&self) -> SyscallNo {
self.callno
}
pub fn has_code(&self, err_code: SyscallErrorCode) -> bool {
self.errno.is_code(err_code)
}
pub fn is(&self, call_no: SyscallNo, err_code: SyscallErrorCode) -> bool {
self.callno == call_no && self.errno == err_code.into()
}
}
impl Display for SyscallExn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Syscall {:?} failed with {:?}", self.callno, self.errno)
}
}
impl crate::__contract_spec::ContractTypeName for SyscallExn {
const NAME: crate::__contract_spec::ContractSpecIdView<'static> =
crate::__contract_spec::ContractSpecIdView {
crate_name: ::core::env!("CARGO_PKG_NAME"),
major: crate::exn::EXN_ABI_MAJOR,
module_path: ::core::module_path!(),
declared: "SyscallExn",
};
}
#[cfg(test)]
mod selector_tests {
use super::*;
use crate::exn::Exn;
#[test]
fn library_exn_selector_matches_sha256() {
let expected = thruput_contract_util::exception_selector(
"1.0.0",
"sdk",
"LibraryExn",
);
assert_eq!(LibraryExn::SELECTOR, expected);
}
#[test]
fn library_exn_selector_nonzero() {
assert_ne!(LibraryExn::SELECTOR, 0);
}
}