secret_cosmwasm_std/errors/
std_error.rs

1#[cfg(feature = "backtraces")]
2use std::backtrace::Backtrace;
3use std::fmt;
4use thiserror::Error;
5
6use crate::errors::{RecoverPubkeyError, SigningError, VerificationError};
7
8/// Structured error type for init, execute and query.
9///
10/// This can be serialized and passed over the Wasm/VM boundary, which allows us to use structured
11/// error types in e.g. integration tests. In that process backtraces are stripped off.
12///
13/// The prefix "Std" means "the standard error within the standard library". This is not the only
14/// result/error type in cosmwasm-std.
15///
16/// When new cases are added, they should describe the problem rather than what was attempted (e.g.
17/// InvalidBase64 is preferred over Base64DecodingErr). In the long run this allows us to get rid of
18/// the duplication in "StdError::FooErr".
19///
20/// Checklist for adding a new error:
21/// - Add enum case
22/// - Add creator function in std_error_helpers.rs
23#[derive(Error, Debug)]
24pub enum StdError {
25    #[error("Verification error: {source}")]
26    VerificationErr {
27        source: VerificationError,
28        #[cfg(feature = "backtraces")]
29        backtrace: Backtrace,
30    },
31    #[error("Signing error: {source}")]
32    SigningErr {
33        source: SigningError,
34        #[cfg(feature = "backtraces")]
35        backtrace: Backtrace,
36    },
37    #[error("Recover pubkey error: {source}")]
38    RecoverPubkeyErr {
39        source: RecoverPubkeyError,
40        #[cfg(feature = "backtraces")]
41        backtrace: Backtrace,
42    },
43    /// Whenever there is no specific error type available
44    #[error("Generic error: {msg}")]
45    GenericErr {
46        msg: String,
47        #[cfg(feature = "backtraces")]
48        backtrace: Backtrace,
49    },
50    #[error("Invalid Base64 string: {msg}")]
51    InvalidBase64 {
52        msg: String,
53        #[cfg(feature = "backtraces")]
54        backtrace: Backtrace,
55    },
56    #[error("Invalid data size: expected={expected} actual={actual}")]
57    InvalidDataSize {
58        expected: u64,
59        actual: u64,
60        #[cfg(feature = "backtraces")]
61        backtrace: Backtrace,
62    },
63    #[error("Invalid hex string: {msg}")]
64    InvalidHex {
65        msg: String,
66        #[cfg(feature = "backtraces")]
67        backtrace: Backtrace,
68    },
69    /// Whenever UTF-8 bytes cannot be decoded into a unicode string, e.g. in String::from_utf8 or str::from_utf8.
70    #[error("Cannot decode UTF8 bytes into string: {msg}")]
71    InvalidUtf8 {
72        msg: String,
73        #[cfg(feature = "backtraces")]
74        backtrace: Backtrace,
75    },
76    #[error("{kind} not found")]
77    NotFound {
78        kind: String,
79        #[cfg(feature = "backtraces")]
80        backtrace: Backtrace,
81    },
82    #[error("Error parsing into type {target_type}: {msg}")]
83    ParseErr {
84        /// the target type that was attempted
85        target_type: String,
86        msg: String,
87        #[cfg(feature = "backtraces")]
88        backtrace: Backtrace,
89    },
90    #[error("Error serializing type {source_type}: {msg}")]
91    SerializeErr {
92        /// the source type that was attempted
93        source_type: String,
94        msg: String,
95        #[cfg(feature = "backtraces")]
96        backtrace: Backtrace,
97    },
98    #[error("Overflow: {source}")]
99    Overflow {
100        source: OverflowError,
101        #[cfg(feature = "backtraces")]
102        backtrace: Backtrace,
103    },
104    #[error("Divide by zero: {source}")]
105    DivideByZero {
106        source: DivideByZeroError,
107        #[cfg(feature = "backtraces")]
108        backtrace: Backtrace,
109    },
110    #[error("Conversion error: ")]
111    ConversionOverflow {
112        #[from]
113        source: ConversionOverflowError,
114        #[cfg(feature = "backtraces")]
115        backtrace: Backtrace,
116    },
117}
118
119impl StdError {
120    pub fn verification_err(source: VerificationError) -> Self {
121        StdError::VerificationErr {
122            source,
123            #[cfg(feature = "backtraces")]
124            backtrace: Backtrace::capture(),
125        }
126    }
127
128    pub fn recover_pubkey_err(source: RecoverPubkeyError) -> Self {
129        StdError::RecoverPubkeyErr {
130            source,
131            #[cfg(feature = "backtraces")]
132            backtrace: Backtrace::capture(),
133        }
134    }
135
136    pub fn generic_err(msg: impl Into<String>) -> Self {
137        StdError::GenericErr {
138            msg: msg.into(),
139            #[cfg(feature = "backtraces")]
140            backtrace: Backtrace::capture(),
141        }
142    }
143
144    pub fn invalid_base64(msg: impl ToString) -> Self {
145        StdError::InvalidBase64 {
146            msg: msg.to_string(),
147            #[cfg(feature = "backtraces")]
148            backtrace: Backtrace::capture(),
149        }
150    }
151
152    pub fn invalid_data_size(expected: usize, actual: usize) -> Self {
153        StdError::InvalidDataSize {
154            // Cast is safe because usize is 32 or 64 bit large in all environments we support
155            expected: expected as u64,
156            actual: actual as u64,
157            #[cfg(feature = "backtraces")]
158            backtrace: Backtrace::capture(),
159        }
160    }
161
162    pub fn invalid_hex(msg: impl ToString) -> Self {
163        StdError::InvalidHex {
164            msg: msg.to_string(),
165            #[cfg(feature = "backtraces")]
166            backtrace: Backtrace::capture(),
167        }
168    }
169
170    pub fn invalid_utf8(msg: impl ToString) -> Self {
171        StdError::InvalidUtf8 {
172            msg: msg.to_string(),
173            #[cfg(feature = "backtraces")]
174            backtrace: Backtrace::capture(),
175        }
176    }
177
178    pub fn not_found(kind: impl Into<String>) -> Self {
179        StdError::NotFound {
180            kind: kind.into(),
181            #[cfg(feature = "backtraces")]
182            backtrace: Backtrace::capture(),
183        }
184    }
185
186    pub fn parse_err(target: impl Into<String>, msg: impl ToString) -> Self {
187        StdError::ParseErr {
188            target_type: target.into(),
189            msg: msg.to_string(),
190            #[cfg(feature = "backtraces")]
191            backtrace: Backtrace::capture(),
192        }
193    }
194
195    pub fn serialize_err(source: impl Into<String>, msg: impl ToString) -> Self {
196        StdError::SerializeErr {
197            source_type: source.into(),
198            msg: msg.to_string(),
199            #[cfg(feature = "backtraces")]
200            backtrace: Backtrace::capture(),
201        }
202    }
203
204    pub fn overflow(source: OverflowError) -> Self {
205        StdError::Overflow {
206            source,
207            #[cfg(feature = "backtraces")]
208            backtrace: Backtrace::capture(),
209        }
210    }
211
212    pub fn divide_by_zero(source: DivideByZeroError) -> Self {
213        StdError::DivideByZero {
214            source,
215            #[cfg(feature = "backtraces")]
216            backtrace: Backtrace::capture(),
217        }
218    }
219}
220
221impl PartialEq<StdError> for StdError {
222    fn eq(&self, rhs: &StdError) -> bool {
223        match self {
224            StdError::VerificationErr {
225                source,
226                #[cfg(feature = "backtraces")]
227                    backtrace: _,
228            } => {
229                if let StdError::VerificationErr {
230                    source: rhs_source,
231                    #[cfg(feature = "backtraces")]
232                        backtrace: _,
233                } = rhs
234                {
235                    source == rhs_source
236                } else {
237                    false
238                }
239            }
240            StdError::RecoverPubkeyErr {
241                source,
242                #[cfg(feature = "backtraces")]
243                    backtrace: _,
244            } => {
245                if let StdError::RecoverPubkeyErr {
246                    source: rhs_source,
247                    #[cfg(feature = "backtraces")]
248                        backtrace: _,
249                } = rhs
250                {
251                    source == rhs_source
252                } else {
253                    false
254                }
255            }
256            StdError::GenericErr {
257                msg,
258                #[cfg(feature = "backtraces")]
259                    backtrace: _,
260            } => {
261                if let StdError::GenericErr {
262                    msg: rhs_msg,
263                    #[cfg(feature = "backtraces")]
264                        backtrace: _,
265                } = rhs
266                {
267                    msg == rhs_msg
268                } else {
269                    false
270                }
271            }
272            StdError::InvalidBase64 {
273                msg,
274                #[cfg(feature = "backtraces")]
275                    backtrace: _,
276            } => {
277                if let StdError::InvalidBase64 {
278                    msg: rhs_msg,
279                    #[cfg(feature = "backtraces")]
280                        backtrace: _,
281                } = rhs
282                {
283                    msg == rhs_msg
284                } else {
285                    false
286                }
287            }
288            StdError::InvalidDataSize {
289                expected,
290                actual,
291                #[cfg(feature = "backtraces")]
292                    backtrace: _,
293            } => {
294                if let StdError::InvalidDataSize {
295                    expected: rhs_expected,
296                    actual: rhs_actual,
297                    #[cfg(feature = "backtraces")]
298                        backtrace: _,
299                } = rhs
300                {
301                    expected == rhs_expected && actual == rhs_actual
302                } else {
303                    false
304                }
305            }
306            StdError::InvalidHex {
307                msg,
308                #[cfg(feature = "backtraces")]
309                    backtrace: _,
310            } => {
311                if let StdError::InvalidHex {
312                    msg: rhs_msg,
313                    #[cfg(feature = "backtraces")]
314                        backtrace: _,
315                } = rhs
316                {
317                    msg == rhs_msg
318                } else {
319                    false
320                }
321            }
322            StdError::InvalidUtf8 {
323                msg,
324                #[cfg(feature = "backtraces")]
325                    backtrace: _,
326            } => {
327                if let StdError::InvalidUtf8 {
328                    msg: rhs_msg,
329                    #[cfg(feature = "backtraces")]
330                        backtrace: _,
331                } = rhs
332                {
333                    msg == rhs_msg
334                } else {
335                    false
336                }
337            }
338            StdError::NotFound {
339                kind,
340                #[cfg(feature = "backtraces")]
341                    backtrace: _,
342            } => {
343                if let StdError::NotFound {
344                    kind: rhs_kind,
345                    #[cfg(feature = "backtraces")]
346                        backtrace: _,
347                } = rhs
348                {
349                    kind == rhs_kind
350                } else {
351                    false
352                }
353            }
354            StdError::ParseErr {
355                target_type,
356                msg,
357                #[cfg(feature = "backtraces")]
358                    backtrace: _,
359            } => {
360                if let StdError::ParseErr {
361                    target_type: rhs_target_type,
362                    msg: rhs_msg,
363                    #[cfg(feature = "backtraces")]
364                        backtrace: _,
365                } = rhs
366                {
367                    target_type == rhs_target_type && msg == rhs_msg
368                } else {
369                    false
370                }
371            }
372            StdError::SerializeErr {
373                source_type,
374                msg,
375                #[cfg(feature = "backtraces")]
376                    backtrace: _,
377            } => {
378                if let StdError::SerializeErr {
379                    source_type: rhs_source_type,
380                    msg: rhs_msg,
381                    #[cfg(feature = "backtraces")]
382                        backtrace: _,
383                } = rhs
384                {
385                    source_type == rhs_source_type && msg == rhs_msg
386                } else {
387                    false
388                }
389            }
390            StdError::Overflow {
391                source,
392                #[cfg(feature = "backtraces")]
393                    backtrace: _,
394            } => {
395                if let StdError::Overflow {
396                    source: rhs_source,
397                    #[cfg(feature = "backtraces")]
398                        backtrace: _,
399                } = rhs
400                {
401                    source == rhs_source
402                } else {
403                    false
404                }
405            }
406            StdError::DivideByZero {
407                source,
408                #[cfg(feature = "backtraces")]
409                    backtrace: _,
410            } => {
411                if let StdError::DivideByZero {
412                    source: rhs_source,
413                    #[cfg(feature = "backtraces")]
414                        backtrace: _,
415                } = rhs
416                {
417                    source == rhs_source
418                } else {
419                    false
420                }
421            }
422            StdError::ConversionOverflow {
423                source,
424                #[cfg(feature = "backtraces")]
425                    backtrace: _,
426            } => {
427                if let StdError::ConversionOverflow {
428                    source: rhs_source,
429                    #[cfg(feature = "backtraces")]
430                        backtrace: _,
431                } = rhs
432                {
433                    source == rhs_source
434                } else {
435                    false
436                }
437            }
438            StdError::SigningErr {
439                source,
440                #[cfg(feature = "backtraces")]
441                    backtrace: _,
442            } => {
443                if let StdError::SigningErr {
444                    source: rhs_source,
445                    #[cfg(feature = "backtraces")]
446                        backtrace: _,
447                } = rhs
448                {
449                    source == rhs_source
450                } else {
451                    false
452                }
453            }
454        }
455    }
456}
457
458impl From<std::str::Utf8Error> for StdError {
459    fn from(source: std::str::Utf8Error) -> Self {
460        Self::invalid_utf8(source)
461    }
462}
463
464impl From<std::string::FromUtf8Error> for StdError {
465    fn from(source: std::string::FromUtf8Error) -> Self {
466        Self::invalid_utf8(source)
467    }
468}
469
470impl From<VerificationError> for StdError {
471    fn from(source: VerificationError) -> Self {
472        Self::verification_err(source)
473    }
474}
475
476impl From<RecoverPubkeyError> for StdError {
477    fn from(source: RecoverPubkeyError) -> Self {
478        Self::recover_pubkey_err(source)
479    }
480}
481
482impl From<OverflowError> for StdError {
483    fn from(source: OverflowError) -> Self {
484        Self::overflow(source)
485    }
486}
487
488impl From<DivideByZeroError> for StdError {
489    fn from(source: DivideByZeroError) -> Self {
490        Self::divide_by_zero(source)
491    }
492}
493
494/// The return type for init, execute and query. Since the error type cannot be serialized to JSON,
495/// this is only available within the contract and its unit tests.
496///
497/// The prefix "Std" means "the standard result within the standard library". This is not the only
498/// result/error type in cosmwasm-std.
499pub type StdResult<T> = core::result::Result<T, StdError>;
500
501#[derive(Error, Debug, PartialEq, Eq)]
502pub enum OverflowOperation {
503    Add,
504    Sub,
505    Mul,
506    Pow,
507    Shr,
508    Shl,
509}
510
511impl fmt::Display for OverflowOperation {
512    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
513        write!(f, "{:?}", self)
514    }
515}
516
517#[derive(Error, Debug, PartialEq, Eq)]
518#[error("Cannot {operation} with {operand1} and {operand2}")]
519pub struct OverflowError {
520    pub operation: OverflowOperation,
521    pub operand1: String,
522    pub operand2: String,
523}
524
525impl OverflowError {
526    pub fn new(
527        operation: OverflowOperation,
528        operand1: impl ToString,
529        operand2: impl ToString,
530    ) -> Self {
531        Self {
532            operation,
533            operand1: operand1.to_string(),
534            operand2: operand2.to_string(),
535        }
536    }
537}
538
539/// The error returned by [`TryFrom`] conversions that overflow, for example
540/// when converting from [`Uint256`] to [`Uint128`].
541///
542/// [`TryFrom`]: std::convert::TryFrom
543/// [`Uint256`]: crate::Uint256
544/// [`Uint128`]: crate::Uint128
545#[derive(Error, Debug, PartialEq, Eq)]
546#[error("Error converting {source_type} to {target_type} for {value}")]
547pub struct ConversionOverflowError {
548    pub source_type: &'static str,
549    pub target_type: &'static str,
550    pub value: String,
551}
552
553impl ConversionOverflowError {
554    pub fn new(
555        source_type: &'static str,
556        target_type: &'static str,
557        value: impl Into<String>,
558    ) -> Self {
559        Self {
560            source_type,
561            target_type,
562            value: value.into(),
563        }
564    }
565}
566
567#[derive(Error, Debug, PartialEq, Eq)]
568#[error("Cannot devide {operand} by zero")]
569pub struct DivideByZeroError {
570    pub operand: String,
571}
572
573impl DivideByZeroError {
574    pub fn new(operand: impl ToString) -> Self {
575        Self {
576            operand: operand.to_string(),
577        }
578    }
579}
580
581#[derive(Error, Debug, PartialEq, Eq)]
582pub enum CheckedMultiplyRatioError {
583    #[error("Denominator must not be zero")]
584    DivideByZero,
585
586    #[error("Multiplication overflow")]
587    Overflow,
588}
589
590#[derive(Error, Debug, PartialEq, Eq)]
591pub enum CheckedFromRatioError {
592    #[error("Denominator must not be zero")]
593    DivideByZero,
594
595    #[error("Overflow")]
596    Overflow,
597}
598
599#[derive(Error, Debug, PartialEq, Eq)]
600#[error("Round up operation failed because of overflow")]
601pub struct RoundUpOverflowError;
602
603#[cfg(test)]
604mod tests {
605    use super::*;
606    use std::str;
607
608    // constructors
609
610    // example of reporting contract errors with format!
611    #[test]
612    fn generic_err_owned() {
613        let guess = 7;
614        let error = StdError::generic_err(format!("{} is too low", guess));
615        match error {
616            StdError::GenericErr { msg, .. } => {
617                assert_eq!(msg, String::from("7 is too low"));
618            }
619            e => panic!("unexpected error, {:?}", e),
620        }
621    }
622
623    // example of reporting static contract errors
624    #[test]
625    fn generic_err_ref() {
626        let error = StdError::generic_err("not implemented");
627        match error {
628            StdError::GenericErr { msg, .. } => assert_eq!(msg, "not implemented"),
629            e => panic!("unexpected error, {:?}", e),
630        }
631    }
632
633    #[test]
634    fn invalid_base64_works_for_strings() {
635        let error = StdError::invalid_base64("my text");
636        match error {
637            StdError::InvalidBase64 { msg, .. } => {
638                assert_eq!(msg, "my text");
639            }
640            _ => panic!("expect different error"),
641        }
642    }
643
644    #[test]
645    fn invalid_base64_works_for_errors() {
646        let original = base64::DecodeError::InvalidLength;
647        let error = StdError::invalid_base64(original);
648        match error {
649            StdError::InvalidBase64 { msg, .. } => {
650                assert_eq!(msg, "Encoded text cannot have a 6-bit remainder.");
651            }
652            _ => panic!("expect different error"),
653        }
654    }
655
656    #[test]
657    fn invalid_data_size_works() {
658        let error = StdError::invalid_data_size(31, 14);
659        match error {
660            StdError::InvalidDataSize {
661                expected, actual, ..
662            } => {
663                assert_eq!(expected, 31);
664                assert_eq!(actual, 14);
665            }
666            _ => panic!("expect different error"),
667        }
668    }
669
670    #[test]
671    fn invalid_hex_works_for_strings() {
672        let error = StdError::invalid_hex("my text");
673        match error {
674            StdError::InvalidHex { msg, .. } => {
675                assert_eq!(msg, "my text");
676            }
677            _ => panic!("expect different error"),
678        }
679    }
680
681    #[test]
682    fn invalid_hex_works_for_errors() {
683        let original = hex::FromHexError::OddLength;
684        let error = StdError::invalid_hex(original);
685        match error {
686            StdError::InvalidHex { msg, .. } => {
687                assert_eq!(msg, "Odd number of digits");
688            }
689            _ => panic!("expect different error"),
690        }
691    }
692
693    #[test]
694    fn invalid_utf8_works_for_strings() {
695        let error = StdError::invalid_utf8("my text");
696        match error {
697            StdError::InvalidUtf8 { msg, .. } => {
698                assert_eq!(msg, "my text");
699            }
700            _ => panic!("expect different error"),
701        }
702    }
703
704    #[test]
705    fn invalid_utf8_works_for_errors() {
706        let original = String::from_utf8(vec![0x80]).unwrap_err();
707        let error = StdError::invalid_utf8(original);
708        match error {
709            StdError::InvalidUtf8 { msg, .. } => {
710                assert_eq!(msg, "invalid utf-8 sequence of 1 bytes from index 0");
711            }
712            _ => panic!("expect different error"),
713        }
714    }
715
716    #[test]
717    fn not_found_works() {
718        let error = StdError::not_found("gold");
719        match error {
720            StdError::NotFound { kind, .. } => assert_eq!(kind, "gold"),
721            _ => panic!("expect different error"),
722        }
723    }
724
725    #[test]
726    fn parse_err_works() {
727        let error = StdError::parse_err("Book", "Missing field: title");
728        match error {
729            StdError::ParseErr {
730                target_type, msg, ..
731            } => {
732                assert_eq!(target_type, "Book");
733                assert_eq!(msg, "Missing field: title");
734            }
735            _ => panic!("expect different error"),
736        }
737    }
738
739    #[test]
740    fn serialize_err_works() {
741        let error = StdError::serialize_err("Book", "Content too long");
742        match error {
743            StdError::SerializeErr {
744                source_type, msg, ..
745            } => {
746                assert_eq!(source_type, "Book");
747                assert_eq!(msg, "Content too long");
748            }
749            _ => panic!("expect different error"),
750        }
751    }
752
753    #[test]
754    fn underflow_works_for_u128() {
755        let error =
756            StdError::overflow(OverflowError::new(OverflowOperation::Sub, 123u128, 456u128));
757        match error {
758            StdError::Overflow {
759                source:
760                    OverflowError {
761                        operation,
762                        operand1,
763                        operand2,
764                    },
765                ..
766            } => {
767                assert_eq!(operation, OverflowOperation::Sub);
768                assert_eq!(operand1, "123");
769                assert_eq!(operand2, "456");
770            }
771            _ => panic!("expect different error"),
772        }
773    }
774
775    #[test]
776    fn overflow_works_for_i64() {
777        let error = StdError::overflow(OverflowError::new(OverflowOperation::Sub, 777i64, 1234i64));
778        match error {
779            StdError::Overflow {
780                source:
781                    OverflowError {
782                        operation,
783                        operand1,
784                        operand2,
785                    },
786                ..
787            } => {
788                assert_eq!(operation, OverflowOperation::Sub);
789                assert_eq!(operand1, "777");
790                assert_eq!(operand2, "1234");
791            }
792            _ => panic!("expect different error"),
793        }
794    }
795
796    #[test]
797    fn divide_by_zero_works() {
798        let error = StdError::divide_by_zero(DivideByZeroError::new(123u128));
799        match error {
800            StdError::DivideByZero {
801                source: DivideByZeroError { operand },
802                ..
803            } => assert_eq!(operand, "123"),
804            _ => panic!("expect different error"),
805        }
806    }
807
808    #[test]
809    fn implements_debug() {
810        let error: StdError = StdError::from(OverflowError::new(OverflowOperation::Sub, 3, 5));
811        let embedded = format!("Debug: {:?}", error);
812        #[cfg(not(feature = "backtraces"))]
813        let expected = r#"Debug: Overflow { source: OverflowError { operation: Sub, operand1: "3", operand2: "5" } }"#;
814        #[cfg(feature = "backtraces")]
815        let expected = r#"Debug: Overflow { source: OverflowError { operation: Sub, operand1: "3", operand2: "5" }, backtrace: <disabled> }"#;
816        assert_eq!(embedded, expected);
817    }
818
819    #[test]
820    fn implements_display() {
821        let error: StdError = StdError::from(OverflowError::new(OverflowOperation::Sub, 3, 5));
822        let embedded = format!("Display: {}", error);
823        assert_eq!(embedded, "Display: Overflow: Cannot Sub with 3 and 5");
824    }
825
826    #[test]
827    fn implements_partial_eq() {
828        let u1 = StdError::from(OverflowError::new(OverflowOperation::Sub, 3, 5));
829        let u2 = StdError::from(OverflowError::new(OverflowOperation::Sub, 3, 5));
830        let u3 = StdError::from(OverflowError::new(OverflowOperation::Sub, 3, 7));
831        let s1 = StdError::serialize_err("Book", "Content too long");
832        let s2 = StdError::serialize_err("Book", "Content too long");
833        let s3 = StdError::serialize_err("Book", "Title too long");
834        assert_eq!(u1, u2);
835        assert_ne!(u1, u3);
836        assert_ne!(u1, s1);
837        assert_eq!(s1, s2);
838        assert_ne!(s1, s3);
839    }
840
841    #[test]
842    fn from_std_str_utf8error_works() {
843        let error: StdError = str::from_utf8(b"Hello \xF0\x90\x80World")
844            .unwrap_err()
845            .into();
846        match error {
847            StdError::InvalidUtf8 { msg, .. } => {
848                assert_eq!(msg, "invalid utf-8 sequence of 3 bytes from index 6")
849            }
850            err => panic!("Unexpected error: {:?}", err),
851        }
852    }
853
854    #[test]
855    fn from_std_string_fromutf8error_works() {
856        let error: StdError = String::from_utf8(b"Hello \xF0\x90\x80World".to_vec())
857            .unwrap_err()
858            .into();
859        match error {
860            StdError::InvalidUtf8 { msg, .. } => {
861                assert_eq!(msg, "invalid utf-8 sequence of 3 bytes from index 6")
862            }
863            err => panic!("Unexpected error: {:?}", err),
864        }
865    }
866}