nkeys/
error.rs

1//! # Error wrappers and boilerplate
2//!
3//! This module provides some basic boilerplate for errors. As a consumer of this
4//! library, you should expect that all public functions return a `Result` type
5//! using this local `Error`, which implements the standard Error trait.
6//! As a general rule, errors that come from dependent crates are wrapped by
7//! this crate's error type.
8#![allow(unused_macros)]
9
10use core::fmt;
11use signatory::signature;
12
13use std::{
14    error::Error as StdError,
15    string::{String, ToString},
16};
17
18/// Provides an error type specific to the nkeys library
19#[derive(Debug)]
20pub struct Error {
21    kind: ErrorKind,
22
23    description: Option<String>,
24}
25
26/// Provides context as to how a particular nkeys error might have occurred
27#[derive(Debug, Copy, Clone, PartialEq)]
28pub enum ErrorKind {
29    /// Indicates an inappropriate byte prefix was used for an encoded key string
30    InvalidPrefix,
31    /// Indicates a key string was used with the wrong length
32    InvalidKeyLength,
33    /// Indicates a signature verification mismatch. Use this to check for invalid signatures or messages
34    VerifyError,
35    /// Indicates an unexpected underlying error occurred while trying to perform routine signature tasks.
36    SignatureError,
37    /// Indicates a checksum mismatch occurred while validating a crc-encoded string
38    ChecksumFailure,
39    /// Indicates a miscellaneous error occurred during encoding or decoding the nkey-specific formats
40    CodecFailure,
41    /// Indicates a key type mismatch, e.g. attempting to sign with only a public key
42    IncorrectKeyType,
43    /// Payload not valid (or failed to be decrypted)
44    InvalidPayload,
45    /// Signature did not match the expected length (64 bytes)
46    InvalidSignatureLength,
47}
48
49/// A handy macro borrowed from the `signatory` crate that lets library-internal code generate
50/// more readable exception handling flows
51#[macro_export]
52macro_rules! err {
53    ($variant:ident, $msg:expr) => {
54        $crate::error::Error::new(
55            $crate::error::ErrorKind::$variant,
56            Some($msg)
57        )
58    };
59    ($variant:ident, $fmt:expr, $($arg:tt)+) => {
60        err!($variant, &format!($fmt, $($arg)+))
61    };
62}
63
64impl ErrorKind {
65    pub fn as_str(self) -> &'static str {
66        match self {
67            ErrorKind::InvalidPrefix => "Invalid byte prefix",
68            ErrorKind::InvalidKeyLength => "Invalid key length",
69            ErrorKind::InvalidSignatureLength => "Invalid signature length",
70            ErrorKind::VerifyError => "Signature verification failure",
71            ErrorKind::ChecksumFailure => "Checksum match failure",
72            ErrorKind::CodecFailure => "Codec failure",
73            ErrorKind::SignatureError => "Signature failure",
74            ErrorKind::IncorrectKeyType => "Incorrect key type",
75            ErrorKind::InvalidPayload => "Invalid payload",
76        }
77    }
78}
79
80impl fmt::Display for ErrorKind {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "{}", self.as_str())
83    }
84}
85
86impl Error {
87    /// Creates a new nkeys error wrapper
88    pub fn new(kind: ErrorKind, description: Option<&str>) -> Self {
89        Error {
90            kind,
91            description: description.map(|desc| desc.to_string()),
92        }
93    }
94
95    /// An accessor exposing the error kind enum. Crate consumers should have little to no
96    /// need to access this directly and it's mostly used to assert that internal functions
97    /// are creating appropriate error wrappers.
98    pub fn kind(&self) -> ErrorKind {
99        self.kind
100    }
101}
102
103/// Creates an nkeys error derived from an error that came from the `signatory` crate
104impl From<signature::Error> for Error {
105    fn from(source: signature::Error) -> Error {
106        err!(SignatureError, &format!("Signature error: {}", source))
107    }
108}
109
110/// Creates an nkeys error derived from a decoding failure in the `data_encoding` crate
111impl From<data_encoding::DecodeError> for Error {
112    fn from(source: data_encoding::DecodeError) -> Error {
113        err!(CodecFailure, "Data encoding failure: {}", source)
114    }
115}
116
117impl StdError for Error {
118    fn description(&self) -> &str {
119        if let Some(ref desc) = self.description {
120            desc
121        } else {
122            self.kind.as_str()
123        }
124    }
125}
126
127impl fmt::Display for Error {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        match self.description {
130            Some(ref desc) => write!(f, "{}: {}", self.kind.as_str(), desc),
131            None => write!(f, "{}", self.kind.as_str()),
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    #[test]
139    fn test_error_to_string() {
140        assert_eq!(
141            err!(InvalidKeyLength, "Testing").to_string(),
142            "Invalid key length: Testing"
143        );
144        assert_eq!(
145            err!(InvalidKeyLength, "Testing {}", 1).to_string(),
146            "Invalid key length: Testing 1"
147        );
148    }
149}