scale_value/scale_impls/tracing_decoder/
error.rs

1// Copyright (C) 2022-2024 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-value crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::path::Path;
17use crate::prelude::*;
18use crate::scale::DecodeError;
19use crate::string_impls::format_hex;
20use crate::Value;
21use core::fmt::Write;
22
23/// An error encountered when decoding some bytes using the [`crate::scale::tracing`] module.
24#[derive(Clone, Debug)]
25pub struct TraceDecodingError<Val> {
26    inner: TraceDecodingErrorInner<Val>,
27}
28
29impl<Val> TraceDecodingError<Val> {
30    pub(crate) fn map_decoded_so_far<NewVal>(
31        self,
32        f: impl FnOnce(Val) -> NewVal,
33    ) -> TraceDecodingError<NewVal> {
34        match self.inner {
35            TraceDecodingErrorInner::FromDecodeError(e) => {
36                TraceDecodingErrorInner::FromDecodeError(e).into()
37            }
38            TraceDecodingErrorInner::FromVisitor(e) => {
39                TraceDecodingErrorInner::FromVisitor(VisitorError {
40                    at: e.at,
41                    decode_error: e.decode_error,
42                    decoded_so_far: f(e.decoded_so_far),
43                })
44                .into()
45            }
46        }
47    }
48    pub(crate) fn with_outer_context<NewVal>(
49        self,
50        outer_path: impl FnOnce() -> Path,
51        default_outer_value: impl FnOnce() -> NewVal,
52        into_outer_value: impl FnOnce(Val) -> NewVal,
53    ) -> TraceDecodingError<NewVal> {
54        match self.inner {
55            TraceDecodingErrorInner::FromDecodeError(e) => {
56                TraceDecodingErrorInner::FromVisitor(VisitorError {
57                    at: outer_path(),
58                    decoded_so_far: default_outer_value(),
59                    decode_error: e,
60                })
61                .into()
62            }
63            TraceDecodingErrorInner::FromVisitor(e) => {
64                TraceDecodingErrorInner::FromVisitor(VisitorError {
65                    at: e.at,
66                    decoded_so_far: into_outer_value(e.decoded_so_far),
67                    decode_error: e.decode_error,
68                })
69                .into()
70            }
71        }
72    }
73}
74
75impl<Val> From<TraceDecodingErrorInner<Val>> for TraceDecodingError<Val> {
76    fn from(value: TraceDecodingErrorInner<Val>) -> Self {
77        TraceDecodingError { inner: value }
78    }
79}
80
81#[derive(Clone, Debug)]
82enum TraceDecodingErrorInner<Val> {
83    FromDecodeError(DecodeError),
84    FromVisitor(VisitorError<Val>),
85}
86
87#[derive(Clone, Debug)]
88struct VisitorError<Val> {
89    at: Path,
90    decoded_so_far: Val,
91    decode_error: DecodeError,
92}
93
94impl<Ctx: core::fmt::Debug> core::fmt::Display for TraceDecodingError<Value<Ctx>> {
95    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96        match &self.inner {
97            TraceDecodingErrorInner::FromDecodeError(e) => {
98                write!(f, "Error decoding value: {e}")
99            }
100            TraceDecodingErrorInner::FromVisitor(e) => {
101                write!(
102                    f,
103                    "Error decoding value at {}: {}\nDecoded so far:\n\n",
104                    e.at, e.decode_error,
105                )?;
106                display_value_with_typeid(f, &e.decoded_so_far)
107            }
108        }
109    }
110}
111
112impl<Ctx: core::fmt::Debug> core::error::Error for TraceDecodingError<Value<Ctx>> {}
113
114impl<TypeId> From<DecodeError> for TraceDecodingError<TypeId> {
115    fn from(value: DecodeError) -> Self {
116        TraceDecodingErrorInner::FromDecodeError(value).into()
117    }
118}
119
120impl<TypeId> From<codec::Error> for TraceDecodingError<TypeId> {
121    fn from(value: codec::Error) -> Self {
122        TraceDecodingErrorInner::FromDecodeError(value.into()).into()
123    }
124}
125
126fn display_value_with_typeid<Id: core::fmt::Debug>(
127    f: &mut core::fmt::Formatter<'_>,
128    value: &Value<Id>,
129) -> core::fmt::Result {
130    crate::string_impls::ToWriterBuilder::new()
131        .pretty()
132        .format_context(|type_id, writer: &mut &mut core::fmt::Formatter| {
133            write!(writer, "{type_id:?}")
134        })
135        .add_custom_formatter(|value, writer| format_hex(value, writer))
136        .write(value, f)
137}