Skip to content

Commit

Permalink
Improve errors & ProcedureStream::from_*
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Nov 26, 2024
1 parent 63574fe commit 87a6077
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 94 deletions.
12 changes: 8 additions & 4 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ publish = false # TODO: Metadata
# TODO: Disable all features for each of them
[dependencies]
# Public
futures = "0.3.31" # TODO: Maybe `futures_core`?
serde = "1.0.215"
futures-core = { version = "0.3.31", default-features = false }
serde = { version = "1.0.215", default-features = false }

# Private
erased-serde = "0.4.5"
pin-project-lite = "0.2.15"
erased-serde = { version = "0.4.5", default-features = false, features = [
"std",
] }
pin-project-lite = { version = "0.2.15", default-features = false }

# TODO: Remove these
[dev-dependencies]
futures = "0.3.31"
serde_json = "1.0.133"
37 changes: 21 additions & 16 deletions crates/core/src/dyn_input.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use std::{any::Any, fmt};
use std::{
any::{type_name, Any},
fmt,
};

use serde::{de::Error, Deserialize};

use crate::{DeserializeError, DowncastError};

/// TODO
pub struct DynInput<'a, 'de> {
pub(crate) value: Option<&'a mut dyn Any>,
Expand All @@ -22,14 +27,21 @@ impl<'a, 'de> DynInput<'a, 'de> {
}

/// TODO
pub fn value<T: 'static>(self) -> Option<T> {
Some(
self.value?
.downcast_mut::<Option<T>>()?
.take()
// This takes method takes `self` and it's not `Clone` so it's not possible to double take the value.
.expect("unreachable"),
)
pub fn value<T: 'static>(self) -> Result<T, DowncastError> {
Ok(self
.value
.ok_or(DowncastError {
from: None,
to: type_name::<T>(),
})?
.downcast_mut::<Option<T>>()
.ok_or(DowncastError {
from: Some(self.type_name),
to: type_name::<T>(),
})?
.take()
// This takes method takes `self` and it's not `Clone` so it's not possible to double take the value.
.expect("unreachable"))
}
}

Expand All @@ -38,10 +50,3 @@ impl<'a, 'de> fmt::Debug for DynInput<'a, 'de> {
todo!();
}
}

// TODO: Here or `error.rs`???
// TODO: impl Debug, Display, Error
#[derive(Debug)] // TODO: Remove
pub struct DeserializeError(pub(crate) erased_serde::Error);

// TODO: This should be convertable to a `ResolverError`.
176 changes: 124 additions & 52 deletions crates/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ use std::{error, fmt};

use serde::{Serialize, Serializer};

use crate::DeserializeError;

/// TODO
pub enum ProcedureError<S: Serializer> {
/// Attempted to deserialize a value but failed.
Deserialize(DeserializeError),
// /// Attempting to downcast a value failed.
// Downcast {
// // If `None`, the procedure was got a deserializer but expected a value.
// // else the name of the type that was provided by the caller.
// from: Option<&'static str>,
// // The type the procedure expected.
// to: &'static str,
// }, // TODO: Is this going to be possible. Maybe `DowncastError` type?
/// Attempting to downcast a value failed.
Downcast(DowncastError),
/// An error occurred while serializing the value returned by the procedure.
Serializer(S::Error),
/// An error occurred while running the procedure.
Expand All @@ -27,7 +19,7 @@ impl<S: Serializer> From<ResolverError> for ProcedureError<S> {
match err.0 {
Repr::Custom { .. } => ProcedureError::Resolver(err),
Repr::Deserialize(err) => ProcedureError::Deserialize(err),
// Repr::Downcast { from, to } => todo!(),
Repr::Downcast(downcast) => ProcedureError::Downcast(downcast),
}
}
}
Expand All @@ -37,7 +29,7 @@ impl<S: Serializer> fmt::Debug for ProcedureError<S> {
// TODO: Proper format
match self {
Self::Deserialize(err) => write!(f, "Deserialize({:?})", err),
// Self::Downcast { from, to } => write!(f, "Downcast({:?} -> {:?})", from, to),
Self::Downcast(err) => write!(f, "Downcast({:?})", err),
Self::Serializer(err) => write!(f, "Serializer({:?})", err),
Self::Resolver(err) => write!(f, "Resolver({:?})", err),
}
Expand All @@ -52,34 +44,24 @@ impl<S: Serializer> fmt::Display for ProcedureError<S> {

impl<S: Serializer> error::Error for ProcedureError<S> {}

#[derive(Debug)] // TODO: Remove
enum Repr {
// An actual resolver error.
Custom {
status: u16,
// source: erased_serde::Serialize

// type_name: &'static str,
// type_id: TypeId,
// inner: Box<dyn ErasedError>,
// #[cfg(debug_assertions)]
// source_type_name
value: Box<dyn ErrorInternalExt>,
},
// We hide these in here for DX (being able to do `?`) and then convert them to proper `ProcedureError` variants.
Deserialize(DeserializeError),
// Downcast {
// from: Option<&'static str>,
// to: &'static str,
// },
Downcast(DowncastError),
}

impl From<DeserializeError> for ResolverError {
fn from(err: DeserializeError) -> Self {
Self(Repr::Deserialize(err))
impl From<DowncastError> for ResolverError {
fn from(err: DowncastError) -> Self {
Self(Repr::Downcast(err))
}
}

#[derive(Debug)] // TODO: Custom Debug & std::error::Error
/// TODO
pub struct ResolverError(Repr);

impl ResolverError {
Expand All @@ -90,20 +72,61 @@ impl ResolverError {
) -> Self {
Self(Repr::Custom {
status,
// TODO: Avoid allocing `E` & `T` separately.
// type_name: type_name::<T>(),
// type_id: TypeId::of::<T>(),
// inner: Box::new(value),
value: Box::new(ErrorInternal { value, err: source }),
})
}

pub(crate) fn _erased_serde(source: erased_serde::Error) -> Self {
Self(Repr::Deserialize(DeserializeError(source)))
/// TODO
pub fn status(&self) -> &dyn erased_serde::Serialize {
match &self.0 {
Repr::Custom { status, value: _ } => status,
// We flatten these to `ResolverError` so this won't be hit.
Repr::Deserialize(_) => unreachable!(),
Repr::Downcast(_) => unreachable!(),
}
}

/// TODO
pub fn value(&self) -> &dyn erased_serde::Serialize {
match &self.0 {
Repr::Custom {
status: _,
value: error,
} => error.value(),
// We flatten these to `ResolverError` so this won't be hit.
Repr::Deserialize(_) => unreachable!(),
Repr::Downcast(_) => unreachable!(),
}
}

// pub(crate) fn _downcast(from: Option<&'static str>, to: &'static str) -> Self {
// Self(Repr::Downcast { from, to })
// }
/// TODO
pub fn error(&self) -> Option<&dyn error::Error> {
match &self.0 {
Repr::Custom {
status: _,
value: error,
} => error.error(),
// We flatten these to `ResolverError` so this won't be hit.
Repr::Deserialize(_) => unreachable!(),
Repr::Downcast(_) => unreachable!(),
}
}
}

impl fmt::Debug for ResolverError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Repr::Custom {
status,
value: error,
} => {
write!(f, "status: {status:?}, error: {:?}", error.debug())
}
// In practice these won't be hit.
Repr::Deserialize(err) => write!(f, "Deserialize({err:?})"),
Repr::Downcast(err) => write!(f, "Downcast({err:?})"),
}
}
}

impl fmt::Display for ResolverError {
Expand All @@ -114,26 +137,75 @@ impl fmt::Display for ResolverError {

impl error::Error for ResolverError {}

trait ErasedError {
// fn to_box_any(self: Box<Self>) -> Box<dyn Any>;
/// TODO
pub struct DeserializeError(pub(crate) erased_serde::Error);

// fn to_value(&self) -> Option<Result<serde_value::Value, serde_value::SerializerError>>;
impl fmt::Debug for DeserializeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Deserialize({:?})", self.0)
}
}

struct ResolverErrorInternal<T: Serialize, E: error::Error + 'static> {
status: u16,
impl fmt::Display for DeserializeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}

impl error::Error for DeserializeError {}

impl From<DeserializeError> for ResolverError {
fn from(err: DeserializeError) -> Self {
Self(Repr::Deserialize(err))
}
}

/// TODO
pub struct DowncastError {
// If `None`, the procedure was got a deserializer but expected a value.
// else the name of the type that was provided by the caller.
pub(crate) from: Option<&'static str>,
// The type the procedure expected.
pub(crate) to: &'static str,
}

impl fmt::Debug for DowncastError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Downcast(from: {:?}, to: {:?})", self.from, self.to)
}
}

