pub use gcore::errors::{Error as CoreError, *};
pub use scale_info::scale::Error as CodecError;
use crate::ActorId;
use alloc::vec::Vec;
use core::{fmt, str};
use gprimitives::utils::ByteSliceFormatter;
use parity_scale_codec::Decode;
pub type Result<T, E = Error> = core::result::Result<T, E>;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Core(#[from] CoreError),
#[error("Conversion error: {0}")]
Convert(#[from] ConversionError),
#[error("Scale codec decoding error: {0}")]
Decode(CodecError),
#[error("Gstd API error: {0}")]
Gstd(#[from] UsageError),
#[error("Received error reply '{0}' due to {1}")]
ErrorReply(ErrorReplyPayload, ErrorReplyReason),
#[error("Received unsupported reply '{hex}'", hex = ByteSliceFormatter::Dynamic(.0))]
UnsupportedReply(Vec<u8>),
#[error("Timeout has occurred: expected at {0}, now {1}")]
Timeout(u32, u32),
}
impl Error {
pub fn timed_out(&self) -> bool {
matches!(self, Error::Timeout(..))
}
pub fn error_reply_panic<T: Decode>(&self) -> Option<Result<T, Self>> {
self.error_reply_panic_bytes()
.map(|mut bytes| T::decode(&mut bytes).map_err(Error::Decode))
}
pub fn error_reply_panic_bytes(&self) -> Option<&[u8]> {
if let Self::ErrorReply(
payload,
ErrorReplyReason::Execution(SimpleExecutionError::UserspacePanic),
) = self
{
Some(&payload.0)
} else {
None
}
}
pub fn error_reply_exit_inheritor(&self) -> Option<ActorId> {
if let Self::ErrorReply(
payload,
ErrorReplyReason::UnavailableActor(SimpleUnavailableActorError::ProgramExited),
) = self
{
let id = ActorId::try_from(payload.0.as_slice())
.unwrap_or_else(|e| unreachable!("protocol always returns valid `ActorId`: {e}"));
Some(id)
} else {
None
}
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ErrorReplyPayload(pub Vec<u8>);
impl ErrorReplyPayload {
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
pub fn try_as_str(&self) -> Option<&str> {
str::from_utf8(&self.0).ok()
}
pub fn into_inner(self) -> Vec<u8> {
self.0
}
}
impl From<Vec<u8>> for ErrorReplyPayload {
fn from(value: Vec<u8>) -> Self {
Self(value)
}
}
impl fmt::Debug for ErrorReplyPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.try_as_str()
.map(|v| write!(f, "{v}"))
.unwrap_or_else(|| write!(f, "{}", ByteSliceFormatter::Dynamic(&self.0)))
}
}
impl fmt::Display for ErrorReplyPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum UsageError {
#[error("Wait duration can not be zero")]
EmptyWaitDuration,
#[error("System reservation amount can not be zero in config")]
ZeroSystemReservationAmount,
#[error("Mutex lock duration can not be zero")]
ZeroMxLockDuration,
#[error("Reply deposit can not be zero when setting reply hook")]
ZeroReplyDeposit,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{format, vec};
#[test]
fn error_unsupported_reply_display() {
let payload = Error::UnsupportedReply(vec![1, 2, 3]);
assert_eq!(
format!("{payload}"),
"Received unsupported reply '0x010203'"
);
}
}