1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
use std::convert;
use std::error::Error;
use std::fmt;
use std::io;
use std::num;

/// Error struct used for generating an `io::Error` from a generic description.
#[derive(Debug)]
pub struct RustyError {
    descr: &'static str,
    detail: Option<String>,
    share_index: Option<u8>,
    share_groups: Option<Vec<Vec<u8>>>
}

#[derive(Debug)]
pub enum RustyErrorTypes {
    DuplicateShareNum(u8),
    DuplicateShareData(u8),
    EmptyShares,
    IncompatibleSets(Vec<Vec<u8>>),
    InvalidSignature(u8, String),
    MissingShares(u8, usize),
    MissingSignature(u8),
    SecretDeserializationIssue,
    ShareParsingError(u8, String)
}

impl RustyError {
    /// Initializes a new error with a description and optional detail string.
    fn new(descr: &'static str, detail: Option<String>, share_index: Option<u8>, share_groups: Option<Vec<Vec<u8>>>) -> RustyError {
        RustyError {
            descr: descr,
            detail: detail,
            share_index: share_index,
            share_groups: share_groups
        }
    }

    /// Returns a `RustyError` with a given `RustyErrorType`.
    pub fn with_type(error_type: RustyErrorTypes) -> RustyError {
        RustyError {
            descr: RustyError::descr_for_type(&error_type),
            detail: RustyError::detail_for_type(&error_type),
            share_index: RustyError::share_num_for_type(&error_type),
            share_groups: RustyError::share_groups_for_type(error_type),
        }
    }

    /// Returns the index of the share that raised the error, if any.
    pub fn share_index(&self) -> Option<u8> {
        self.share_index
    }

    /// Returns the group of shares that were generated during the same secret share.
    /// It can be used to provide a debug message to the user telling him what shares are incompatible.
    pub fn share_groups(&self) -> Option<Vec<Vec<u8>>> {
        self.share_groups.clone()
    }

    fn descr_for_type(error_type: &RustyErrorTypes) -> &'static str {
        match *error_type {
            RustyErrorTypes::EmptyShares => "No shares were provided.",
            RustyErrorTypes::IncompatibleSets(_) => "The shares are incompatible with each other.",
            RustyErrorTypes::InvalidSignature(_, _) => "The signature of this share is not valid.",
            RustyErrorTypes::MissingShares(_, _) => "The number of shares provided is insufficient to recover the secret.",
            RustyErrorTypes::MissingSignature(_) => "Signature is missing while shares are required to be signed.",
            RustyErrorTypes::SecretDeserializationIssue => "An issue was encountered deserializing the secret.
            Updating to the latest version of RustySecrets might help fix this.",
            RustyErrorTypes::ShareParsingError(_, _) => "This share is incorrectly formatted.",
            RustyErrorTypes::DuplicateShareNum(_) => "This share number has already been used by a previous share.",
            RustyErrorTypes::DuplicateShareData(_) => "The data encoded in this share is the same as the one found in a previous share."
        }
    }

    fn detail_for_type(error_type: &RustyErrorTypes) -> Option<String> {
        match *error_type {
            RustyErrorTypes::MissingShares(required, found) => Some(format!("{} shares are required to recover the secret,
                                                                   found only {}.", required, found)),
            RustyErrorTypes::ShareParsingError(_, ref description) => Some(description.clone()),
            _ => None
        }
    }

    fn share_groups_for_type(error_type: RustyErrorTypes) -> Option<Vec<Vec<u8>>>{
        match error_type {
            RustyErrorTypes::IncompatibleSets(groups) => Some(groups),
            _ => None
        }
    }

    fn share_num_for_type(error_type: &RustyErrorTypes) -> Option<u8> {
        match *error_type {
            RustyErrorTypes::InvalidSignature(share_num, _)
            | RustyErrorTypes::MissingSignature(share_num)
            | RustyErrorTypes::ShareParsingError(share_num, _)
            | RustyErrorTypes::DuplicateShareNum(share_num)
            | RustyErrorTypes::DuplicateShareData(share_num) => Some(share_num),
            _ => None
        }
    }
}

impl fmt::Display for RustyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.detail {
            None => write!(f, "{}", self.descr),
            Some(ref detail) => write!(f, "{} ({})", self.descr, detail),
        }
    }
}

impl Error for RustyError {
    fn description(&self) -> &str {
        self.descr
    }
    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl From<io::Error> for RustyError {
    fn from(err: io::Error) -> RustyError {
        let descr = err.description().to_owned();

        RustyError::new("from io:Error", Some(descr), None, None)
    }
}

impl From<RustyError> for io::Error {
    fn from(me: RustyError) -> io::Error {
        io::Error::new(io::ErrorKind::Other, me)
    }
}

/// Returns an `io::Error` from description string and optional detail string.
/// Particularly useful in `Result` expressions.
pub fn other_io_err(descr: &'static str, detail: Option<String>,
                    share_num: Option<u8>, share_groups: Option<Vec<Vec<u8>>>) -> io::Error {
    convert::From::from(RustyError::new(descr, detail, share_num, share_groups))
}

/// maps a `ParseIntError` to an `Error`
pub fn pie2error(p: num::ParseIntError) -> RustyError {
    RustyError::new("Integer parsing error", Some(p.to_string()), None, None)
}

#[cfg(test)]
mod tests_custom_err {
    use std::error;
    use custom_error::RustyError;

    #[test]
    fn test_custom_error() {
        let desc = "Boring error description";
        let detail = "More of it";
        let ewd = RustyError::new(desc, Some(detail.to_string()), None, None);

        assert_eq!(error::Error::description(&ewd), desc);
        match error::Error::cause(&ewd) {
            Some(_) => assert!(false),
            None => assert!(true),
        }
        let _formated_err = format!("{}", ewd);
        let ewod = RustyError::new(desc, None, None, None);
        let _formated_err = format!("{}", ewod);
    }
}

#[cfg(test)]
mod tests_std_err {
    use std::error::Error;
    use custom_error::pie2error;

    #[test]
    fn test_parse_errors() {
        let nan = "2a".to_string();
        match nan.parse::<u8>().map_err(pie2error) {
            Ok(_) => assert!(false),
            Err(x) => assert_eq!("Integer parsing error", x.description()),
        }
    }
}