Skip to main content

candid/
error.rs

1//! `candid::Result<T> = Result<T, candid::Error>>`
2
3use serde::{de, ser};
4use std::collections::TryReserveError;
5use std::{io, num::ParseIntError};
6use thiserror::Error;
7
8pub type Result<T = ()> = std::result::Result<T, Error>;
9
10#[derive(Debug)]
11pub struct Label {
12    pos: usize,
13    message: String,
14}
15
16#[derive(Debug, Error)]
17pub enum Error {
18    #[error("binary parser error: {}", .0.first().map_or_else(|| "io error".to_string(), |f| format!("{} at byte offset {}", f.message, f.pos/2)))]
19    Binread(Vec<Label>),
20
21    #[error(transparent)]
22    Reserve(#[from] TryReserveError),
23
24    #[error("Subtyping error: {0}")]
25    Subtype(String),
26
27    #[error(transparent)]
28    Custom(#[from] anyhow::Error),
29}
30
31impl Error {
32    pub fn msg<T: ToString>(msg: T) -> Self {
33        Error::Custom(anyhow::anyhow!(msg.to_string()))
34    }
35    pub fn subtype<T: ToString>(msg: T) -> Self {
36        Error::Subtype(msg.to_string())
37    }
38}
39
40fn get_binread_labels(e: &binread::Error) -> Vec<Label> {
41    use binread::Error::*;
42    match e {
43        BadMagic { pos, .. } => {
44            let pos = (pos * 2) as usize;
45            vec![Label {
46                pos,
47                message: "Unexpected bytes".to_string(),
48            }]
49        }
50        Custom { pos, err } => {
51            let pos = (pos * 2) as usize;
52            let err = err
53                .downcast_ref::<&str>()
54                .unwrap_or(&"unknown error (there's a bug in error reporting)");
55            vec![Label {
56                pos,
57                message: (*err).to_string(),
58            }]
59        }
60        EnumErrors {
61            pos,
62            variant_errors,
63        } => {
64            let pos = (pos * 2) as usize;
65            let variant = variant_errors
66                .iter()
67                .find(|(_, e)| !matches!(e, BadMagic { .. }));
68            // Should have at most one non-magic error
69            match variant {
70                None => vec![Label {
71                    pos,
72                    message: "Unknown opcode".to_string(),
73                }],
74                Some((id, e)) => {
75                    let mut labels = get_binread_labels(e);
76                    labels.push(Label {
77                        pos,
78                        message: (*id).to_string(),
79                    });
80                    labels
81                }
82            }
83        }
84        NoVariantMatch { pos } => {
85            let pos = (pos * 2) as usize;
86            vec![Label {
87                pos,
88                message: "No variant match".to_string(),
89            }]
90        }
91        AssertFail { pos, message } => {
92            let pos = (pos * 2) as usize;
93            vec![Label {
94                pos,
95                message: message.to_string(),
96            }]
97        }
98        Io(_) => vec![],
99        _ => unreachable!(),
100    }
101}
102
103impl ser::Error for Error {
104    fn custom<T: std::fmt::Display>(msg: T) -> Self {
105        Error::msg(format!("Serialize error: {msg}"))
106    }
107}
108
109impl de::Error for Error {
110    fn custom<T: std::fmt::Display>(msg: T) -> Self {
111        let msg = msg.to_string();
112        if let Some(msg) = msg.strip_prefix("Subtyping error: ") {
113            Error::Subtype(msg.to_string())
114        } else {
115            Error::msg(format!("Deserialize error: {msg}"))
116        }
117    }
118    fn invalid_type(_: de::Unexpected<'_>, exp: &dyn de::Expected) -> Self {
119        Error::Subtype(format!("{exp}"))
120    }
121}
122
123impl From<io::Error> for Error {
124    fn from(e: io::Error) -> Error {
125        Error::msg(format!("io error: {e}"))
126    }
127}
128
129impl From<binread::Error> for Error {
130    fn from(e: binread::Error) -> Error {
131        Error::Binread(get_binread_labels(&e))
132    }
133}
134
135impl From<ParseIntError> for Error {
136    fn from(e: ParseIntError) -> Error {
137        Error::msg(format!("ParseIntError: {e}"))
138    }
139}