use bloop_protocol::codec::{Decode, EncodeError};
use bloop_protocol::frame::RawMessage;
use bloop_protocol::message::ErrorResponse;
use bloop_protocol::set::{MessageSetError, Payload, decode_message};
use thiserror::Error;
pub use bloop_protocol::set::Request;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum RequestError {
#[error("not connected to the server")]
Disconnected,
#[error("the client is shut down")]
Shutdown,
#[error("server answered with error {0:?}")]
Error(ErrorResponse),
#[error("server sent an unexpected response with opcode 0x{:02x}", .0.message_type)]
UnexpectedResponse(RawMessage),
#[error("server sent a malformed response")]
Malformed(#[source] MessageSetError),
#[error(transparent)]
Encode(#[from] EncodeError),
}
pub(crate) fn decode_response<M>(raw: RawMessage) -> Result<M, RequestError>
where
M: Payload + Decode,
{
if raw.message_type == ErrorResponse::OPCODE {
return match decode_message::<ErrorResponse>(&raw) {
Ok(error) => Err(RequestError::Error(error)),
Err(error) => Err(RequestError::Malformed(error)),
};
}
match decode_message::<M>(&raw) {
Ok(message) => Ok(message),
Err(MessageSetError::UnknownOpcode(_)) => Err(RequestError::UnexpectedResponse(raw)),
Err(error) => Err(RequestError::Malformed(error)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use bloop_protocol::message::Pong;
use bloop_protocol::set::encode_message;
#[test]
fn success_response_decodes() {
let raw = encode_message(&Pong).unwrap();
assert!(decode_response::<Pong>(raw).is_ok());
}
#[test]
fn error_response_maps_to_request_error() {
let raw = encode_message(&ErrorResponse::UnknownNfcUid).unwrap();
let error = decode_response::<Pong>(raw).unwrap_err();
assert!(matches!(
error,
RequestError::Error(ErrorResponse::UnknownNfcUid)
));
}
#[test]
fn custom_error_code_survives() {
let raw = encode_message(&ErrorResponse::Custom(0x90)).unwrap();
let error = decode_response::<Pong>(raw).unwrap_err();
assert!(matches!(
error,
RequestError::Error(ErrorResponse::Custom(0x90))
));
}
#[test]
fn foreign_opcode_is_unexpected() {
let raw = RawMessage::new(0x42, vec![]);
let error = decode_response::<Pong>(raw).unwrap_err();
assert!(matches!(error, RequestError::UnexpectedResponse(_)));
}
#[test]
fn malformed_payload_is_reported() {
let raw = RawMessage::new(Pong::OPCODE, vec![1, 2, 3]);
let error = decode_response::<Pong>(raw).unwrap_err();
assert!(matches!(error, RequestError::Malformed(_)));
}
}