frame_decode/utils/
decode_with_error_tracing.rs1use alloc::format;
17use alloc::string::String;
18
19pub fn decode_with_error_tracing<'scale, 'resolver, Resolver, Id, V>(
24 cursor: &mut &'scale [u8],
25 type_id: Id,
26 types: &'resolver Resolver,
27 visitor: V,
28) -> Result<V::Value<'scale, 'resolver>, DecodeErrorTrace>
29where
30 Resolver: scale_type_resolver::TypeResolver<TypeId = Id>,
31 Id: core::fmt::Debug + Clone,
32 V: scale_decode::Visitor<TypeResolver = Resolver>,
33 V::Error: core::fmt::Debug,
34{
35 let initial = *cursor;
36 match scale_decode::visitor::decode_with_visitor(cursor, type_id.clone(), types, visitor) {
37 Ok(value) => Ok(value),
38 #[cfg(not(feature = "error-tracing"))]
40 Err(e) => {
41 *cursor = initial;
42
43 Err(DecodeErrorTrace {
44 original_error: format!("{e:?}"),
45 tracing_error: String::new(),
46 })
47 }
48 #[cfg(feature = "error-tracing")]
50 Err(e) => {
51 *cursor = initial;
54 let res = scale_value::scale::tracing::decode_as_type(cursor, type_id.clone(), types)
55 .map(|v| v.map_context(|id| format!("{id:?}")))
56 .map_err(|te| DecodeErrorTrace {
57 original_error: format!("{e:?}"),
58 tracing_error: alloc::string::ToString::to_string(&te),
59 })?;
60
61 use core::fmt::Write;
63 let mut res_string = String::new();
64 write!(
65 &mut res_string,
66 "Failed to decode value with custom visitor (but tracing decoded it):\n\n"
67 )
68 .unwrap();
69
70 scale_value::stringify::to_writer_custom()
71 .pretty()
72 .format_context(|type_id, w: &mut &mut String| write!(w, "{type_id}"))
73 .add_custom_formatter(|v, w| {
74 scale_value::stringify::custom_formatters::format_hex(v, w)
75 })
76 .add_custom_formatter(|v, w| {
77 if let scale_value::ValueDef::Composite(scale_value::Composite::Unnamed(vals)) =
79 &v.value
80 {
81 let are_primitive = vals
82 .iter()
83 .all(|val| matches!(val.value, scale_value::ValueDef::Primitive(_)));
84 if are_primitive {
85 return Some(write!(w, "{v}"));
86 }
87 }
88 None
89 })
90 .write(&res, &mut res_string)
91 .expect("writing to string should always succeed");
92
93 Err(DecodeErrorTrace {
94 original_error: format!("{e:?}"),
95 tracing_error: res_string,
96 })
97 }
98 }
99}
100
101#[derive(Clone, Debug, thiserror::Error)]
103pub struct DecodeErrorTrace {
104 original_error: String,
105 tracing_error: String,
106}
107
108impl core::fmt::Display for DecodeErrorTrace {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110 let DecodeErrorTrace {
111 original_error,
112 tracing_error,
113 } = self;
114
115 write!(f, "{original_error}")?;
116 if !tracing_error.is_empty() {
117 write!(f, ":\n\n{tracing_error}")?;
118 }
119 Ok(())
120 }
121}