use crate::xdr::*;
use core::fmt::{self, Debug};
#[cfg(not(target_arch = "riscv32"))]
use core::error::Error;
#[cfg(not(target_arch = "riscv32"))]
use core::fmt::Display;
#[cfg(feature = "dev")]
use thruput_native_exec::invoke_error::{PanicLocation, PanicRecord};
#[cfg(not(target_arch = "riscv32"))]
const CAP: usize = 500;
#[cfg(not(target_arch = "riscv32"))]
const NO_MESSAGE: &str = "<fault>";
#[derive(Clone, Default)]
pub struct FaultString {
#[cfg(not(target_arch = "riscv32"))]
msg: Option<heapless::String<CAP>>,
}
impl FaultString {
#[cfg(not(target_arch = "riscv32"))]
pub fn as_str(&self) -> &str {
self.msg.as_ref().map(|s| s.as_str()).unwrap_or(NO_MESSAGE)
}
#[inline]
pub fn empty() -> Self {
Self::default()
}
#[cfg(not(target_arch = "riscv32"))]
pub fn via_display<V>(v: &V) -> Self
where
V: Display + ?Sized,
{
use core::fmt::Write;
let mut s = heapless::String::new();
write!(s, "{}", v).unwrap_or_else(|_| {
s.clear();
let _ = write!(s, "<too-big>");
});
Self { msg: Some(s) }
}
#[cfg(target_arch = "riscv32")]
pub fn via_display<V: ?Sized>(_v: &V) -> Self {
Self {}
}
#[cfg(not(target_arch = "riscv32"))]
pub fn from_display(s: impl Display) -> Self {
Self::via_display(&s)
}
#[cfg(target_arch = "riscv32")]
pub fn from_display<S>(_s: S) -> Self {
Self {}
}
}
impl PartialEq for FaultString {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl Eq for FaultString {}
#[cfg(not(target_arch = "riscv32"))]
impl Debug for FaultString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("FaultString").field(&self.as_str()).finish()
}
}
#[cfg(target_arch = "riscv32")]
impl Debug for FaultString {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
#[cfg(not(target_arch = "riscv32"))]
impl Error for FaultString {}
#[cfg(not(target_arch = "riscv32"))]
impl Display for FaultString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(target_arch = "riscv32")]
impl core::fmt::Display for FaultString {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
impl WriteXDR for FaultString {
fn write_xdr<F, W: xdrs::Writer<F>>(
&self,
_w: &mut W,
) -> xdrs::WriteResult<()> {
Ok(())
}
unsafe fn write_xdr_unchecked<F, W: xdrs::WriterUnchecked<F>>(
&self,
_w: &mut W,
) {
}
}
impl<'r> ReadXDR<'r> for FaultString {
fn read_xdr<R: xdrs::Reader<'r>>(_r: &mut R) -> xdrs::ReadResult<Self> {
Ok(Self::empty())
}
}
impl xdrs::cxs::ConstMaxSize for FaultString {
const MAX_SIZE: Option<usize> = Some(0);
}
impl xdrs::txs::ConstMaxSize for FaultString {
const MAX_SIZE: Option<usize> = Some(0);
}
#[cfg(target_arch = "riscv32")]
const _: () = assert!(core::mem::size_of::<FaultString>() == 0);
const _: () = assert!(matches!(
<FaultString as xdrs::cxs::ConstMaxSize>::MAX_SIZE,
Some(0)
));
impl crate::__contract_spec::ContractTypeName for FaultString {
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: "FaultString",
};
}
impl crate::__contract_spec::ContractTypeSpec for FaultString {
const TYPE_DEF: crate::__contract_spec::ContractTypeDefView<'static> =
crate::__contract_spec::ContractTypeDefView::Struct(
crate::__contract_spec::ContractStructDefView {
name: <Self as crate::__contract_spec::ContractTypeName>::NAME,
fields: &[],
},
);
}
impl crate::__contract_spec::ContractTypeRefSpec for FaultString {
const INFO: crate::__contract_spec::ContractTypeInfo<'static> =
crate::__contract_spec::ContractTypeInfo {
ty: crate::__contract_spec::ContractTypeView::Named(
<Self as crate::__contract_spec::ContractTypeName>::NAME,
),
};
const __THRUPUT_SPEC_ANCHOR_ROOT: fn() -> usize =
Self::__thruput_spec_anchor;
#[cfg_attr(target_arch = "riscv32", inline(never))]
#[cfg_attr(
target_arch = "riscv32",
unsafe(link_section = "thruputspecanchortextv0")
)]
fn __thruput_spec_anchor() -> usize {
0
}
}
#[doc(hidden)]
#[allow(non_upper_case_globals)]
pub const __thruput_spec_fragment_FaultString:
crate::__contract_spec::SpecFragmentView<'static> =
crate::__contract_spec::SpecFragmentView::TypeDef(
<FaultString as crate::__contract_spec::ContractTypeSpec>::TYPE_DEF,
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encodes_to_nothing_whatever_the_message() {
for fs in [
FaultString::empty(),
FaultString::from_display("boom"),
FaultString::from_display(
core::str::from_utf8(&[b'x'; 500]).unwrap(),
),
] {
assert!(txs::encode(&fs).unwrap().is_empty());
}
}
#[test]
fn decodes_from_nothing() {
let s: FaultString = txs::decode(&[]).unwrap();
assert_eq!(s.as_str(), "<fault>");
}
#[test]
fn message_is_readable_host_side() {
assert_eq!(FaultString::from_display("boom").as_str(), "boom");
assert_eq!(FaultString::empty().as_str(), "<fault>");
}
#[test]
fn oversized_message_is_marked() {
let long = core::str::from_utf8(&[b'x'; CAP + 1]).unwrap();
assert_eq!(FaultString::from_display(long).as_str(), "<too-big>");
}
}
#[cfg_attr(not(target_arch = "riscv32"), derive(Debug))]
#[derive(Clone, Default)]
pub struct FaultInfo {
#[cfg(feature = "dev")]
record: PanicRecord,
}
impl FaultInfo {
#[inline]
pub fn empty() -> Self {
Self::default()
}
#[cfg(not(target_arch = "riscv32"))]
pub fn as_str(&self) -> &str {
#[cfg(feature = "dev")]
if let Some(message) = self.record.message.as_deref() {
return message;
}
NO_MESSAGE
}
#[cfg(feature = "dev")]
pub fn location(&self) -> Option<&PanicLocation> {
self.record.location.as_ref()
}
}
impl PartialEq for FaultInfo {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl Eq for FaultInfo {}
#[cfg(not(target_arch = "riscv32"))]
impl Display for FaultInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())?;
#[cfg(feature = "dev")]
if let Some(location) = self.location() {
write!(f, " at {location}")?;
}
Ok(())
}
}
#[cfg(target_arch = "riscv32")]
impl Debug for FaultInfo {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
#[cfg(not(target_arch = "riscv32"))]
impl Error for FaultInfo {}
#[cfg(feature = "dev")]
impl From<&PanicRecord> for FaultInfo {
fn from(record: &PanicRecord) -> Self {
Self {
record: record.clone(),
}
}
}
#[cfg(not(feature = "dev"))]
const _: () = assert!(core::mem::size_of::<FaultInfo>() == 0);