1use std::fmt;
2
3use rutie::{self, Exception, Object};
4
5pub enum ErrorKind {
6 Message(String),
7 RutieException(rutie::AnyException),
8 NotImplemented(&'static str),
9}
10use self::ErrorKind::*;
11
12impl fmt::Display for ErrorKind {
13 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
14 match *self {
15 Message(ref msg) => write!(f, "{}", msg),
16 RutieException(ref exception) => {
17 let inspect = exception.protect_send("inspect", &[]);
18 let msg = match inspect {
19 Ok(inspect) => inspect
20 .try_convert_to::<rutie::RString>()
21 .map(|rstring| rstring.to_string())
22 .unwrap_or_else(|_| "unexpected inspect result".to_owned()),
23 Err(_) => "error calling inspect".to_owned(),
24 };
25 write!(f, "{}", msg)
26 }
27 NotImplemented(ref description) => write!(f, "{}", description),
28 }
29 }
30}
31
32impl fmt::Debug for ErrorKind {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 fmt::Display::fmt(self, f)
35 }
36}
37
38pub struct Error {
39 kind: ErrorKind,
40 context: Vec<String>,
41}
42
43impl Error {
44 pub fn chain_context<F, S>(mut self, func: F) -> Self
45 where
46 F: FnOnce() -> S,
47 S: Into<String>,
48 {
49 self.context.push(func().into());
50 self
51 }
52
53 fn describe_context(&self) -> String {
54 if self.context.is_empty() {
55 "".to_owned()
56 } else {
57 format!("\nContext from Rust:\n - {}", self.context.join("\n - "))
58 }
59 }
60}
61
62impl ::std::error::Error for Error {
63 fn description(&self) -> &str {
64 match self.kind {
65 Message(_) => "Generic Error",
66 RutieException(_) => "Rutie Exception",
67 NotImplemented(description) => description,
68 }
69 }
70}
71
72impl fmt::Display for Error {
73 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74 write!(f, "{:?}\n{}", self.kind, self.describe_context())
75 }
76}
77
78impl fmt::Debug for Error {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 fmt::Display::fmt(self, f)
81 }
82}
83
84impl From<ErrorKind> for Error {
85 fn from(kind: ErrorKind) -> Self {
86 Error {
87 kind,
88 context: vec![],
89 }
90 }
91}
92
93impl<'a> From<&'a str> for Error {
94 fn from(string: &'a str) -> Self {
95 ErrorKind::Message(string.into()).into()
96 }
97}
98
99impl From<String> for Error {
100 fn from(string: String) -> Self {
101 ErrorKind::Message(string).into()
102 }
103}
104impl From<rutie::AnyException> for Error {
105 fn from(exception: rutie::AnyException) -> Self {
106 ErrorKind::RutieException(exception).into()
107 }
108}
109
110impl From<rutie::AnyObject> for Error {
113 fn from(obj: rutie::AnyObject) -> Self {
114 obj.try_convert_to::<rutie::AnyException>()
115 .map(Into::into)
116 .unwrap_or_else(|_| "Error coercing AnyObject into AnyException".into())
117 }
118}
119
120impl serde::de::Error for Error {
121 fn custom<T>(msg: T) -> Self
122 where
123 T: fmt::Display,
124 {
125 format!("{}", msg).into()
126 }
127}
128
129impl serde::ser::Error for Error {
130 fn custom<T>(msg: T) -> Self
131 where
132 T: fmt::Display,
133 {
134 format!("{}", msg).into()
135 }
136}
137
138pub trait IntoException {
139 fn into_exception(self, default_class: rutie::Class) -> rutie::AnyException;
140}
141
142impl IntoException for Error {
143 fn into_exception(self, default_class: rutie::Class) -> rutie::AnyException {
144 match self.kind {
145 RutieException(ref exception) => {
146 let msg = format!("{}{}", exception.message(), self.describe_context());
147 exception.exception(Some(&msg))
148 }
149 _ => {
150 let msg = format!("{}", self);
151 let obj =
152 default_class.new_instance(&[rutie::RString::new_utf8(&msg).to_any_object()]);
153 rutie::AnyException::from(obj.value())
154 }
155 }
156 }
157}
158
159pub type Result<T> = ::std::result::Result<T, Error>;
160
161pub trait ResultExt {
165 fn chain_context<F, S>(self, func: F) -> Self
166 where
167 F: FnOnce() -> S,
168 S: Into<String>;
169}
170
171impl<T> ResultExt for Result<T> {
172 fn chain_context<F, S>(self, func: F) -> Self
173 where
174 F: FnOnce() -> S,
175 S: Into<String>,
176 {
177 match self {
178 Ok(ok) => Ok(ok),
179 Err(err) => Err(err.chain_context(func)),
180 }
181 }
182}