Skip to content

Commit

Permalink
Merge pull request #102 from monadicus/feat/flatten-error-type
Browse files Browse the repository at this point in the history
Refactor(Snops): Flattten error object
  • Loading branch information
gluax authored Apr 5, 2024
2 parents 19fac00 + dd1f229 commit 0f2998a
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 149 deletions.
48 changes: 22 additions & 26 deletions crates/snops-common/src/rpc/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,29 @@ use strum_macros::AsRefStr;
use thiserror::Error;

#[macro_export]
macro_rules! impl_serialize_pretty_error {
macro_rules! impl_into_type_str {
($name:path) => {
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
PrettyError::from(self).serialize(serializer)
impl From<&$name> for String {
fn from(e: &$name) -> Self {
e.as_ref().to_string()
}
}
};

($name:path, |_| $body:expr) => {
impl From<&$name> for String {
fn from(_: &$name) -> Self {
$body
}
}
};

($name:path, |$from_var:ident| $body:expr) => {
impl From<&$name> for String {
fn from($from_var: &$name) -> Self {
use $name::*;

$body
}
}
};
Expand Down Expand Up @@ -45,25 +60,6 @@ macro_rules! impl_into_status_code {
};
}

#[derive(Debug, Serialize)]
pub struct PrettyError {
#[serde(rename = "type")]
pub ty: String,
pub error: String,
}

impl<E> From<&E> for PrettyError
where
E: std::error::Error + AsRef<str>,
{
fn from(error: &E) -> Self {
Self {
ty: error.as_ref().to_string(),
error: error.to_string(),
}
}
}

#[derive(Debug, Error, Serialize, Deserialize, AsRefStr)]
pub enum AgentError {
#[error("invalid agent state")]
Expand Down
66 changes: 32 additions & 34 deletions crates/snops/src/cannon/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ use std::path::PathBuf;

use axum::http::StatusCode;
use serde::{ser::SerializeStruct, Serialize, Serializer};
use snops_common::{
impl_into_status_code, impl_serialize_pretty_error, rpc::error::PrettyError, state::NodeKey,
};
use snops_common::{impl_into_status_code, impl_into_type_str, state::NodeKey};
use strum_macros::AsRefStr;
use thiserror::Error;

Expand All @@ -14,13 +12,13 @@ use crate::error::{CommandError, StateError};
#[derive(Debug, Error, AsRefStr)]
pub enum AuthorizeError {
/// For when a bad AOT command is run
#[error("command error: {0}")]
#[error(transparent)]
Command(#[from] CommandError),
/// For if invalid JSON is returned from the AOT command
#[error("expected function, fee, and broadcast fields in response")]
InvalidJson,
/// For if invalid JSON is returned from the AOT command
#[error("parse json error: {0}")]
#[error("{0}")]
Json(#[source] serde_json::Error),
/// For if invalid JSON is returned from the AOT command
#[error("expected JSON object in response")]
Expand All @@ -32,19 +30,19 @@ impl_into_status_code!(AuthorizeError, |value| match value {
_ => StatusCode::INTERNAL_SERVER_ERROR,
});

impl_into_type_str!(AuthorizeError, |value| match value {
Command(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
_ => value.as_ref().to_string(),
});

impl Serialize for AuthorizeError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Error", 2)?;
state.serialize_field("type", self.as_ref())?;

match self {
Self::Command(e) => state.serialize_field("error", e),
_ => state.serialize_field("error", &self.to_string()),
}?;

state.serialize_field("type", &String::from(self))?;
state.serialize_field("error", &self.to_string())?;
state.end()
}
}
Expand All @@ -63,7 +61,6 @@ pub enum TransactionDrainError {
}

impl_into_status_code!(TransactionDrainError);
impl_serialize_pretty_error!(TransactionDrainError);

#[derive(Debug, Error, AsRefStr)]
pub enum TransactionSinkError {
Expand All @@ -79,7 +76,6 @@ pub enum TransactionSinkError {
}

impl_into_status_code!(TransactionSinkError);
impl_serialize_pretty_error!(TransactionSinkError);

#[derive(Debug, Error, AsRefStr)]
pub enum SourceError {
Expand All @@ -102,7 +98,6 @@ pub enum SourceError {
}

impl_into_status_code!(SourceError);
impl_serialize_pretty_error!(SourceError);

#[derive(Debug, Error, AsRefStr)]
pub enum CannonInstanceError {
Expand Down Expand Up @@ -175,19 +170,19 @@ impl Serialize for ExecutionContextError {

#[derive(Debug, Error, AsRefStr)]
pub enum CannonError {
#[error("authorize error: {0}")]
#[error(transparent)]
Authorize(#[from] AuthorizeError),
#[error("cannon instance error: {0}")]
#[error(transparent)]
CannonInstance(#[from] CannonInstanceError),
#[error("command error cannon `{0}`: {1}")]
#[error("cannon `{0}`: {1}")]
Command(usize, #[source] CommandError),
#[error("exec ctx error: {0}")]
#[error(transparent)]
ExecutionContext(#[from] ExecutionContextError),
#[error("target agent offline for {0} `{1}`: {2}")]
TargetAgentOffline(&'static str, usize, String),
#[error("tx drain error: {0}")]
#[error(transparent)]
TransactionDrain(#[from] TransactionDrainError),
#[error("tx sink error: {0}")]
#[error(transparent)]
TransactionSink(#[from] TransactionSinkError),
#[error("send `auth` error for cannon `{0}`: {1}")]
SendAuthError(
Expand All @@ -196,9 +191,9 @@ pub enum CannonError {
),
#[error("send `tx` error for cannon `{0}`: {1}")]
SendTxError(usize, #[source] tokio::sync::mpsc::error::SendError<String>),
#[error("source error: {0}")]
#[error(transparent)]
Source(#[from] SourceError),
#[error("state error: {0}")]
#[error(transparent)]
State(#[from] StateError),
}

Expand All @@ -215,23 +210,26 @@ impl_into_status_code!(CannonError, |value| match value {
_ => StatusCode::INTERNAL_SERVER_ERROR,
});

impl_into_type_str!(CannonError, |value| match value {
Authorize(e) => format!("{}.{}", value.as_ref(), String::from(e)),
CannonInstance(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
Command(_, e) => format!("{}.{}", value.as_ref(), e.as_ref()),
ExecutionContext(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
TransactionDrain(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
TransactionSink(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
Source(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
State(e) => format!("{}.{}", value.as_ref(), String::from(e)),
_ => value.as_ref().to_string(),
});

impl Serialize for CannonError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Error", 2)?;
state.serialize_field("type", self.as_ref())?;

match self {
Self::Authorize(e) => state.serialize_field("error", e),
Self::CannonInstance(e) => state.serialize_field("error", e),
Self::Command(_, e) => state.serialize_field("error", e),
Self::ExecutionContext(e) => state.serialize_field("error", e),
Self::TransactionDrain(e) => state.serialize_field("error", e),
Self::TransactionSink(e) => state.serialize_field("error", e),
_ => state.serialize_field("error", &self.to_string()),
}?;
state.serialize_field("type", &String::from(self))?;
state.serialize_field("error", &self.to_string())?;

state.end()
}
Expand Down
73 changes: 41 additions & 32 deletions crates/snops/src/env/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use axum::http::StatusCode;
use serde::{ser::SerializeStruct, Serialize, Serializer};
use snops_common::{
impl_into_status_code, impl_serialize_pretty_error,
rpc::error::PrettyError,
impl_into_status_code, impl_into_type_str,
state::{AgentId, NodeKey},
};
use strum_macros::AsRefStr;
Expand All @@ -25,11 +24,11 @@ pub enum ExecutionError {
AgentOffline,
#[error("env `{0}` not found")]
EnvNotFound(usize),
#[error("cannon error: `{0}`")]
#[error(transparent)]
Cannon(#[from] CannonError),
#[error("join error: `{0}`")]
#[error("{0}")]
Join(#[from] JoinError),
#[error("reconcile error: `{0}`")]
#[error(transparent)]
Reconcile(#[from] BatchReconcileError),
#[error("env timeline is already being executed")]
TimelineAlreadyStarted,
Expand All @@ -43,16 +42,21 @@ impl_into_status_code!(ExecutionError, |value| match value {
_ => StatusCode::INTERNAL_SERVER_ERROR,
});

impl_into_type_str!(ExecutionError, |value| match value {
Cannon(e) => format!("{}.{}", value.as_ref(), &String::from(e)),
_ => value.as_ref().to_string(),
});

impl Serialize for ExecutionError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Error", 2)?;
state.serialize_field("type", self.as_ref())?;
state.serialize_field("type", &String::from(self))?;

match self {
Self::Cannon(e) => state.serialize_field("error", e),
Self::Cannon(e) => state.serialize_field("error", &e.to_string()),
_ => state.serialize_field("error", &self.to_string()),
}?;

Expand Down Expand Up @@ -83,8 +87,6 @@ impl_into_status_code!(DelegationError, |value| match value {
}
});

impl_serialize_pretty_error!(DelegationError);

#[derive(Debug, Error, AsRefStr)]
pub enum PrepareError {
#[error("duplicate node key: {0}")]
Expand All @@ -95,7 +97,7 @@ pub enum PrepareError {
MissingStorage,
#[error("cannot have a node with zero replicas")]
NodeHas0Replicas,
#[error("reconcile error: `{0}`")]
#[error(transparent)]
Reconcile(#[from] ReconcileError),
}

Expand All @@ -105,16 +107,21 @@ impl_into_status_code!(PrepareError, |value| match value {
Reconcile(e) => e.into(),
});

impl_into_type_str!(PrepareError, |value| match value {
Reconcile(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
_ => value.as_ref().to_string(),
});

impl Serialize for PrepareError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Error", 2)?;
state.serialize_field("type", self.as_ref())?;
state.serialize_field("type", &String::from(self))?;

match self {
Self::Reconcile(e) => state.serialize_field("error", e),
Self::Reconcile(e) => state.serialize_field("error", &e.to_string()),
_ => state.serialize_field("error", &self.to_string()),
}?;

Expand All @@ -129,7 +136,6 @@ pub enum CleanupError {
}

impl_into_status_code!(CleanupError, |_| StatusCode::NOT_FOUND);
impl_serialize_pretty_error!(CleanupError);

#[derive(Debug, Error, AsRefStr)]
pub enum ReconcileError {
Expand All @@ -146,23 +152,21 @@ impl_into_status_code!(ReconcileError, |value| match value {
EnvNotFound(_) | ExpectedInternalAgentPeer { .. } => StatusCode::NOT_FOUND,
});

impl_serialize_pretty_error!(ReconcileError);

#[derive(Debug, Error, AsRefStr)]
pub enum EnvError {
#[error("cannon error: `{0}`")]
#[error(transparent)]
Cannon(#[from] CannonError),
#[error("cleanup error: `{0}`")]
#[error(transparent)]
Cleanup(#[from] CleanupError),
#[error("delegation errors occured:{}", .0.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n"))]
Delegation(Vec<DelegationError>),
#[error("exec error: `{0}`")]
#[error(transparent)]
Execution(#[from] ExecutionError),
#[error("prepare error: `{0}`")]
#[error(transparent)]
Prepare(#[from] PrepareError),
#[error("reconcile error: `{0}`")]
#[error(transparent)]
Reconcile(#[from] ReconcileError),
#[error("schema error: `{0}`")]
#[error(transparent)]
Schema(#[from] SchemaError),
}

Expand All @@ -176,23 +180,28 @@ impl_into_status_code!(EnvError, |value| match value {
Schema(e) => e.into(),
});

impl_into_type_str!(EnvError, |value| match value {
Cannon(e) => format!("{}.{}", value.as_ref(), String::from(e)),
Cleanup(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
Delegation(e) => format!(
"{}.{}",
value.as_ref(),
e.iter().map(|x| x.as_ref()).collect::<Vec<_>>().join(",")
),
Execution(e) => format!("{}.{}", value.as_ref(), String::from(e)),
Prepare(e) => format!("{}.{}", value.as_ref(), String::from(e)),
Reconcile(e) => format!("{}.{}", value.as_ref(), e.as_ref()),
Schema(e) => format!("{}.{}", value.as_ref(), String::from(e)),
});

impl Serialize for EnvError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Error", 2)?;
state.serialize_field("type", self.as_ref())?;

match self {
Self::Cannon(e) => state.serialize_field("error", e),
Self::Cleanup(e) => state.serialize_field("error", e),
Self::Delegation(e) => state.serialize_field("error", e),
Self::Execution(e) => state.serialize_field("error", e),
Self::Prepare(e) => state.serialize_field("error", e),
Self::Reconcile(e) => state.serialize_field("error", e),
Self::Schema(e) => state.serialize_field("error", e),
}?;
state.serialize_field("type", &String::from(self))?;
state.serialize_field("error", &self.to_string())?;

state.end()
}
Expand Down
Loading

0 comments on commit 0f2998a

Please sign in to comment.