sui_display/v2/
error.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::BTreeSet;
5use std::fmt;
6use std::mem;
7use std::sync::Arc;
8
9use move_core_types::annotated_visitor as AV;
10use move_core_types::language_storage::TypeTag;
11use sui_types::object::option_visitor as OV;
12use sui_types::object::rpc_visitor as RV;
13
14use crate::v2::lexer::Lexeme;
15use crate::v2::lexer::OwnedLexeme;
16use crate::v2::lexer::Token;
17use crate::v2::peek::Peekable2Ext;
18
19/// Errors related to the display format as a whole.
20///
21/// NB. Limit errors (`Too*`) are duplicated here and in `FormatError` because they occur while
22/// working on a format, and need to be propagated up to the Display overall.
23#[derive(thiserror::Error, Debug)]
24pub enum Error {
25    #[error("Duplicate name {0:?}")]
26    NameDuplicate(String),
27
28    #[error("Name pattern {0:?} produced no output")]
29    NameEmpty(String),
30
31    #[error("Name pattern {0:?} did not evaluate to a string")]
32    NameInvalid(String),
33
34    #[error("Error evaluating name pattern {0:?}: {1}")]
35    NameEvaluation(String, FormatError),
36
37    #[error("Display contains too many elements")]
38    TooBig,
39
40    #[error("Display tries to load too many objects")]
41    TooManyLoads,
42
43    #[error("Display produces too much output")]
44    TooMuchOutput,
45}
46
47/// Errors related to a single format string.
48#[derive(thiserror::Error, Debug, Clone)]
49pub enum FormatError {
50    #[error("BCS error: {0}")]
51    Bcs(#[from] bcs::Error),
52
53    #[error("Hex {0} contains invalid character")]
54    InvalidHexCharacter(OwnedLexeme),
55
56    #[error("Invalid {0}")]
57    InvalidIdentifier(OwnedLexeme),
58
59    #[error("Invalid {what} at byte offset {offset}: {err}")]
60    InvalidNumber {
61        what: &'static str,
62        offset: usize,
63        err: String,
64    },
65
66    #[error("Odd number of characters in hex {0}")]
67    OddHexLiteral(OwnedLexeme),
68
69    #[error("Storage error: {0}")]
70    Store(Arc<anyhow::Error>),
71
72    #[error("Display contains too many elements")]
73    TooBig,
74
75    #[error("Format is nested too deeply")]
76    TooDeep,
77
78    #[error("Display tries to load too many objects")]
79    TooManyLoads,
80
81    #[error("Display produces too much output")]
82    TooMuchOutput,
83
84    #[error("Invalid transform: {0}")]
85    TransformInvalid(&'static str),
86
87    /// The above error augmented with the offset of the originating expression.
88    #[error("Invalid transform for expression at byte offset {offset}: {reason}")]
89    TransformInvalid_ { offset: usize, reason: &'static str },
90
91    #[error("Unexpected end-of-string, expected {expect}")]
92    UnexpectedEos { expect: ExpectedSet },
93
94    #[error("Unexpected {0}, expected end-of-string")]
95    UnexpectedRemaining(OwnedLexeme),
96
97    #[error("Unexpected {actual}, expected {expect}")]
98    UnexpectedToken {
99        actual: OwnedLexeme,
100        expect: ExpectedSet,
101    },
102
103    #[error("Vector at byte offset {offset} requires 1 type parameter, found {arity}")]
104    VectorArity { offset: usize, arity: usize },
105
106    #[error("Internal error: vector without element type")]
107    VectorNoType,
108
109    #[error(
110        "Vector at byte offset {offset}, could have element type {} or {}",
111        .this.to_canonical_display(true),
112        .that.to_canonical_display(true),
113    )]
114    VectorTypeMismatch {
115        offset: usize,
116        this: TypeTag,
117        that: TypeTag,
118    },
119
120    #[error("Deserialization error: {0}")]
121    Visitor(#[from] AV::Error),
122}
123
124/// The set of patterns that the parser tried to match against the next token, in a given
125/// invocation of `match_token!` or `match_token_opt!`. This is used to provide a clearer error
126/// message.
127#[derive(Debug, Clone)]
128pub struct ExpectedSet {
129    /// Other sets of patterns that were attempted on the same location.
130    prev: Vec<ExpectedSet>,
131
132    /// The set of patterns that were tried in this invocation.
133    tried: &'static [Expected],
134}
135
136#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
137pub(crate) enum Expected {
138    /// Expected a token spanning a particular literal slice.
139    Literal(&'static str),
140    /// Expected any slice of source string that matches a specific token.
141    Token(Token),
142}
143
144/// The result of a `match_token_opt!` invocation, which can succeed, or return the set of patterns
145/// it tried, and the offset they were tried at.
146#[derive(Clone)]
147pub(crate) enum Match<T> {
148    Found(T),
149    Tried(Option<usize>, ExpectedSet),
150}
151
152impl Error {
153    /// Whether this error is because of something outside the user's control.
154    pub fn is_internal_error(&self) -> bool {
155        matches!(self, Self::NameEvaluation(_, e) if e.is_internal_error())
156    }
157
158    /// Whether this error is because a resource limit was exceeded.
159    pub fn is_resource_limit_error(&self) -> bool {
160        matches!(self, Self::NameEvaluation(_, e) if e.is_resource_limit_error())
161            || matches!(
162                self,
163                Self::TooBig | Self::TooManyLoads | Self::TooMuchOutput
164            )
165    }
166}
167
168impl FormatError {
169    /// Whether this error is because of something outside the user's control.
170    pub fn is_internal_error(&self) -> bool {
171        matches!(self, Self::Bcs(_) | Self::Store(_) | Self::Visitor(_))
172    }
173
174    /// Whether this error is because a resource limit was exceeded.
175    pub fn is_resource_limit_error(&self) -> bool {
176        matches!(
177            self,
178            Self::TooBig | Self::TooDeep | Self::TooManyLoads | Self::TooMuchOutput
179        )
180    }
181
182    /// Indicate that the error occurred while processing an expression at `offset`.
183    pub(crate) fn for_expr_at_offset(self, offset: usize) -> Self {
184        match self {
185            FormatError::TransformInvalid(reason) => {
186                FormatError::TransformInvalid_ { offset, reason }
187            }
188            error => error,
189        }
190    }
191
192    // Indicate that `tried` was also tried at `offset`, in case the error is related to other
193    // tokens that were tried at the same location.
194    pub(crate) fn also_tried(self, offset: Option<usize>, tried: ExpectedSet) -> Self {
195        match (offset, self) {
196            (Some(offset), FormatError::UnexpectedToken { actual, expect })
197                if offset == actual.2 =>
198            {
199                FormatError::UnexpectedToken {
200                    actual,
201                    expect: expect.union(tried),
202                }
203            }
204
205            (None, FormatError::UnexpectedEos { expect }) => FormatError::UnexpectedEos {
206                expect: expect.union(tried),
207            },
208
209            (_, error) => error,
210        }
211    }
212}
213
214impl ExpectedSet {
215    pub(crate) fn new(tried: &'static [Expected]) -> Self {
216        Self {
217            prev: vec![],
218            tried,
219        }
220    }
221
222    pub(crate) fn with_prev(mut self, prev: ExpectedSet) -> Self {
223        self.prev.push(prev);
224        self
225    }
226
227    pub(crate) fn union(mut self, mut other: ExpectedSet) -> Self {
228        // Always arrange for `self` to be the larger set, so that we drain the smaller set into
229        // the larger, to always do the minimal work. Ordering does not matter because tokens
230        // across all sets are collected into a set before displaying them.
231        if self.prev.len() < other.prev.len() {
232            mem::swap(&mut self, &mut other);
233        }
234
235        self.prev.append(&mut other.prev);
236        self.prev.push(other);
237        self
238    }
239
240    pub(crate) fn into_error(self, actual: Option<&Lexeme<'_>>) -> FormatError {
241        if let Some(actual) = actual {
242            FormatError::UnexpectedToken {
243                actual: actual.detach(),
244                expect: self,
245            }
246        } else {
247            FormatError::UnexpectedEos { expect: self }
248        }
249    }
250}
251
252impl<T> Match<T> {
253    pub(crate) fn is_not_found(&self) -> bool {
254        matches!(self, Match::Tried(_, _))
255    }
256}
257
258impl fmt::Display for Expected {
259    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
260        match self {
261            Expected::Token(token) => write!(f, "{token}"),
262            Expected::Literal(s) => write!(f, "'{s}'"),
263        }
264    }
265}
266
267impl fmt::Display for ExpectedSet {
268    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269        // Gather all the tokens that were tried.
270        let mut expected = BTreeSet::new();
271        let mut stack = vec![self];
272        while let Some(set) = stack.pop() {
273            expected.extend(set.tried);
274            stack.extend(&set.prev);
275        }
276
277        if expected.is_empty() {
278            return write!(f, "nothing");
279        }
280
281        let mut tokens = expected.into_iter().peekable2();
282        let mut prefix = if tokens.peek2().is_some() {
283            "one of "
284        } else {
285            ""
286        };
287
288        while let Some(token) = tokens.next() {
289            write!(f, "{prefix}{token}")?;
290            prefix = if tokens.peek2().is_some() {
291                ", "
292            } else {
293                ", or "
294            };
295        }
296
297        Ok(())
298    }
299}
300
301impl From<RV::Error> for FormatError {
302    fn from(error: RV::Error) -> Self {
303        match error {
304            RV::Error::Visitor(err) => err.into(),
305            RV::Error::Option(err) => err.into(),
306            RV::Error::Meter(err) => err.into(),
307            RV::Error::UnexpectedType => {
308                FormatError::Bcs(bcs::Error::Custom("unexpected type".to_string()))
309            }
310        }
311    }
312}
313
314impl From<RV::MeterError> for FormatError {
315    fn from(error: RV::MeterError) -> Self {
316        match error {
317            RV::MeterError::TooBig => FormatError::TooBig,
318            RV::MeterError::TooDeep => FormatError::TooDeep,
319        }
320    }
321}
322
323impl From<OV::Error> for FormatError {
324    fn from(OV::Error: OV::Error) -> Self {
325        FormatError::Bcs(bcs::Error::ExpectedOption)
326    }
327}
328
329impl From<std::fmt::Error> for FormatError {
330    fn from(_: std::fmt::Error) -> Self {
331        FormatError::TooMuchOutput
332    }
333}