diff --git a/.cirrus.yml b/.cirrus.yml index 5896c777..34a242d2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -27,6 +27,22 @@ stable_test_task: - cargo fmt --all -- --check before_cache_script: rm -rf $CARGO_HOME/registry/index +no_std_test_task: + name: "Rust Stable (no_std)" + container: + image: rust:latest + cpu: 1 + memory: 2Gi + cargo_cache: + folder: $CARGO_HOME/registry + fingerprint_script: cat Cargo.toml + setup_script: + - rustup target add thumbv6m-none-eabi + primary_test_script: + - rustc --version + - cargo build --no-default-features --target thumbv6m-none-eabi + before_cache_script: rm -rf $CARGO_HOME/registry/index + nightly_test_task: name: "Rust Nightly" container: diff --git a/Cargo.toml b/Cargo.toml index e03d057d..23c88942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,8 @@ repository = "https://github.com/shepmaster/snafu" license = "MIT OR Apache-2.0" -keywords = ["error", "ergonomic", "library"] -categories = ["rust-patterns"] +keywords = ["error", "ergonomic", "library", "no_std"] +categories = ["rust-patterns", "no-std"] exclude = [ "/.cirrus.yml", @@ -24,10 +24,13 @@ exclude = [ all-features = true [features] -default = ["backtraces"] +default = ["backtraces", "std"] + +# Implement the `std::error::Error` trait. +std = [] # Adds the backtrace type -backtraces = ["snafu-derive/backtraces", "backtrace"] +backtraces = ["std", "snafu-derive/backtraces", "backtrace"] # Add conversion to `backtrace::Backtrace` backtrace-crate = ["backtraces"] diff --git a/compatibility-tests/without-backtrace/Cargo.toml b/compatibility-tests/without-backtrace/Cargo.toml index 91e9d4e3..1aafe16b 100644 --- a/compatibility-tests/without-backtrace/Cargo.toml +++ b/compatibility-tests/without-backtrace/Cargo.toml @@ -5,4 +5,4 @@ authors = ["Jake Goulding "] edition = "2018" [dependencies] -snafu = { path = "../..", default-features = false, features = [] } +snafu = { path = "../..", default-features = false, features = ["std"] } diff --git a/snafu-derive/src/lib.rs b/snafu-derive/src/lib.rs index db6b762c..d77f3f8d 100644 --- a/snafu-derive/src/lib.rs +++ b/snafu-derive/src/lib.rs @@ -1298,7 +1298,7 @@ impl<'a> quote::ToTokens for ContextSelector<'a> { let backtrace_field = match *backtrace_field { Some(ref field) => { let name = &field.name; - quote! { #name: std::default::Default::default(), } + quote! { #name: core::default::Default::default(), } } None => quote! {}, }; @@ -1308,7 +1308,7 @@ impl<'a> quote::ToTokens for ContextSelector<'a> { .zip(user_fields) .map(|(gen_ty, f)| { let Field { ref ty, .. } = *f; - quote! { #gen_ty: std::convert::Into<#ty> } + quote! { #gen_ty: core::convert::Into<#ty> } }) .chain(self.0.provided_where_clauses()) .collect(); @@ -1318,16 +1318,16 @@ impl<'a> quote::ToTokens for ContextSelector<'a> { impl<#(#generic_names,)*> #selector_name { #[doc = "Consume the selector and return a `Result` with the associated error"] - #visibility fn fail<#(#original_generics_without_defaults,)* __T>(self) -> std::result::Result<__T, #parameterized_enum_name> + #visibility fn fail<#(#original_generics_without_defaults,)* __T>(self) -> core::result::Result<__T, #parameterized_enum_name> where #(#where_clauses),* { let Self { #(#names),* } = self; let error = #enum_name::#variant_name { #backtrace_field - #( #names: std::convert::Into::into(#names) ),* + #( #names: core::convert::Into::into(#names) ),* }; - std::result::Result::Err(error) + core::result::Result::Err(error) } } } @@ -1367,7 +1367,7 @@ impl<'a> quote::ToTokens for ContextSelector<'a> { quote! { impl#generics_list snafu::IntoError<#parameterized_enum_name> for #selector_name where - #parameterized_enum_name: std::error::Error + snafu::ErrorCompat, + #parameterized_enum_name: snafu::Error + snafu::ErrorCompat, #(#where_clauses),* { type Source = #source_ty; @@ -1453,11 +1453,11 @@ impl<'a> quote::ToTokens for DisplayImpl<'a> { stream.extend({ quote! { - impl<#(#original_generics),*> std::fmt::Display for #parameterized_enum_name + impl<#(#original_generics),*> core::fmt::Display for #parameterized_enum_name where #(#where_clauses),* { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { #[allow(unused_variables)] match *self { #(#variants_to_display)* @@ -1509,13 +1509,13 @@ impl<'a> ErrorImpl<'a> { } = *source_field; quote! { #enum_name::#variant_name { ref #field_name, .. } => { - std::option::Option::Some(#field_name.as_error_source()) + core::option::Option::Some(#field_name.as_error_source()) } } } None => { quote! { - #enum_name::#variant_name { .. } => { std::option::Option::None } + #enum_name::#variant_name { .. } => { core::option::Option::None } } } } @@ -1543,7 +1543,7 @@ impl<'a> quote::ToTokens for ErrorImpl<'a> { let variants_to_source = &self.variants_to_source(); let cause_fn = quote! { - fn cause(&self) -> Option<&dyn std::error::Error> { + fn cause(&self) -> Option<&dyn snafu::Error> { use snafu::AsErrorSource; match *self { #(#variants_to_source)* @@ -1552,7 +1552,7 @@ impl<'a> quote::ToTokens for ErrorImpl<'a> { }; let source_fn = quote! { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + fn source(&self) -> Option<&(dyn snafu::Error + 'static)> { use snafu::AsErrorSource; match *self { #(#variants_to_source)* @@ -1562,9 +1562,9 @@ impl<'a> quote::ToTokens for ErrorImpl<'a> { stream.extend({ quote! { - impl<#(#original_generics),*> std::error::Error for #parameterized_enum_name + impl<#(#original_generics),*> snafu::Error for #parameterized_enum_name where - Self: std::fmt::Debug + std::fmt::Display, + Self: core::fmt::Debug + core::fmt::Display, #(#where_clauses),* { #description_fn @@ -1606,12 +1606,12 @@ impl<'a> ErrorCompatImpl<'a> { .. } = *backtrace_field; quote! { - #enum_name::#variant_name { ref #field_name, .. } => { std::option::Option::Some(#field_name) } + #enum_name::#variant_name { ref #field_name, .. } => { core::option::Option::Some(#field_name) } } } _ => { quote! { - #enum_name::#variant_name { .. } => { std::option::Option::None } + #enum_name::#variant_name { .. } => { core::option::Option::None } } } } @@ -1673,19 +1673,19 @@ impl StructInfo { let description_fn = quote! { fn description(&self) -> &str { - std::error::Error::description(&self.0) + snafu::Error::description(&self.0) } }; let cause_fn = quote! { - fn cause(&self) -> Option<&dyn std::error::Error> { - std::error::Error::cause(&self.0) + fn cause(&self) -> Option<&dyn snafu::Error> { + snafu::Error::cause(&self.0) } }; let source_fn = quote! { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - std::error::Error::source(&self.0) + fn source(&self) -> Option<&(dyn snafu::Error + 'static)> { + snafu::Error::source(&self.0) } }; @@ -1700,7 +1700,7 @@ impl StructInfo { }; let error_impl = quote! { - impl#generics std::error::Error for #parameterized_struct_name + impl#generics snafu::Error for #parameterized_struct_name where #(#where_clauses),* { @@ -1720,18 +1720,18 @@ impl StructInfo { }; let display_impl = quote! { - impl#generics std::fmt::Display for #parameterized_struct_name + impl#generics core::fmt::Display for #parameterized_struct_name where #(#where_clauses),* { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::Display::fmt(&self.0, f) + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(&self.0, f) } } }; let from_impl = quote! { - impl#generics std::convert::From<#inner_type> for #parameterized_struct_name + impl#generics core::convert::From<#inner_type> for #parameterized_struct_name where #(#where_clauses),* { diff --git a/src/futures/try_future.rs b/src/futures/try_future.rs index 1d7a4d4c..77eda6a7 100644 --- a/src/futures/try_future.rs +++ b/src/futures/try_future.rs @@ -2,15 +2,15 @@ //! //! [`TryFuture`]: futures_core::future::TryFuture -use crate::{ErrorCompat, IntoError}; -use futures_core::future::TryFuture; -use pin_project::pin_project; -use std::{ +use crate::{Error, ErrorCompat, IntoError}; +use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context as TaskContext, Poll}, }; +use futures_core::future::TryFuture; +use pin_project::pin_project; /// Additions to [`TryFuture`]. pub trait TryFutureExt: TryFuture + Sized { @@ -49,7 +49,7 @@ pub trait TryFutureExt: TryFuture + Sized { fn context(self, context: C) -> Context where C: IntoError, - E: std::error::Error + ErrorCompat; + E: Error + ErrorCompat; /// Extend a [`TryFuture`]'s error with lazily-generated context-sensitive /// information. @@ -87,7 +87,7 @@ pub trait TryFutureExt: TryFuture + Sized { where F: FnOnce() -> C, C: IntoError, - E: std::error::Error + ErrorCompat; + E: Error + ErrorCompat; } impl TryFutureExt for Fut @@ -97,7 +97,7 @@ where fn context(self, context: C) -> Context where C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { Context { inner: self, @@ -110,7 +110,7 @@ where where F: FnOnce() -> C, C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { WithContext { inner: self, @@ -137,7 +137,7 @@ impl Future for Context where Fut: TryFuture, C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Output = Result; @@ -173,7 +173,7 @@ where Fut: TryFuture, F: FnOnce() -> C, C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Output = Result; diff --git a/src/futures/try_stream.rs b/src/futures/try_stream.rs index 7f995c85..bf738cfd 100644 --- a/src/futures/try_stream.rs +++ b/src/futures/try_stream.rs @@ -2,14 +2,14 @@ //! //! [`TryStream`]: futures_core::TryStream -use crate::{ErrorCompat, IntoError}; -use futures_core::stream::{Stream, TryStream}; -use pin_project::pin_project; -use std::{ +use crate::{Error, ErrorCompat, IntoError}; +use core::{ marker::PhantomData, pin::Pin, task::{Context as TaskContext, Poll}, }; +use futures_core::stream::{Stream, TryStream}; +use pin_project::pin_project; /// Additions to [`TryStream`]. pub trait TryStreamExt: TryStream + Sized { @@ -49,7 +49,7 @@ pub trait TryStreamExt: TryStream + Sized { fn context(self, context: C) -> Context where C: IntoError + Clone, - E: std::error::Error + ErrorCompat; + E: Error + ErrorCompat; /// Extend a [`TryStream`]'s error with lazily-generated /// context-sensitive information. @@ -88,7 +88,7 @@ pub trait TryStreamExt: TryStream + Sized { where F: FnMut() -> C, C: IntoError, - E: std::error::Error + ErrorCompat; + E: Error + ErrorCompat; } impl TryStreamExt for St @@ -98,7 +98,7 @@ where fn context(self, context: C) -> Context where C: IntoError + Clone, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { Context { inner: self, @@ -111,7 +111,7 @@ where where F: FnMut() -> C, C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { WithContext { inner: self, @@ -138,7 +138,7 @@ impl Stream for Context where St: TryStream, C: IntoError + Clone, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Item = Result; @@ -177,7 +177,7 @@ where St: TryStream, F: FnMut() -> C, C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Item = Result; diff --git a/src/futures01/future.rs b/src/futures01/future.rs index ac48c87f..8a436c62 100644 --- a/src/futures01/future.rs +++ b/src/futures01/future.rs @@ -2,10 +2,9 @@ //! //! [`Future`]: futures01_crate::Future -use crate::{ErrorCompat, IntoError}; +use crate::{Error, ErrorCompat, IntoError}; +use core::marker::PhantomData; use futures01::{Async, Future}; -use std::error; -use std::marker::PhantomData; /// Additions to [`Future`]. pub trait FutureExt: Future + Sized { @@ -47,7 +46,7 @@ pub trait FutureExt: Future + Sized { fn context(self, context: C) -> Context where C: IntoError, - E: error::Error + ErrorCompat; + E: Error + ErrorCompat; /// Extend a [`Future`]'s error with lazily-generated context-sensitive /// information. @@ -88,7 +87,7 @@ pub trait FutureExt: Future + Sized { where F: FnOnce() -> C, C: IntoError, - E: error::Error + ErrorCompat; + E: Error + ErrorCompat; } impl FutureExt for Fut @@ -98,7 +97,7 @@ where fn context(self, context: C) -> Context where C: IntoError, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { Context { future: self, @@ -111,7 +110,7 @@ where where F: FnOnce() -> C, C: IntoError, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { WithContext { future: self, @@ -134,7 +133,7 @@ impl Future for Context where Fut: Future, C: IntoError, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Item = Fut::Item; type Error = E; @@ -163,7 +162,7 @@ where Fut: Future, F: FnOnce() -> C, C: IntoError, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Item = Fut::Item; type Error = E; diff --git a/src/futures01/stream.rs b/src/futures01/stream.rs index 81a25818..8d88646d 100644 --- a/src/futures01/stream.rs +++ b/src/futures01/stream.rs @@ -2,10 +2,9 @@ //! //! [`Stream`]: futures01_crate::Stream -use crate::{ErrorCompat, IntoError}; +use crate::{Error, ErrorCompat, IntoError}; +use core::marker::PhantomData; use futures01::{Async, Stream}; -use std::error; -use std::marker::PhantomData; /// Additions to [`Stream`]. pub trait StreamExt: Stream + Sized { @@ -47,7 +46,7 @@ pub trait StreamExt: Stream + Sized { fn context(self, context: C) -> Context where C: IntoError + Clone, - E: error::Error + ErrorCompat; + E: Error + ErrorCompat; /// Extend a [`Stream`]'s error with lazily-generated context-sensitive /// information. @@ -88,7 +87,7 @@ pub trait StreamExt: Stream + Sized { where F: FnMut() -> C, C: IntoError, - E: error::Error + ErrorCompat; + E: Error + ErrorCompat; } impl StreamExt for St @@ -98,7 +97,7 @@ where fn context(self, context: C) -> Context where C: IntoError + Clone, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { Context { stream: self, @@ -111,7 +110,7 @@ where where F: FnMut() -> C, C: IntoError, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { WithContext { stream: self, @@ -134,7 +133,7 @@ impl Stream for Context where St: Stream, C: IntoError + Clone, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Item = St::Item; type Error = E; @@ -160,7 +159,7 @@ where St: Stream, F: FnMut() -> C, C: IntoError, - E: error::Error + ErrorCompat, + E: Error + ErrorCompat, { type Item = St::Item; type Error = E; diff --git a/src/lib.rs b/src/lib.rs index 6a327439..0a264d99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![deny(missing_docs)] +#![cfg_attr(not(any(feature = "std", test)), no_std)] //! # SNAFU //! @@ -127,7 +128,15 @@ generate_guide! { doc_comment::doctest!("../README.md", readme_tests); -use std::error; +#[cfg(any(feature = "std", test))] +#[doc(hidden)] +pub use std::error::Error; + +#[cfg(not(any(feature = "std", test)))] +mod no_std_error; +#[cfg(not(any(feature = "std", test)))] +#[doc(hidden)] +pub use no_std_error::Error; /// Ensure a condition is true. If it is not, return from the function /// with an error. @@ -195,7 +204,7 @@ pub trait ResultExt: Sized { fn context(self, context: C) -> Result where C: IntoError, - E2: std::error::Error + ErrorCompat; + E2: Error + ErrorCompat; /// Extend a [`Result`][]'s error with lazily-generated context-sensitive information. /// @@ -235,14 +244,14 @@ pub trait ResultExt: Sized { where F: FnOnce() -> C, C: IntoError, - E2: std::error::Error + ErrorCompat; + E2: Error + ErrorCompat; #[doc(hidden)] #[deprecated(since = "0.4.0", note = "use ResultExt::context instead")] fn eager_context(self, context: C) -> Result where C: IntoError, - E2: std::error::Error + ErrorCompat, + E2: Error + ErrorCompat, { self.context(context) } @@ -253,17 +262,17 @@ pub trait ResultExt: Sized { where F: FnOnce() -> C, C: IntoError, - E2: std::error::Error + ErrorCompat, + E2: Error + ErrorCompat, { self.with_context(context) } } -impl ResultExt for std::result::Result { +impl ResultExt for Result { fn context(self, context: C) -> Result where C: IntoError, - E2: std::error::Error + ErrorCompat, + E2: Error + ErrorCompat, { self.map_err(|error| context.into_error(error)) } @@ -272,7 +281,7 @@ impl ResultExt for std::result::Result { where F: FnOnce() -> C, C: IntoError, - E2: std::error::Error + ErrorCompat, + E2: Error + ErrorCompat, { self.map_err(|error| { let context = context(); @@ -322,7 +331,7 @@ pub trait OptionExt: Sized { fn context(self, context: C) -> Result where C: IntoError, - E: std::error::Error + ErrorCompat; + E: Error + ErrorCompat; /// Convert an [`Option`][] into a [`Result`][] with /// lazily-generated context-sensitive information. @@ -363,14 +372,14 @@ pub trait OptionExt: Sized { where F: FnOnce() -> C, C: IntoError, - E: std::error::Error + ErrorCompat; + E: Error + ErrorCompat; #[doc(hidden)] #[deprecated(since = "0.4.0", note = "use OptionExt::context instead")] fn eager_context(self, context: C) -> Result where C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { self.context(context).map_err(Into::into) } @@ -381,7 +390,7 @@ pub trait OptionExt: Sized { where F: FnOnce() -> C, C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { self.with_context(context).map_err(Into::into) } @@ -391,7 +400,7 @@ impl OptionExt for Option { fn context(self, context: C) -> Result where C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { self.ok_or_else(|| context.into_error(NoneError)) } @@ -400,7 +409,7 @@ impl OptionExt for Option { where F: FnOnce() -> C, C: IntoError, - E: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { self.ok_or_else(|| context().into_error(NoneError)) } @@ -439,6 +448,7 @@ where } } +#[cfg(any(feature = "std", test))] impl ErrorCompat for Box where E: ErrorCompat, @@ -495,35 +505,35 @@ pub trait AsErrorSource { /// For maximum effectiveness, this needs to be called as a method /// to benefit from Rust's automatic dereferencing of method /// receivers. - fn as_error_source(&self) -> &(dyn error::Error + 'static); + fn as_error_source(&self) -> &(dyn Error + 'static); } -impl AsErrorSource for dyn error::Error + 'static { - fn as_error_source(&self) -> &(dyn error::Error + 'static) { +impl AsErrorSource for dyn Error + 'static { + fn as_error_source(&self) -> &(dyn Error + 'static) { self } } -impl AsErrorSource for dyn error::Error + Send + 'static { - fn as_error_source(&self) -> &(dyn error::Error + 'static) { +impl AsErrorSource for dyn Error + Send + 'static { + fn as_error_source(&self) -> &(dyn Error + 'static) { self } } -impl AsErrorSource for dyn error::Error + Sync + 'static { - fn as_error_source(&self) -> &(dyn error::Error + 'static) { +impl AsErrorSource for dyn Error + Sync + 'static { + fn as_error_source(&self) -> &(dyn Error + 'static) { self } } -impl AsErrorSource for dyn error::Error + Send + Sync + 'static { - fn as_error_source(&self) -> &(dyn error::Error + 'static) { +impl AsErrorSource for dyn Error + Send + Sync + 'static { + fn as_error_source(&self) -> &(dyn Error + 'static) { self } } -impl AsErrorSource for T { - fn as_error_source(&self) -> &(dyn error::Error + 'static) { +impl AsErrorSource for T { + fn as_error_source(&self) -> &(dyn Error + 'static) { self } } @@ -533,13 +543,13 @@ impl AsErrorSource for T { /// /// It is expected that most users of SNAFU will not directly interact /// with this trait. -pub trait IntoError +pub trait IntoError where - Error: std::error::Error + ErrorCompat, + E: Error + ErrorCompat, { /// The underlying error type Source; /// Combine the information to produce the error - fn into_error(self, source: Self::Source) -> Error; + fn into_error(self, source: Self::Source) -> E; } diff --git a/src/no_std_error.rs b/src/no_std_error.rs new file mode 100644 index 00000000..d21969da --- /dev/null +++ b/src/no_std_error.rs @@ -0,0 +1,45 @@ +#![allow(missing_docs)] + +use core::fmt::{Debug, Display}; + +pub trait Error: Debug + Display { + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + fn cause(&self) -> Option<&dyn Error> { + self.source() + } + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} + +macro_rules! impl_error { + ($($e:path),*) => { + $( + impl Error for $e {} + )* + } +} + +// All errors supported by our minimum suported Rust version can be supported by +// default. +impl_error![ + core::str::ParseBoolError, // 1.0 + core::str::Utf8Error, // 1.0 + core::num::ParseIntError, // 1.0 + core::num::ParseFloatError, // 1.0 + core::char::DecodeUtf16Error, // 1.9 + core::fmt::Error, // 1.11 + core::cell::BorrowMutError, // 1.13 + core::cell::BorrowError, // 1.13 + core::char::ParseCharError // 1.20 +]; + +// We can gate these together with std futures. +#[cfg(feature = "unstable-futures")] +impl_error![ + core::num::TryFromIntError, // 1.34 + core::array::TryFromSliceError, // 1.34 + core::char::CharTryFromError // 1.34 +];