diff --git a/packages/libs/error-stack/CHANGELOG.md b/packages/libs/error-stack/CHANGELOG.md index 772ee56a8b1..76f26a252f3 100644 --- a/packages/libs/error-stack/CHANGELOG.md +++ b/packages/libs/error-stack/CHANGELOG.md @@ -4,13 +4,14 @@ All notable changes to `error-stack` will be documented in this file. ## 0.2.0 - Unreleased -### Features +### Breaking Changes -- Implement [`Termination`](https://doc.rust-lang.org/stable/std/process/trait.Termination.html) for `Report` ([#671](https://github.com/hashintel/hash/pull/671)) +- Use `Provider` API from `core::any` ([#697](https://github.com/hashintel/hash/pull/697)) +- Hide `futures-core` feature ([#695](https://github.com/hashintel/hash/pull/695)) -### Fixes +### Features -- Hide `futures-core` feature ([#695](https://github.com/hashintel/hash/pull/695)) +- Implement [`Termination`](https://doc.rust-lang.org/stable/std/process/trait.Termination.html) for `Report` ([#671](https://github.com/hashintel/hash/pull/671)) ## [0.1.0](https://github.com/hashintel/hash/tree/d14efbc38559fc38d36e03ebdd499b44cb80c668/packages/libs/error-stack) - 2022-06-10 diff --git a/packages/libs/error-stack/src/context.rs b/packages/libs/error-stack/src/context.rs index 79dd00f47d1..f6cfe69d212 100644 --- a/packages/libs/error-stack/src/context.rs +++ b/packages/libs/error-stack/src/context.rs @@ -1,9 +1,9 @@ -use core::fmt; - #[cfg(nightly)] -use crate::provider::Demand; +use core::any::Demand; #[cfg(all(nightly, any(feature = "std", feature = "spantrace")))] -use crate::provider::Provider; +use core::any::Provider; +use core::fmt; + use crate::Report; /// Defines the current context of a [`Report`]. diff --git a/packages/libs/error-stack/src/frame/attachment.rs b/packages/libs/error-stack/src/frame/attachment.rs index e2a227e6711..a56ba97878c 100644 --- a/packages/libs/error-stack/src/frame/attachment.rs +++ b/packages/libs/error-stack/src/frame/attachment.rs @@ -1,5 +1,5 @@ #[cfg(nightly)] -use crate::provider::{Demand, Provider}; +use core::any::{Demand, Provider}; /// Wrapper around an attachment to provide the inner type. #[repr(transparent)] diff --git a/packages/libs/error-stack/src/frame/mod.rs b/packages/libs/error-stack/src/frame/mod.rs index 0b4835b4ed5..833037d9316 100644 --- a/packages/libs/error-stack/src/frame/mod.rs +++ b/packages/libs/error-stack/src/frame/mod.rs @@ -4,12 +4,12 @@ mod kind; mod vtable; use alloc::boxed::Box; +#[cfg(nightly)] +use core::any::{self, Demand, Provider}; use core::{fmt, mem, mem::ManuallyDrop, panic::Location, ptr::NonNull}; pub use self::kind::{AttachmentKind, FrameKind}; use self::{erasable::ErasableFrame, vtable::VTable}; -#[cfg(nightly)] -use crate::provider::{self, Demand, Provider}; use crate::{frame::attachment::AttachmentProvider, Context}; /// A single context or attachment inside of a [`Report`]. @@ -140,7 +140,7 @@ impl Frame { where T: ?Sized + 'static, { - provider::request_ref(self) + any::request_ref(self) } /// Requests the value of `T` from the `Frame` if provided. @@ -150,7 +150,7 @@ impl Frame { where T: 'static, { - provider::request_value(self) + any::request_value(self) } /// Returns if `T` is the held context or attachment by this frame. diff --git a/packages/libs/error-stack/src/frame/vtable.rs b/packages/libs/error-stack/src/frame/vtable.rs index bd12621f224..39c4ede54d5 100644 --- a/packages/libs/error-stack/src/frame/vtable.rs +++ b/packages/libs/error-stack/src/frame/vtable.rs @@ -1,4 +1,6 @@ use alloc::boxed::Box; +#[cfg(nightly)] +use core::any::{Demand, Provider}; use core::{ any::TypeId, fmt, @@ -6,10 +8,7 @@ use core::{ }; #[cfg(nightly)] -use crate::{ - frame::attachment::AttachmentProvider, - provider::{Demand, Provider}, -}; +use crate::frame::attachment::AttachmentProvider; use crate::{ frame::{kind::AttachmentKind, ErasableFrame}, Context, FrameKind, diff --git a/packages/libs/error-stack/src/lib.rs b/packages/libs/error-stack/src/lib.rs index a3b79b1cf17..30ca38d3b61 100644 --- a/packages/libs/error-stack/src/lib.rs +++ b/packages/libs/error-stack/src/lib.rs @@ -299,12 +299,7 @@ //! return an iterator of all provided values with the specified type. The value, which was provided //! most recently will be returned first. //! -//! **Currently, the API has not yet landed in `core::any`, thus in the meantime it has been -//! included in the library implementation and is available at [`error_stack::provider`]. Using it -//! requires a nightly compiler.** -//! //! [`attach`]: Report::attach -//! [`error_stack::provider`]: crate::provider //! [`Provider` API]: https://rust-lang.github.io/rfcs/3192-dyno.html //! //! ### Macros for Convenience @@ -371,6 +366,7 @@ //! [`SpanTrace`]: tracing_error::SpanTrace #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(nightly, feature(provide_any))] #![cfg_attr(all(doc, nightly), feature(doc_auto_cfg))] #![cfg_attr(all(nightly, feature = "std"), feature(backtrace))] #![warn( @@ -397,8 +393,6 @@ mod context; mod ext; #[cfg(feature = "hooks")] mod hook; -#[cfg(nightly)] -pub mod provider; #[cfg(test)] pub(crate) mod test_helper; diff --git a/packages/libs/error-stack/src/provider.rs b/packages/libs/error-stack/src/provider.rs deleted file mode 100644 index ec3c553d772..00000000000 --- a/packages/libs/error-stack/src/provider.rs +++ /dev/null @@ -1,422 +0,0 @@ -//! Contains the [`Provider`] trait and accompanying API, which enable trait objects to provide data -//! based on typed requests, an alternate form of runtime reflection. -//! -//! # `Provider` and `Demand` -//! -//! `Provider` and the associated APIs support generic, type-driven access to data, and a mechanism -//! for implementers to provide such data. The key parts of the interface are the `Provider` -//! trait for objects which can provide data, and the [`request_value`] and [`request_ref`] -//! functions for requesting data from an object which implements `Provider`. Generally, end users -//! should not call `request_*` directly, they are helper functions for intermediate implementers -//! to use to implement a user-facing interface. -//! -//! Typically, a data provider is a trait object of a trait which extends `Provider`. A user will -//! request data from a trait object by specifying the type of the data. -//! -//! ## Data flow -//! -//! * A user requests an object of a specific type, which is delegated to `request_value` or -//! `request_ref` -//! * `request_*` creates a `Demand` object and passes it to `Provider::provide` -//! * The data provider's implementation of `Provider::provide` tries providing values of different -//! types using `Demand::provide_*`. If the type matches the type requested by the user, the value -//! will be stored in the `Demand` object. -//! * `request_*` unpacks the `Demand` object and returns any stored value to the user. -//! -//! ## Examples -//! -//! ``` -//! # #![allow(dead_code)] -//! use error_stack::provider::{request_ref, Demand, Provider}; -//! -//! // Definition of MyTrait, a data provider. -//! trait MyTrait: Provider { -//! // ... -//! } -//! -//! // Methods on `MyTrait` trait objects. -//! impl dyn MyTrait + '_ { -//! /// Get a reference to a field of the implementing struct. -//! pub fn get_context_by_ref(&self) -> Option<&T> { -//! request_ref::(self) -//! } -//! } -//! -//! // Downstream implementation of `MyTrait` and `Provider`. -//! # struct SomeConcreteType { some_string: String } -//! impl MyTrait for SomeConcreteType { -//! // ... -//! } -//! -//! impl Provider for SomeConcreteType { -//! fn provide<'a>(&'a self, demand: &mut Demand<'a>) { -//! // Provide a string reference. We could provide multiple values with -//! // different types here. -//! demand.provide_ref::(&self.some_string); -//! } -//! } -//! -//! // Downstream usage of `MyTrait`. -//! fn use_my_trait(obj: &dyn MyTrait) { -//! // Request a &String from obj. -//! let _ = obj.get_context_by_ref::().unwrap(); -//! } -//! ``` -//! -//! In this example, if the concrete type of `obj` in `use_my_trait` is `SomeConcreteType`, then -//! the `get_context_ref` call will return a reference to `obj.some_string` with type `&String`. - -/////////////////////////////////////////////////////////////////////////////// -// Provider trait -/////////////////////////////////////////////////////////////////////////////// - -extern crate alloc; - -use core::any::TypeId; - -/// Trait implemented by a type which can dynamically provide values based on type. -pub trait Provider { - /// Data providers should implement this method to provide *all* values they are able to - /// provide by using `demand`. - /// - /// # Examples - /// - /// Provides a reference to a field with type `String` as a `&str`. - /// - /// ```rust - /// use error_stack::provider::{Demand, Provider}; - /// # struct SomeConcreteType { field: String } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_ref::(&self.field); - /// } - /// } - /// ``` - fn provide<'a>(&'a self, demand: &mut Demand<'a>); -} - -/// Request a value from the `Provider`. -/// -/// # Examples -/// -/// Get a string value from a provider. -/// -/// ```rust -/// use error_stack::provider::{request_value, Provider}; -/// -/// # #[allow(dead_code)] -/// fn get_string(provider: &P) -> String { -/// request_value::(provider).unwrap() -/// } -/// ``` -pub fn request_value<'a, T, P>(provider: &'a P) -> Option -where - T: 'static, - P: Provider + ?Sized, -{ - request_by_type_tag::<'a, tags::Value, P>(provider) -} - -/// Request a reference from the `Provider`. -/// -/// # Examples -/// -/// Get a string reference from a provider. -/// -/// ```rust -/// use error_stack::provider::{request_ref, Provider}; -/// -/// # #[allow(dead_code)] -/// fn get_str(provider: &P) -> &str { -/// request_ref::(provider).unwrap() -/// } -/// ``` -pub fn request_ref<'a, T, P>(provider: &'a P) -> Option<&'a T> -where - T: 'static + ?Sized, - P: Provider + ?Sized, -{ - request_by_type_tag::<'a, tags::Ref>, P>(provider) -} - -/// Request a specific value by tag from the `Provider`. -fn request_by_type_tag<'a, I, P>(provider: &'a P) -> Option -where - I: tags::Type<'a>, - P: Provider + ?Sized, -{ - let mut tagged = TaggedOption::<'a, I>(None); - provider.provide(tagged.as_demand()); - tagged.0 -} - -/////////////////////////////////////////////////////////////////////////////// -// Demand and its methods -/////////////////////////////////////////////////////////////////////////////// - -/// A helper object for providing data by type. -/// -/// A data provider provides values by calling this type's provide methods. -#[repr(transparent)] -pub struct Demand<'a>(dyn Erased<'a> + 'a); - -impl<'a> Demand<'a> { - /// Create a new `&mut Demand` from a `&mut dyn Erased` trait object. - fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Demand<'a> { - // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Demand<'a>` is safe since - // `Demand` is repr(transparent). - unsafe { &mut *(erased as *mut dyn Erased as *mut Demand) } - } - - /// Provide a value or other type with only static lifetimes. - /// - /// # Examples - /// - /// Provides a `String` by cloning. - /// - /// ```rust - /// use error_stack::provider::{Demand, Provider}; - /// # struct SomeConcreteType { field: String } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_value::(|| self.field.clone()); - /// } - /// } - /// ``` - pub fn provide_value(&mut self, fulfil: F) -> &mut Self - where - T: 'static, - F: FnOnce() -> T, - { - self.provide_with::, F>(fulfil) - } - - /// Provide a reference, note that the referee type must be bounded by `'static`, - /// but may be unsized. - /// - /// # Examples - /// - /// Provides a reference to a field as a `&str`. - /// - /// ```rust - /// use error_stack::provider::{Demand, Provider}; - /// # struct SomeConcreteType { field: String } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_ref::(&self.field); - /// } - /// } - /// ``` - pub fn provide_ref(&mut self, value: &'a T) -> &mut Self { - self.provide::>>(value) - } - - /// Provide a value with the given `Type` tag. - fn provide(&mut self, value: I::Reified) -> &mut Self - where - I: tags::Type<'a>, - { - if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { - res.0 = Some(value); - } - self - } - - /// Provide a value with the given `Type` tag, using a closure to prevent unnecessary work. - fn provide_with(&mut self, fulfil: F) -> &mut Self - where - I: tags::Type<'a>, - F: FnOnce() -> I::Reified, - { - if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { - res.0 = Some(fulfil()); - } - self - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Type tags -/////////////////////////////////////////////////////////////////////////////// - -mod tags { - //! Type tags are used to identify a type using a separate value. This module includes type tags - //! for some very common types. - //! - //! Many users of the provider APIs will not need to use type tags at all. But if you want to - //! use them with more complex types (typically those including lifetime parameters), you will - //! need to write your own tags. - - use core::marker::PhantomData; - - /// This trait is implemented by specific tag types in order to allow - /// describing a type which can be requested for a given lifetime `'a`. - /// - /// A few example implementations for type-driven tags can be found in this - /// module, although crates may also implement their own tags for more - /// complex types with internal lifetimes. - pub trait Type<'a>: Sized + 'static { - /// The type of values which may be tagged by this tag for the given - /// lifetime. - type Reified: 'a; - } - - /// Similar to the [`Type`] trait, but represents a type which may be unsized (i.e., has a - /// `'Sized` bound). E.g., `str`. - pub trait MaybeSizedType<'a>: Sized + 'static { - type Reified: 'a + ?Sized; - } - - impl<'a, T: Type<'a>> MaybeSizedType<'a> for T { - type Reified = T::Reified; - } - - /// Type-based tag for types bounded by `'static`, i.e., with no borrowed elements. - #[derive(Debug)] - pub struct Value(PhantomData); - - impl<'a, T: 'static> Type<'a> for Value { - type Reified = T; - } - - /// Type-based tag similar to [`Value`] but which may be unsized (i.e., has a `'Sized` bound). - #[derive(Debug)] - pub struct MaybeSizedValue(PhantomData); - - impl<'a, T: ?Sized + 'static> MaybeSizedType<'a> for MaybeSizedValue { - type Reified = T; - } - - /// Type-based tag for `&'a T` types. - #[derive(Debug)] - pub struct Ref(PhantomData); - - impl<'a, I: MaybeSizedType<'a>> Type<'a> for Ref { - type Reified = &'a I::Reified; - } -} - -/// An `Option` with a type tag `I`. -/// -/// Since this struct implements `Erased`, the type can be erased to make a dynamically typed -/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically -/// checked for the concrete type, there is some degree of type safety. -#[repr(transparent)] -struct TaggedOption<'a, I: tags::Type<'a>>(Option); - -impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { - fn as_demand(&mut self) -> &mut Demand<'a> { - Demand::new(self as &mut (dyn Erased<'a> + 'a)) - } -} - -/// Represents a type-erased but identifiable object. -/// -/// This trait is exclusively implemented by the `TaggedOption` type. -trait Erased<'a>: 'a { - /// The `TypeId` of the erased type. - fn tag_id(&self) -> TypeId; -} - -impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { - fn tag_id(&self) -> TypeId { - TypeId::of::() - } -} - -impl<'a> dyn Erased<'a> { - /// Returns some reference to the dynamic value if it is tagged with `I`, - /// or `None` otherwise. - #[inline] - fn downcast_mut(&mut self) -> Option<&mut TaggedOption<'a, I>> - where - I: tags::Type<'a>, - { - if self.tag_id() == TypeId::of::() { - // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &mut *(self as *mut Self).cast::>() }) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - extern crate alloc; - - use alloc::{borrow::ToOwned, boxed::Box, string::String}; - - use super::*; - - struct SomeConcreteType { - some_string: String, - } - - impl Provider for SomeConcreteType { - fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - demand - .provide_ref::(&self.some_string) - .provide_ref::(&self.some_string) - .provide_value::(|| "bye".to_owned()); - } - } - - // Test the provide and request mechanisms with a by-reference trait object. - #[test] - fn test_provider() { - let obj: &dyn Provider = &SomeConcreteType { - some_string: "hello".to_owned(), - }; - - assert_eq!(&**request_ref::(obj).unwrap(), "hello"); - assert_eq!(&*request_value::(obj).unwrap(), "bye"); - assert_eq!(request_value::(obj), None); - } - - // Test the provide and request mechanisms with a boxed trait object. - #[test] - fn test_provider_boxed() { - let obj: Box = Box::new(SomeConcreteType { - some_string: "hello".to_owned(), - }); - - assert_eq!(&**request_ref::(&*obj).unwrap(), "hello"); - assert_eq!(&*request_value::(&*obj).unwrap(), "bye"); - assert_eq!(request_value::(&*obj), None); - } - - // Test the provide and request mechanisms with a concrete object. - #[test] - fn test_provider_concrete() { - let obj = SomeConcreteType { - some_string: "hello".to_owned(), - }; - - assert_eq!(&**request_ref::(&obj).unwrap(), "hello"); - assert_eq!(&*request_value::(&obj).unwrap(), "bye"); - assert_eq!(request_value::(&obj), None); - } - - trait OtherTrait: Provider {} - - impl OtherTrait for SomeConcreteType {} - - impl dyn OtherTrait { - fn get_ref(&self) -> Option<&T> { - request_ref::(self) - } - } - - // Test the provide and request mechanisms via an intermediate trait. - #[test] - fn test_provider_intermediate() { - let obj: &dyn OtherTrait = &SomeConcreteType { - some_string: "hello".to_owned(), - }; - assert_eq!(obj.get_ref::().unwrap(), "hello"); - } -} diff --git a/packages/libs/error-stack/src/report.rs b/packages/libs/error-stack/src/report.rs index c37583385c9..25eaf10b338 100644 --- a/packages/libs/error-stack/src/report.rs +++ b/packages/libs/error-stack/src/report.rs @@ -1,17 +1,20 @@ use alloc::{boxed::Box, string::ToString, vec::Vec}; use core::{fmt, fmt::Write, marker::PhantomData, panic::Location}; -#[cfg(all(nightly, feature = "std"))] -use std::backtrace::{Backtrace, BacktraceStatus}; #[cfg(feature = "std")] use std::process::ExitCode; +#[cfg(all(nightly, feature = "std"))] +use std::{ + any, + backtrace::{Backtrace, BacktraceStatus}, +}; #[cfg(feature = "spantrace")] use tracing_error::{SpanTrace, SpanTraceStatus}; +#[cfg(all(nightly, any(feature = "std", feature = "spantrace")))] +use crate::context::temporary_provider; #[cfg(nightly)] use crate::iter::{RequestRef, RequestValue}; -#[cfg(all(nightly, any(feature = "std", feature = "spantrace")))] -use crate::{context::temporary_provider, provider::request_ref}; use crate::{ iter::{Frames, FramesMut}, AttachmentKind, Context, Frame, FrameKind, @@ -37,7 +40,7 @@ use crate::{ /// [`Backtrace` documentation][`Backtrace`]. To enable capturing of the span trace, an /// [`ErrorLayer`] has to be enabled. Please also see the [Feature Flags] section. /// -/// [`provide`]: crate::provider::Provider::provide +/// [`provide`]: core::any::Provider::provide /// [`ErrorLayer`]: tracing_error::ErrorLayer /// [`attach()`]: Self::attach /// [`new()`]: Self::new @@ -185,14 +188,14 @@ impl Report { let provider = temporary_provider(&context); #[cfg(all(nightly, feature = "std"))] - let backtrace = if request_ref::(&provider).is_some() { + let backtrace = if any::request_ref::(&provider).is_some() { None } else { Some(Backtrace::capture()) }; #[cfg(all(nightly, feature = "spantrace"))] - let span_trace = if request_ref::(&provider).is_some() { + let span_trace = if any::request_ref::(&provider).is_some() { None } else { Some(SpanTrace::capture()) @@ -378,14 +381,14 @@ impl Report { } /// Creates an iterator of references of type `T` that have been [`attached`](Self::attach) or - /// that are [`provided`](crate::provider::Provider::provide) by [`Context`] objects. + /// that are [`provide`](core::any::Provider::provide)d by [`Context`] objects. #[cfg(nightly)] pub const fn request_ref(&self) -> RequestRef<'_, T> { RequestRef::new(self) } /// Creates an iterator of values of type `T` that have been [`attached`](Self::attach) or - /// that are [`provided`](crate::provider::Provider::provide) by [`Context`] objects. + /// that are [`provide`](core::any::Provider::provide)d by [`Context`] objects. #[cfg(nightly)] pub const fn request_value(&self) -> RequestValue<'_, T> { RequestValue::new(self)