impl fmt::Display for DowncastError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}

impl error::Error for DowncastError {}

struct ErrorInternal<T, E> {
value: T,
source: Option<E>,
err: Option<E>,
}

// impl<T: error::Error + erased_serde::Serialize + 'static> ErasedError for T {
// // fn to_box_any(self: Box<Self>) -> Box<dyn Any> {
// // self
// // }
trait ErrorInternalExt {
fn value(&self) -> &dyn erased_serde::Serialize;

fn error(&self) -> Option<&dyn error::Error>;

// // fn to_value(&self) -> Option<Result<serde_value::Value, serde_value::SerializerError>> {
// // Some(serde_value::to_value(self))
// // }
// }
fn debug(&self) -> Option<&dyn fmt::Debug>;
}

// pub struct Serialize(Box<dyn Error>)
impl<T: Serialize + 'static, E: error::Error + 'static> ErrorInternalExt for ErrorInternal<T, E> {
fn value(&self) -> &dyn erased_serde::Serialize {
&self.value
}

fn error(&self) -> Option<&dyn error::Error> {
self.err.as_ref().map(|err| err as &dyn error::Error)
}

fn debug(&self) -> Option<&dyn fmt::Debug> {
self.err.as_ref().map(|err| err as &dyn fmt::Debug)
}
}
17 changes: 8 additions & 9 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,25 @@
//! TODO: Discuss how middleware don't exist here.
// TODO: Crate icon and stuff

// TODO: Solve layer:
// TODO: Returning non-Serialize types (Eg. `File`)
// TODO: Should downcast have custom error (for more info Eg. type_name)?
// - `ProcedureStream::from_value`
// - `ProcedureStream::from_future`
// - Crate documentation
// - Finish `Debug` impls
// - Returning non-Serialize types (Eg. `File`) via `ProcedureStream`.
//
// - Rename `DynInput` to `DynValue` maybe???
// - `ProcedureStream` to `impl futures::Stream` adapter.
// - `ProcedureStream::poll_next` - Keep or remove???
// - `Send` + `Sync` and the issues with single-threaded async runtimes
// - `DynInput<'a, 'de>` should really be &'a Input<'de>` but that's hard.

// TODO: Optional:
// - Finish `Debug` impls
// - Crate documentation

mod dyn_input;
mod error;
mod procedure;
mod stream;

pub use dyn_input::{DeserializeError, DynInput};
pub use error::{ProcedureError, ResolverError};
pub use dyn_input::DynInput;
pub use error::{DeserializeError, DowncastError, ProcedureError, ResolverError};
pub use procedure::Procedure;
pub use stream::ProcedureStream;

Expand Down
Loading

0 comments on commit 87a6077

Please sign in to comment.