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}