compio_py_dynamic_openssl/
error.rs1use std::{
13 borrow::Cow,
14 error,
15 ffi::{CStr, CString, c_char, c_int, c_ulong},
16 fmt, io, ptr,
17};
18
19use crate::sys as ffi;
20
21type ErrType = c_ulong;
22
23#[derive(Debug, Clone)]
24pub struct ErrorStack(Vec<Error>);
25
26impl ErrorStack {
27 pub fn get() -> Self {
28 let mut vec = vec![];
29 while let Some(err) = Error::get() {
30 vec.push(err);
31 }
32 ErrorStack(vec)
33 }
34
35 pub fn errors(&self) -> &[Error] {
36 &self.0
37 }
38}
39
40impl fmt::Display for ErrorStack {
41 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
42 if self.0.is_empty() {
43 return fmt.write_str("OpenSSL error");
44 }
45
46 let mut first = true;
47 for err in &self.0 {
48 if !first {
49 fmt.write_str(", ")?;
50 }
51 write!(fmt, "{}", err)?;
52 first = false;
53 }
54 Ok(())
55 }
56}
57
58impl error::Error for ErrorStack {}
59
60impl From<ErrorStack> for io::Error {
61 fn from(e: ErrorStack) -> io::Error {
62 io::Error::new(io::ErrorKind::Other, e)
63 }
64}
65
66impl From<ErrorStack> for fmt::Error {
67 fn from(_: ErrorStack) -> fmt::Error {
68 fmt::Error
69 }
70}
71
72#[derive(Clone)]
73pub struct Error {
74 code: ErrType,
75 file: ShimStr,
76 line: c_int,
77 func: Option<ShimStr>,
78 data: Option<Cow<'static, str>>,
79}
80
81unsafe impl Sync for Error {}
82unsafe impl Send for Error {}
83
84impl Error {
85 pub fn get() -> Option<Self> {
86 let ffi = crate::get();
87 unsafe {
88 let mut file = ptr::null();
89 let mut line = 0;
90 let mut func = ptr::null();
91 let mut data = ptr::null();
92 let mut flags = 0;
93 match ffi.ERR_get_error_all(&mut file, &mut line, &mut func, &mut data, &mut flags) {
94 0 => None,
95 code => {
96 let data = if flags & ffi::ERR_TXT_STRING != 0 {
97 let bytes = CStr::from_ptr(data as *const _).to_bytes();
98 let data = str::from_utf8(bytes).unwrap();
99 let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
100 Cow::Owned(data.to_string())
101 } else {
102 Cow::Borrowed(data)
103 };
104 Some(data)
105 } else {
106 None
107 };
108
109 let file = ShimStr::new(file);
110
111 let func = if func.is_null() {
112 None
113 } else {
114 Some(ShimStr::new(func))
115 };
116
117 Some(Error {
118 code,
119 file,
120 line,
121 func,
122 data,
123 })
124 }
125 }
126 }
127 }
128
129 pub fn code(&self) -> ErrType {
130 self.code
131 }
132
133 pub fn library(&self) -> Option<&'static str> {
134 let ffi = crate::get();
135 unsafe {
136 let cstr = (ffi.ERR_lib_error_string)(self.code);
137 if cstr.is_null() {
138 return None;
139 }
140 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
141 Some(str::from_utf8(bytes).unwrap())
142 }
143 }
144
145 pub fn library_code(&self) -> c_int {
146 let ffi = crate::get();
147 ffi.ERR_GET_LIB(self.code)
148 }
149
150 pub fn function(&self) -> Option<&str> {
151 self.func.as_ref().map(|s| s.as_str())
152 }
153
154 pub fn reason(&self) -> Option<&'static str> {
155 let ffi = crate::get();
156 unsafe {
157 let cstr = (ffi.ERR_reason_error_string)(self.code);
158 if cstr.is_null() {
159 return None;
160 }
161 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
162 Some(str::from_utf8(bytes).unwrap())
163 }
164 }
165
166 pub fn reason_code(&self) -> c_int {
167 let ffi = crate::get();
168 ffi.ERR_GET_REASON(self.code)
169 }
170
171 pub fn file(&self) -> &str {
172 self.file.as_str()
173 }
174
175 pub fn line(&self) -> u32 {
176 self.line as u32
177 }
178
179 pub fn data(&self) -> Option<&str> {
180 self.data.as_ref().map(|s| &**s)
181 }
182}
183
184impl fmt::Debug for Error {
185 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
186 let mut builder = fmt.debug_struct("Error");
187 builder.field("code", &self.code());
188 if let Some(library) = self.library() {
189 builder.field("library", &library);
190 }
191 if let Some(function) = self.function() {
192 builder.field("function", &function);
193 }
194 if let Some(reason) = self.reason() {
195 builder.field("reason", &reason);
196 }
197 builder.field("file", &self.file());
198 builder.field("line", &self.line());
199 if let Some(data) = self.data() {
200 builder.field("data", &data);
201 }
202 builder.finish()
203 }
204}
205
206impl fmt::Display for Error {
207 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(fmt, "error:{:08X}", self.code())?;
209 match self.library() {
210 Some(l) => write!(fmt, ":{}", l)?,
211 None => write!(fmt, ":lib({})", self.library_code())?,
212 }
213 match self.function() {
214 Some(f) => write!(fmt, ":{}", f)?,
215 None => write!(fmt, ":func({})", crate::get().ERR_GET_FUNC(self.code()))?,
216 }
217 match self.reason() {
218 Some(r) => write!(fmt, ":{}", r)?,
219 None => write!(fmt, ":reason({})", self.reason_code())?,
220 }
221 write!(
222 fmt,
223 ":{}:{}:{}",
224 self.file(),
225 self.line(),
226 self.data().unwrap_or("")
227 )
228 }
229}
230
231impl error::Error for Error {}
232
233#[derive(Clone)]
234enum ShimStr {
235 Owned(CString),
236 Borrowed(*const c_char),
237}
238
239impl ShimStr {
240 unsafe fn new(s: *const c_char) -> Self {
241 if crate::get().version_num < 0x30000000 {
242 ShimStr::Borrowed(s)
243 } else {
244 ShimStr::Owned(unsafe { CStr::from_ptr(s) }.to_owned())
245 }
246 }
247
248 fn as_str(&self) -> &str {
249 match self {
250 ShimStr::Owned(s) => s.to_str().unwrap(),
251 ShimStr::Borrowed(s) => unsafe { CStr::from_ptr(*s).to_str().unwrap() },
252 }
253 }
254}