vitaminc_aead_napi/
error.rs1use napi::{Error, Status};
5use vitaminc_aead::plaintext::{self, SourceError};
6
7#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
17#[error("{reason}")]
18pub struct JsFault {
19 status: Status,
20 reason: String,
21}
22
23impl JsFault {
24 pub(crate) fn new(status: Status, reason: impl Into<String>) -> Self {
25 Self {
26 status,
27 reason: reason.into(),
28 }
29 }
30
31 pub fn status(&self) -> Status {
36 self.status
37 }
38
39 pub fn reason(&self) -> &str {
41 &self.reason
42 }
43
44 pub fn from_source(error: &SourceError) -> Error {
47 match error.inner().downcast_ref::<JsFault>() {
48 Some(fault) => fault.clone().into(),
49 None => Error::new(Status::InvalidArg, error.to_string()),
50 }
51 }
52}
53
54impl From<Error> for JsFault {
55 fn from(error: Error) -> Self {
56 Self {
57 status: error.status,
58 reason: error.reason,
59 }
60 }
61}
62
63impl From<JsFault> for Error {
64 fn from(fault: JsFault) -> Self {
65 Error::new(fault.status, fault.reason)
66 }
67}
68
69impl plaintext::Error for JsFault {
70 fn source(error: SourceError) -> Self {
71 match error.inner().downcast_ref::<JsFault>() {
72 Some(fault) => fault.clone(),
73 None => Self::new(Status::InvalidArg, error.to_string()),
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use vitaminc_aead::plaintext::Error as _;
82
83 #[test]
84 fn a_fault_reports_the_status_and_reason_it_was_made_with() {
85 let fault = JsFault::new(Status::GenericFailure, "the getter threw");
86 assert_eq!(fault.status(), Status::GenericFailure);
87 assert_eq!(fault.reason(), "the getter threw");
88 }
89
90 #[test]
91 fn a_fault_round_trips_through_a_napi_error() {
92 let fault = JsFault::new(Status::PendingException, "getter threw");
93 let error: Error = fault.clone().into();
94 assert_eq!(error.status, Status::PendingException);
95 assert_eq!(error.reason, "getter threw");
96 assert_eq!(JsFault::from(error), fault);
97 }
98
99 #[test]
100 fn a_source_error_carrying_a_fault_is_that_fault() {
101 let fault = JsFault::new(Status::InvalidArg, "unpaired surrogate");
102 let wrapped = SourceError::new(fault.clone());
103 assert_eq!(JsFault::source(wrapped), fault);
104 let wrapped = SourceError::new(fault.clone());
105 let error = JsFault::from_source(&wrapped);
106 assert_eq!(
107 (error.status, error.reason.as_str()),
108 (Status::InvalidArg, "unpaired surrogate")
109 );
110 }
111
112 #[test]
113 fn a_source_error_from_elsewhere_keeps_its_message() {
114 let wrapped = SourceError::msg("frame is malformed");
115 assert_eq!(
116 JsFault::source(wrapped),
117 JsFault::new(Status::InvalidArg, "frame is malformed")
118 );
119 let error = JsFault::from_source(&SourceError::msg("frame is malformed"));
120 assert_eq!(error.status, Status::InvalidArg);
121 assert_eq!(error.reason, "frame is malformed");
122 }
123}