use std::{error, fmt};
use ctaphid_types::Command;
#[derive(Debug)]
pub enum Error {
CommandError(CommandError),
RequestError(RequestError),
ResponseError(ResponseError),
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::CommandError(error) => Some(error),
Self::RequestError(error) => Some(error),
Self::ResponseError(error) => Some(error),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CommandError(_) => "command execution failed",
Self::RequestError(_) => "failed to send CTAPHID request",
Self::ResponseError(_) => "failed to receive CTAPHID response",
}
.fmt(f)
}
}
impl From<CommandError> for Error {
fn from(error: CommandError) -> Self {
Self::CommandError(error)
}
}
impl From<RequestError> for Error {
fn from(error: RequestError) -> Self {
Self::RequestError(error)
}
}
impl From<ResponseError> for Error {
fn from(error: ResponseError) -> Self {
Self::ResponseError(error)
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CommandError {
CborError(u8),
InvalidPingData,
NotSupported(Command),
}
impl error::Error for CommandError {}
impl fmt::Display for CommandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CborError(error) => write!(f, "received a CBOR response with status {:x}", error),
Self::InvalidPingData => "received a ping response with wrong data".fmt(f),
Self::NotSupported(command) => write!(
f,
"the command {:?} is not supported by the device",
command
),
}
}
}
#[derive(Debug)]
pub enum RequestError {
IncompleteWrite,
MessageFragmentationFailed(ctaphid_types::FragmentationError),
PacketSendingFailed(Box<dyn std::error::Error>),
PacketSerializationFailed(ctaphid_types::SerializationError),
}
impl error::Error for RequestError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::MessageFragmentationFailed(err) => Some(err),
Self::PacketSendingFailed(err) => Some(err.as_ref()),
Self::PacketSerializationFailed(err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for RequestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IncompleteWrite => "the request could not be written completely",
Self::MessageFragmentationFailed(_) => {
"the request message could not be fragmented into CTAPHID packets"
}
Self::PacketSendingFailed(_) => "a request packet could not be sent to the device",
Self::PacketSerializationFailed(_) => "a request packet could not be serialized",
}
.fmt(f)
}
}
#[derive(Debug)]
pub enum ResponseError {
CommandFailed(ctaphid_types::DeviceError),
MessageDefragmentationFailed(ctaphid_types::DefragmentationError),
PacketParsingFailed(ctaphid_types::ParseError),
PacketReceivingFailed(Box<dyn std::error::Error>),
Timeout,
MissingErrorCode,
UnexpectedCommand {
expected: Command,
actual: Command,
},
UnexpectedKeepAlive(Command),
UnexpectedResponseData(Vec<u8>),
}
impl error::Error for ResponseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::CommandFailed(err) => Some(err),
Self::MessageDefragmentationFailed(err) => Some(err),
Self::PacketParsingFailed(err) => Some(err),
Self::PacketReceivingFailed(err) => Some(err.as_ref()),
_ => None,
}
}
}
impl fmt::Display for ResponseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CommandFailed(_) => "the command execution failed".fmt(f),
Self::MessageDefragmentationFailed(_) => {
"the response message could not be assembled from CTAPHID packets".fmt(f)
}
Self::PacketParsingFailed(_) => "a response packet could not be serialized".fmt(f),
Self::PacketReceivingFailed(_) => {
"a response packet could not be received from the device".fmt(f)
}
Self::Timeout => "no response was received within the specified timeout".fmt(f),
Self::MissingErrorCode => "an error packet does not contain an error code".fmt(f),
Self::UnexpectedCommand { expected, actual } => write!(
f,
"expected a response packet for command {:?} but received {:?}",
expected, actual
),
Self::UnexpectedKeepAlive(command) => {
write!(
f,
"expected a response message to a {:?} command but received a KEEPALIVE message",
command
)
}
Self::UnexpectedResponseData(data) => {
write!(f, "expected an empty response but received {:x?}", data)
}
}
}
}