Skip to content

Commit

Permalink
Refactor error management
Browse files Browse the repository at this point in the history
Change-type: patch
  • Loading branch information
pipex committed Nov 6, 2024
1 parent a8b81ff commit 2ddab40
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 82 deletions.
44 changes: 13 additions & 31 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,26 @@ use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
SerializationError(#[from] serde_json::error::Error),
#[error("cannot read system state: ${0}")]
StateReadFailed(#[from] super::system::SystemReadError),

#[error(transparent)]
FailedToDeserializePathParams(#[from] super::extract::PathDeserializationError),
#[error("cannot write system state: ${0}")]
StateWriteFailed(#[from] super::system::SystemWriteError),

#[error(transparent)]
PatchFailed(#[from] json_patch::PatchError),
#[error("cannot extract path: ${0}")]
PathExtractFailed(#[from] super::extract::PathDeserializationError),

#[error("the string `{0}` is not a valid path")]
InvalidPath(String),
#[error("cannot extract target: ${0}")]
TargetExtractFailed(#[from] super::extract::TargetExtractError),

#[error("no target available on the context")]
TargetIsNone,
#[error("cannot extract view: ${0}")]
ViewExtractFailed(#[from] super::extract::ViewExtractError),

#[error("cannot expand an atom task")]
CannotExpandTask,
#[error("cannot calculate view result: ${0}")]
ViewResultFailed(#[from] super::extract::ViewResultError),

#[error("condition failed: ${0}")]
ConditionFailed(String),

#[error("cannot resolve state path `{path}`: ${reason}")]
TargetResolveFailed {
path: String,
reason: jsonptr::resolve::ResolveError,
},

#[error("cannot resolve path `{path}` on system state: ${reason}")]
PointerResolveFailed {
path: String,
reason: jsonptr::resolve::ResolveError,
},

#[error("cannot assign path `{path}` on system state: ${reason}")]
PointerAssignFailed {
path: String,
reason: jsonptr::assign::AssignError,
},
TaskConditionFailed(#[from] super::task::ConditionFailed),

#[error(transparent)]
Other(#[from] Box<dyn std::error::Error>),
Expand Down
2 changes: 1 addition & 1 deletion src/extract/path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub use error::PathDeserializationError;

impl IntoError for PathDeserializationError {
fn into_error(self) -> Error {
Error::FailedToDeserializePathParams(self)
Error::PathExtractFailed(self)
}
}

Expand Down
55 changes: 47 additions & 8 deletions src/extract/target.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,76 @@
use crate::error::Error;
use crate::system::{FromSystem, System};
use crate::task::Context;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fmt::{self, Display};
use std::marker::PhantomData;
use std::ops::Deref;

use crate::error::{self, IntoError};
use crate::system::{FromSystem, System};
use crate::task::Context;

#[derive(Debug)]
pub enum TargetExtractError {
Undefined,
SerializationFailed(serde_json::error::Error),
DeserializationFailed(serde_json::error::Error),
CannotResolvePath {
path: String,
reason: jsonptr::resolve::ResolveError,
},
}

impl std::error::Error for TargetExtractError {}

impl Display for TargetExtractError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TargetExtractError::SerializationFailed(err) => err.fmt(f)?,
TargetExtractError::DeserializationFailed(err) => err.fmt(f)?,
TargetExtractError::Undefined => {
write!(f, "no target has been defined for the context")?;
}
TargetExtractError::CannotResolvePath { path, reason } => {
write!(f, "cannot resolve state path `{}`: {}", path, reason)?;
}
};
Ok(())
}
}

impl IntoError for TargetExtractError {
fn into_error(self) -> error::Error {
error::Error::TargetExtractFailed(self)
}
}

pub struct Target<S: Clone, T = S> {
target: T,
_system: PhantomData<S>,
}

impl<S: Serialize + Clone, T: DeserializeOwned> FromSystem<S> for Target<S, T> {
type Error = Error;
type Error = TargetExtractError;

fn from_system(_: &System, context: &Context<S>) -> Result<Self, Self::Error> {
if context.target.is_none() {
return Err(Error::TargetIsNone);
return Err(TargetExtractError::Undefined);
}

let tgt = serde_json::to_value(context.target.clone())?;
let tgt = serde_json::to_value(context.target.clone())
.map_err(TargetExtractError::SerializationFailed)?;

// Return an error if the context path does not exist in the target object
let pointer = context.path.as_ref();
let value = pointer
.resolve(&tgt)
.map_err(|e| Error::TargetResolveFailed {
.map_err(|e| TargetExtractError::CannotResolvePath {
path: context.path.to_string(),
reason: e,
})?;

// This will fail if the value cannot be deserialized into the target type
let target = serde_json::from_value::<T>(value.clone())?;
let target = serde_json::from_value::<T>(value.clone())
.map_err(TargetExtractError::DeserializationFailed)?;

Ok(Target {
target,
Expand Down
98 changes: 85 additions & 13 deletions src/extract/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use json_patch::{diff, Patch};
use jsonptr::resolve::ResolveError;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fmt::{self, Display};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

use crate::error::Error;
use crate::error::{Error, IntoError};
use crate::path::Path;
use crate::system::{FromSystem, System};
use crate::task::{Context, Effect, IntoEffect, IntoResult, Result};
Expand All @@ -22,6 +23,72 @@ pub struct View<S, T = S> {
_system: PhantomData<S>,
}

#[derive(Debug)]
pub enum ViewExtractError {
PathResolveFailed {
path: String,
reason: jsonptr::resolve::ResolveError,
},
DeserializationFailed(serde_json::error::Error),
}

impl std::error::Error for ViewExtractError {}

impl Display for ViewExtractError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ViewExtractError::PathResolveFailed { path, reason } => {
write!(
f,
"cannot resolve path `{}` on system state: {}",
path, reason
)?;
}
ViewExtractError::DeserializationFailed(err) => err.fmt(f)?,
}

Ok(())
}
}

impl IntoError for ViewExtractError {
fn into_error(self) -> Error {
Error::ViewExtractFailed(self)
}
}

#[derive(Debug)]
pub enum ViewResultError {
PathAssignFailed {
path: String,
reason: jsonptr::assign::AssignError,
},
DeserializationFailed(serde_json::error::Error),
}

impl std::error::Error for ViewResultError {}

impl Display for ViewResultError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ViewResultError::PathAssignFailed { path, reason } => {
write!(
f,
"cannot assign path `{}` on system state: {}",
path, reason
)
}
ViewResultError::DeserializationFailed(err) => err.fmt(f),
}
}
}

impl IntoError for ViewResultError {
fn into_error(self) -> Error {
Error::ViewResultFailed(self)
}
}

impl<S, T> View<S, T> {
pub(super) fn new(state: Option<T>, path: Path) -> Self {
View {
Expand All @@ -48,7 +115,7 @@ impl<S, T: Default> View<S, T> {
}

impl<S, T: DeserializeOwned> FromSystem<S> for View<S, T> {
type Error = Error;
type Error = ViewExtractError;

fn from_system(
system: &System,
Expand All @@ -63,7 +130,7 @@ impl<S, T: DeserializeOwned> FromSystem<S> for View<S, T> {
// Try to resolve the parent or fail
parent
.resolve(root)
.map_err(|e| Error::PointerResolveFailed {
.map_err(|e| ViewExtractError::PathResolveFailed {
path: context.path.to_string(),
reason: e,
})?;
Expand All @@ -72,12 +139,15 @@ impl<S, T: DeserializeOwned> FromSystem<S> for View<S, T> {
// resolved is because the value does not exist yet unless
// the parent is a scalar
let state: Option<T> = match pointer.resolve(root) {
Ok(value) => Some(serde_json::from_value::<T>(value.clone())?),
Ok(value) => Some(
serde_json::from_value::<T>(value.clone())
.map_err(ViewExtractError::DeserializationFailed)?,
),
Err(e) => match e {
ResolveError::NotFound { .. } => None,
ResolveError::OutOfBounds { .. } => None,
_ => {
return Err(Error::PointerResolveFailed {
return Err(ViewExtractError::PathResolveFailed {
path: context.path.to_string(),
reason: e,
})
Expand Down Expand Up @@ -112,12 +182,13 @@ impl<S, T: Serialize> IntoResult<Patch> for View<S, T> {
let pointer = self.path.as_ref();

if let Some(state) = self.state {
let value = serde_json::to_value(state)?;
let value =
serde_json::to_value(state).map_err(ViewResultError::DeserializationFailed)?;

// Assign the state to the copy
pointer
.assign(root, value)
.map_err(|e| Error::PointerAssignFailed {
.map_err(|e| ViewResultError::PathAssignFailed {
path: self.path.to_string(),
reason: e,
})?;
Expand Down Expand Up @@ -148,7 +219,7 @@ pub struct Create<S, T = S> {
}

impl<S, T: DeserializeOwned + Default> FromSystem<S> for Create<S, T> {
type Error = Error;
type Error = ViewExtractError;

fn from_system(
system: &System,
Expand All @@ -165,7 +236,7 @@ impl<S, T: DeserializeOwned + Default> FromSystem<S> for Create<S, T> {
// Try to resolve the parent or fail
parent
.resolve(root)
.map_err(|e| Error::PointerResolveFailed {
.map_err(|e| ViewExtractError::PathResolveFailed {
path: context.path.to_string(),
reason: e,
})?;
Expand All @@ -178,7 +249,7 @@ impl<S, T: DeserializeOwned + Default> FromSystem<S> for Create<S, T> {
ResolveError::NotFound { .. } => (),
ResolveError::OutOfBounds { .. } => (),
_ => {
return Err(Error::PointerResolveFailed {
return Err(ViewExtractError::PathResolveFailed {
path: context.path.to_string(),
reason: e,
})
Expand Down Expand Up @@ -233,7 +304,7 @@ pub struct Update<S, T = S> {
}

impl<S, T: DeserializeOwned> FromSystem<S> for Update<S, T> {
type Error = Error;
type Error = ViewExtractError;

fn from_system(
system: &System,
Expand All @@ -246,11 +317,12 @@ impl<S, T: DeserializeOwned> FromSystem<S> for Update<S, T> {
// reason, return an error
let value = pointer
.resolve(root)
.map_err(|e| Error::PointerResolveFailed {
.map_err(|e| ViewExtractError::PathResolveFailed {
path: context.path.to_string(),
reason: e,
})?;
let state = serde_json::from_value::<T>(value.clone())?;
let state = serde_json::from_value::<T>(value.clone())
.map_err(ViewExtractError::DeserializationFailed)?;

Ok(Update {
state,
Expand Down
Loading

0 comments on commit 2ddab40

Please sign in to comment.