use std::error::Error;
use std::fmt::{self, Display, Formatter};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NameNamespace {
TypeDeclaration,
Operation,
Field,
Parameter,
EnumCase,
UnionCase,
ChildObject,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueKind {
Unit,
Boolean,
Integer,
Number,
String,
Bytes,
Record,
List,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathSegment {
Descriptor,
Declaration(String),
Operation(String),
Field(String),
Parameter(String),
Case(String),
Index(usize),
Return,
Object(String),
Child(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationErrorKind {
DuplicateName {
namespace: NameNamespace,
name: String,
},
UnknownType {
name: String,
},
RecursiveType {
cycle: Vec<String>,
},
TypeMismatch {
expected: ValueKind,
actual: ValueKind,
},
UnknownField {
name: String,
},
MissingField {
name: String,
},
UnknownCase {
name: String,
},
MissingUnionPayload,
UnexpectedUnionPayload,
UnknownParameter {
name: String,
},
MissingParameter {
name: String,
},
UnknownOperation {
name: String,
},
IntegerOutOfRange {
value: i64,
},
NonFiniteNumber,
BytesInJson,
InvalidObjectName {
name: String,
},
DuplicateInterface {
reference: String,
},
InvalidPath {
path: String,
},
ObjectNameMismatch {
expected: String,
actual: String,
},
InconsistentObjectObservation {
reference: String,
},
ChildrenMissingBeforeDepth {
depth: u32,
},
ChildrenAtDepthBoundary {
depth: u32,
},
InterfaceResponseMismatch {
expected: String,
actual: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidationError {
pub path: Vec<PathSegment>,
pub kind: ValidationErrorKind,
}
impl ValidationError {
pub(crate) fn new(kind: ValidationErrorKind) -> Self {
Self {
path: Vec::new(),
kind,
}
}
pub(crate) fn prepend(mut self, segment: PathSegment) -> Self {
self.path.insert(0, segment);
self
}
}
impl Display for ValidationError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
if self.path.is_empty() {
write!(formatter, "protocol validation failed: {:?}", self.kind)
} else {
write!(
formatter,
"protocol validation failed at {:?}: {:?}",
self.path, self.kind
)
}
}
}
impl Error for ValidationError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtocolInteraction {
Observe,
FetchInterface,
CallOperation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtocolErrorCode {
InvalidRequest,
NotFound,
InterfaceNotFound,
InterfaceMismatch,
OperationNotFound,
InvalidArguments,
ValidatorRequired,
ValidatorMismatch,
InterfaceValidatorRequired,
InterfaceValidatorMismatch,
PermissionDenied,
ResourceLimitExceeded,
Internal,
OperationOutcomeUnknown,
}
impl ProtocolErrorCode {
pub const ALL: [Self; 14] = [
Self::InvalidRequest,
Self::NotFound,
Self::InterfaceNotFound,
Self::InterfaceMismatch,
Self::OperationNotFound,
Self::InvalidArguments,
Self::ValidatorRequired,
Self::ValidatorMismatch,
Self::InterfaceValidatorRequired,
Self::InterfaceValidatorMismatch,
Self::PermissionDenied,
Self::ResourceLimitExceeded,
Self::Internal,
Self::OperationOutcomeUnknown,
];
#[must_use]
pub const fn is_allowed_for(self, interaction: ProtocolInteraction) -> bool {
use ProtocolErrorCode::{
InterfaceMismatch, InterfaceNotFound, InterfaceValidatorMismatch,
InterfaceValidatorRequired, Internal, InvalidArguments, InvalidRequest, NotFound,
OperationNotFound, OperationOutcomeUnknown, PermissionDenied, ResourceLimitExceeded,
ValidatorMismatch, ValidatorRequired,
};
use ProtocolInteraction::{CallOperation, FetchInterface, Observe};
match (interaction, self) {
(_, InvalidRequest | ResourceLimitExceeded | Internal) => true,
(Observe, NotFound) => true,
(FetchInterface, InterfaceNotFound) => true,
(
CallOperation,
NotFound
| InterfaceMismatch
| OperationNotFound
| InvalidArguments
| ValidatorRequired
| ValidatorMismatch
| InterfaceValidatorRequired
| InterfaceValidatorMismatch
| PermissionDenied
| OperationOutcomeUnknown,
) => true,
(
_,
NotFound
| InterfaceNotFound
| InterfaceMismatch
| OperationNotFound
| InvalidArguments
| ValidatorRequired
| ValidatorMismatch
| InterfaceValidatorRequired
| InterfaceValidatorMismatch
| PermissionDenied
| OperationOutcomeUnknown,
) => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProtocolError {
pub code: ProtocolErrorCode,
pub message: String,
}
impl Display for ProtocolError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
write!(
formatter,
"protocol failure ({:?}): {}",
self.code, self.message
)
}
}
impl Error for ProtocolError {}