vpp_plugin/vppinfra/
error.rs1use core::str;
7use std::{
8 error::Error as StdError,
9 ffi::{CStr, CString},
10 fmt::{self, Display},
11 mem::ManuallyDrop,
12};
13
14use super::{Vec, VecRef};
15use crate::bindings::{_clib_error_return, clib_error_t, uword, CLIB_ERROR_ERRNO_VALID};
16
17#[derive(Debug, Copy, Clone)]
21#[repr(transparent)]
22pub struct Error(clib_error_t);
23
24impl fmt::Display for Error {
25 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
26 if self.0.what.is_null() {
27 return write!(fmt, "<none>");
28 }
29
30 if !self.0.where_.is_null() {
31 let where_c = unsafe { CStr::from_ptr(self.0.where_.cast()) };
33 write!(fmt, "{}: ", where_c.to_string_lossy())?;
34 }
35 let what_v = unsafe { VecRef::from_raw(self.0.what) };
37 let what_str = str::from_utf8(what_v).unwrap_or("<invalid>");
38 write!(fmt, "{}", what_str)
39 }
40}
41
42#[derive(Debug)]
47pub struct ErrorStack(Vec<Error>);
48
49impl ErrorStack {
50 pub unsafe fn from_raw(ptr: *mut clib_error_t) -> Self {
57 Self(Vec::from_raw(ptr.cast()))
58 }
59
60 pub fn into_raw(self) -> *mut clib_error_t {
65 let errors = ManuallyDrop::new(self);
66 errors.0.as_mut_ptr().cast()
67 }
68
69 fn new_internal(
75 errors: Option<Self>,
76 what: String,
77 code: Option<crate::bindings::any>,
78 ) -> Self {
79 let errors_ptr = if let Some(errors) = errors {
80 errors.into_raw()
81 } else {
82 std::ptr::null_mut()
83 };
84 let what_c = CString::new(what).expect("message should not contain nul characters");
85 let flags = if code.is_some() {
86 CLIB_ERROR_ERRNO_VALID as uword
87 } else {
88 0
89 };
90 let code = code.unwrap_or_default();
91 unsafe {
95 Self::from_raw(_clib_error_return(
96 errors_ptr,
97 code,
98 flags,
99 std::ptr::null_mut(),
100 what_c.as_ptr(),
101 ))
102 }
103 }
104
105 pub fn new<E: StdError + Send + Sync + 'static>(e: E) -> Self {
113 Self::new_internal(None, e.to_string(), None)
114 }
115
116 pub fn msg<M>(message: M) -> Self
122 where
123 M: Display + Send + Sync + 'static,
124 {
125 Self::new_internal(None, message.to_string(), None)
126 }
127
128 pub fn context<C>(self, context: C) -> Self
134 where
135 C: Display + Send + Sync + 'static,
136 {
137 Self::new_internal(Some(self), context.to_string(), None)
138 }
139
140 pub fn errors(&self) -> &[Error] {
142 self.0.as_slice()
143 }
144}
145
146impl Drop for ErrorStack {
147 fn drop(&mut self) {
148 for e in self.errors() {
149 if e.0.what.is_null() {
150 continue;
151 }
152 let _ = unsafe { Vec::from_raw(e.0.what) };
155 }
156 }
157}
158
159impl Display for ErrorStack {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 let errs = self.errors();
162 if let Some(err) = errs.last() {
163 write!(f, "{}", err)?;
164 if errs.len() > 1 {
165 write!(f, "\n\nCaused by:")?;
166 for err in errs[..errs.len() - 1].iter().rev() {
167 writeln!(f)?;
168 write!(f, " {}", err)?;
169 }
170 }
171 } else {
172 write!(f, "Empty VPP error")?;
173 }
174 Ok(())
175 }
176}
177
178impl StdError for ErrorStack {}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 use crate::vppinfra::clib_mem_init;
185
186 #[test]
187 fn basic() {
188 clib_mem_init();
189
190 let io_e = std::io::Error::new(std::io::ErrorKind::AlreadyExists, "Already exists");
191 let e = ErrorStack::new(io_e);
192 assert_eq!(e.errors().len(), 1);
193 assert_eq!(e.to_string(), "Already exists");
194
195 let e = ErrorStack::msg("Unknown interface");
196 assert_eq!(e.errors().len(), 1);
197 assert_eq!(e.to_string(), "Unknown interface");
198
199 let e = e.context("Failed to enable feature");
200 assert_eq!(e.errors().len(), 2);
201 assert_eq!(
202 e.to_string(),
203 "\
204Failed to enable feature
205
206Caused by:
207 Unknown interface"
208 );
209 }
210
211 #[test]
212 fn ptrs() {
213 clib_mem_init();
214
215 let e = ErrorStack::msg("Unknown interface");
216 assert_eq!(e.errors().len(), 1);
217 assert_eq!(e.to_string(), "Unknown interface");
218
219 let ptr = e.into_raw();
220 let e = unsafe { ErrorStack::from_raw(ptr) };
222 assert_eq!(e.errors().len(), 1);
223 assert_eq!(e.to_string(), "Unknown interface");
224 }
225}