| // Part of the Crubit project, under the Apache License v2.0 with LLVM | |
| // Exceptions. See /LICENSE for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| //! Placeholder module for working with collections of Errors that have not yet | |
| //! been issued. | |
| //! | |
| //! This approach is intended to allow for continuing with compilation after the | |
| //! first error has been encountered, allowing multiple errors to be issued and | |
| //! allowing the bindings generator to guess at the resulting API. This last | |
| //! step is especially import in that it allows us to e.g. generate a mock | |
| //! implementation for a function that is declared in a header, even if the | |
| //! *real* function can't be generated. We can then issue a descriptive error | |
| //! from the Rust compiler when the user attempts to invoke the mock function. | |
| use arc_anyhow::Error; | |
| use core::cell::RefCell; | |
| // Re-exported for macro use. | |
| #[doc(hidden)] | |
| pub mod macro_internals { | |
| pub use error_report::anyhow; | |
| } | |
| /// A collection of errors that have not yet been issued. | |
| /// | |
| /// A non-empty collection of errors must either be `consolidate`d or | |
| /// `discard`ed before it is dropped. Failure to do so will result in a panic. | |
| #[derive(Default)] | |
| pub struct Errors { | |
| list: RefCell<Vec<Error>>, | |
| } | |
| impl Errors { | |
| pub fn new() -> Errors { | |
| Default::default() | |
| } | |
| pub fn add(&self, error: Error) { | |
| self.list.borrow_mut().push(error); | |
| } | |
| pub fn is_empty(&self) -> bool { | |
| self.list.borrow().is_empty() | |
| } | |
| /// If the supplied result contains an error, add it to the list and return None. Otherwise | |
| /// return the result. | |
| pub fn consume_error<T>(&self, r: Result<T, Error>) -> Option<T> { | |
| match r { | |
| Ok(t) => Some(t), | |
| Err(e) => { | |
| self.add(e); | |
| None | |
| } | |
| } | |
| } | |
| /// Consolidates any errors into a single error message. | |
| //// | |
| /// Returns `Ok` if no errors have been added. | |
| pub fn consolidate(&self) -> Result<(), error_report::ErrorList> { | |
| let errors = self.list.take(); | |
| if !errors.is_empty() { | |
| return Err(error_report::ErrorList::from(errors)); | |
| } | |
| Ok(()) | |
| } | |
| /// If `res` is an error, adds the error to the list and consolidates the | |
| /// list into a single error message. | |
| /// | |
| /// Returns `res` unchanged if it is `Ok`. | |
| pub fn consolidate_on_err<T>( | |
| &self, | |
| res: Result<T, Error>, | |
| ) -> Result<T, error_report::ErrorList> { | |
| res.map_err(|error| { | |
| self.add(error); | |
| self.consolidate().unwrap_err() | |
| }) | |
| } | |
| /// Discards without reporting errors. | |
| pub fn discard(&self) { | |
| self.list.take(); | |
| } | |
| } | |
| impl Drop for Errors { | |
| fn drop(&mut self) { | |
| let errors = self.list.get_mut(); | |
| if !errors.is_empty() { | |
| panic!( | |
| "`Error`s dropped without first calling `consolidate` or `discard`:\n{}", | |
| errors.iter().map(|error| error.to_string()).collect::<Vec<_>>().join("\n") | |
| ); | |
| } | |
| } | |
| } | |
| /// Similar to `bail!`, but accepts an `Errors` instance as the first argument. | |
| /// | |
| /// Only use this macro if it is not possible to continue after an error has | |
| /// been generated. Prefer instead to add an entry to `Errors`. | |
| /// | |
| /// The error will be added to the provided `Errors` list before returning | |
| /// `Err(WillError)`. | |
| /// | |
| /// This macro is intended to be used in a function that returns `ErrorsOr<T>`. | |
| #[macro_export] | |
| macro_rules! bail_to_errors { | |
| ($errors:expr, $($arg:tt)*) => { | |
| { | |
| $errors.add(anyhow!($($arg)*)); | |
| return Err($crate::WillError) | |
| } | |
| } | |
| } | |
| /// A promise that an error has been added to an `Errors`. | |
| pub struct WillError; | |
| /// A `Result` alias indicating that the value may not be available due to | |
| /// errors that have been added to an `Errors` instance. | |
| /// | |
| /// Only use this macro if it is not possible to continue after an error has | |
| /// been generated. Prefer instead to add an entry to `Errors` and continue | |
| /// successfully. | |
| pub type ErrorsOr<T> = Result<T, WillError>; |