use buffa::{EnumValue, Enumeration};
use thiserror::Error;
use crate::generated::proto::reallyme::codec::v1::{
__buffa::oneof::codec_error::Error as CodecErrorBranchProto, CodecBackendError,
CodecBaseEncodingError, CodecBoundaryError, CodecCanonicalizationError, CodecError,
CodecErrorOrigin as CodecErrorOriginProto, CodecErrorReason, CodecMultiformatError,
CodecPemError,
};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[non_exhaustive]
pub enum CodecWireErrorBranch {
BaseEncoding,
Pem,
Multiformat,
Canonicalization,
Backend,
Boundary,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[non_exhaustive]
pub enum CodecWireErrorOrigin {
Caller,
Provider,
}
#[derive(Debug, Clone, Copy, Eq, Error, PartialEq)]
#[error("codec wire boundary error")]
pub struct CodecWireError {
branch: CodecWireErrorBranch,
reason: CodecErrorReason,
origin: CodecWireErrorOrigin,
}
#[derive(Debug, Clone, Copy, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum CodecWireErrorConstructionError {
#[error("codec error reason does not belong to the selected branch")]
BranchReasonMismatch,
}
pub type CodecWireResult<T> = Result<T, CodecWireError>;
impl CodecWireError {
#[must_use]
pub const fn branch(self) -> CodecWireErrorBranch {
self.branch
}
#[must_use]
pub const fn reason(self) -> CodecErrorReason {
self.reason
}
#[must_use]
pub const fn origin(self) -> CodecWireErrorOrigin {
self.origin
}
pub fn try_new(
branch: CodecWireErrorBranch,
reason: CodecErrorReason,
) -> Result<Self, CodecWireErrorConstructionError> {
if !reason_is_valid_for_branch(branch, reason) {
return Err(CodecWireErrorConstructionError::BranchReasonMismatch);
}
Ok(Self {
branch,
reason,
origin: error_origin(branch, reason),
})
}
#[must_use]
pub(crate) const fn known_good(branch: CodecWireErrorBranch, reason: CodecErrorReason) -> Self {
Self {
branch,
reason,
origin: error_origin(branch, reason),
}
}
#[must_use]
pub const fn malformed_protobuf() -> Self {
Self::known_good(
CodecWireErrorBranch::Boundary,
CodecErrorReason::CODEC_ERROR_REASON_BOUNDARY_MALFORMED_PROTOBUF,
)
}
#[must_use]
pub const fn internal() -> Self {
Self::known_good(
CodecWireErrorBranch::Backend,
CodecErrorReason::CODEC_ERROR_REASON_BACKEND_INTERNAL,
)
}
}
#[must_use]
pub fn codec_error(error: CodecWireError) -> CodecError {
let reason = EnumValue::from(error.reason());
let branch = match error.branch() {
CodecWireErrorBranch::BaseEncoding => {
CodecErrorBranchProto::BaseEncoding(Box::new(CodecBaseEncodingError {
reason,
__buffa_unknown_fields: Default::default(),
}))
}
CodecWireErrorBranch::Pem => CodecErrorBranchProto::Pem(Box::new(CodecPemError {
reason,
__buffa_unknown_fields: Default::default(),
})),
CodecWireErrorBranch::Multiformat => {
CodecErrorBranchProto::Multiformat(Box::new(CodecMultiformatError {
reason,
__buffa_unknown_fields: Default::default(),
}))
}
CodecWireErrorBranch::Canonicalization => {
CodecErrorBranchProto::Canonicalization(Box::new(CodecCanonicalizationError {
reason,
__buffa_unknown_fields: Default::default(),
}))
}
CodecWireErrorBranch::Backend => {
CodecErrorBranchProto::Backend(Box::new(CodecBackendError {
reason,
__buffa_unknown_fields: Default::default(),
}))
}
CodecWireErrorBranch::Boundary => {
CodecErrorBranchProto::Boundary(Box::new(CodecBoundaryError {
reason,
__buffa_unknown_fields: Default::default(),
}))
}
};
CodecError {
error: Some(branch),
origin: EnumValue::from(match error.origin() {
CodecWireErrorOrigin::Caller => CodecErrorOriginProto::CODEC_ERROR_ORIGIN_CALLER,
CodecWireErrorOrigin::Provider => CodecErrorOriginProto::CODEC_ERROR_ORIGIN_PROVIDER,
}),
__buffa_unknown_fields: Default::default(),
}
}
fn reason_is_valid_for_branch(branch: CodecWireErrorBranch, reason: CodecErrorReason) -> bool {
let value = reason.to_i32();
match branch {
CodecWireErrorBranch::BaseEncoding => (100..=199).contains(&value),
CodecWireErrorBranch::Pem => (200..=299).contains(&value),
CodecWireErrorBranch::Multiformat => (300..=399).contains(&value),
CodecWireErrorBranch::Canonicalization => (400..=499).contains(&value),
CodecWireErrorBranch::Backend => (500..=599).contains(&value),
CodecWireErrorBranch::Boundary => (600..=699).contains(&value),
}
}
const fn error_origin(
branch: CodecWireErrorBranch,
reason: CodecErrorReason,
) -> CodecWireErrorOrigin {
match (branch, reason) {
(CodecWireErrorBranch::Backend, _)
| (
CodecWireErrorBranch::Canonicalization,
CodecErrorReason::CODEC_ERROR_REASON_CANONICAL_INTERNAL,
) => CodecWireErrorOrigin::Provider,
_ => CodecWireErrorOrigin::Caller,
}
}
pub(crate) fn malformed_protobuf_error() -> CodecWireError {
CodecWireError::malformed_protobuf()
}
pub(crate) fn malformed_json_error() -> CodecWireError {
CodecWireError::known_good(
CodecWireErrorBranch::Boundary,
CodecErrorReason::CODEC_ERROR_REASON_BOUNDARY_MALFORMED_JSON,
)
}
pub(crate) fn resource_limit_error() -> CodecWireError {
CodecWireError::known_good(
CodecWireErrorBranch::Boundary,
CodecErrorReason::CODEC_ERROR_REASON_BOUNDARY_RESOURCE_LIMIT_EXCEEDED,
)
}