scale_decode/error/
context.rs

1// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-decode 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
16//! This module provides a [`Context`] type, which tracks the path
17//! that we're attempting to encode to aid in error reporting.
18
19use alloc::{borrow::Cow, vec::Vec};
20
21/// A cheaply clonable opaque context which allows us to track the current
22/// location into a type that we're trying to encode, to aid in
23/// error reporting.
24#[derive(Clone, Default, Debug)]
25pub struct Context {
26    path: Vec<Location>,
27}
28
29impl Context {
30    /// Construct a new, empty context.
31    pub fn new() -> Context {
32        Default::default()
33    }
34    /// Return a new context with the given location appended.
35    pub fn push(&mut self, loc: Location) {
36        self.path.push(loc);
37    }
38    /// Return the current path.
39    pub fn path(&self) -> Path<'_> {
40        Path(Cow::Borrowed(&self.path))
41    }
42}
43
44/// The current path that we're trying to encode.
45pub struct Path<'a>(Cow<'a, Vec<Location>>);
46
47impl<'a> Path<'a> {
48    /// Cheaply convert the path to an owned version.
49    pub fn into_owned(self) -> Path<'static> {
50        Path(Cow::Owned(self.0.into_owned()))
51    }
52    /// Return each location visited, oldest first
53    pub fn locations(&self) -> impl Iterator<Item = &Location> {
54        self.0.iter()
55    }
56}
57
58impl<'a> core::fmt::Display for Path<'a> {
59    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60        for (idx, loc) in self.0.iter().enumerate() {
61            if idx != 0 {
62                f.write_str(".")?;
63            }
64            match &loc.inner {
65                Loc::Field(name) => f.write_str(name)?,
66                Loc::Index(i) => write!(f, "[{i}]")?,
67                Loc::Variant(name) => write!(f, "({name})")?,
68            }
69        }
70        Ok(())
71    }
72}
73
74/// Some location, like a field, variant or index in an array.
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct Location {
77    inner: Loc,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
81enum Loc {
82    Field(Cow<'static, str>),
83    Index(usize),
84    Variant(Cow<'static, str>),
85}
86
87impl Location {
88    /// This represents some struct field.
89    pub fn field(name: impl Into<Cow<'static, str>>) -> Self {
90        Location { inner: Loc::Field(name.into()) }
91    }
92    /// This represents some variant name.
93    pub fn variant(name: impl Into<Cow<'static, str>>) -> Self {
94        Location { inner: Loc::Variant(name.into()) }
95    }
96    /// This represents a tuple or array index.
97    pub fn idx(i: usize) -> Self {
98        Location { inner: Loc::Index(i) }
99    }
100}