#![allow(missing_docs)]
#![allow(unexpected_cfgs)]
use core::fmt::{self, Display, Formatter};
use hashbrown::HashMap;
use minicbor::bytes::ByteVec;
use minicbor::data::Type;
use minicbor::encode::{self, Encoder, Write};
use minicbor::{CborLen, Decode, Decoder, Encode};
use serde::{Serialize, Serializer};
use tinyvec::ArrayVec;
use crate::alloc::string::ToString;
use crate::compat::boxed::Box;
use crate::compat::rand;
use crate::compat::string::String;
use crate::compat::vec::Vec;
use crate::errcode::{Kind, Origin};
use crate::{
cbor_encode_preallocate, deserialize, serialize, Decodable, Encodable, Encoded, Message, Result,
};
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, CborLen)]
#[rustfmt::skip]
#[cbor(map)]
pub struct RequestHeader {
#[n(1)] id: Id,
#[n(2)] pub path: String,
#[n(3)] method: Option<Method>,
#[n(4)] has_body: bool,
}
impl RequestHeader {
pub fn new<P: Into<String>>(method: Method, path: P, has_body: bool) -> Self {
RequestHeader {
id: Id::fresh(),
method: Some(method),
path: path.into(),
has_body,
}
}
pub fn method_string(&self) -> String {
self.method
.map(|m| m.to_string())
.unwrap_or("no method".to_string())
}
}
#[derive(Debug, Clone, Encode, Decode, CborLen, PartialEq, Eq)]
#[rustfmt::skip]
#[cbor(map)]
pub struct ResponseHeader {
#[n(1)] id: Id,
#[n(2)] re: Id,
#[n(3)] status: Option<Status>,
#[n(4)] has_body: bool,
}
impl ResponseHeader {
pub fn is_ok(&self) -> bool {
self.status.map(|s| s == Status::Ok).unwrap_or(false)
}
}
#[derive(Clone)]
pub enum Reply<T> {
Successful(T),
Failed(Error, Option<Status>),
}
impl<T: Serialize> Serialize for Reply<T> {
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Reply::Successful(t) => t.serialize(serializer),
Reply::Failed(e, Some(s)) => {
let mut map: HashMap<&str, String> = Default::default();
map.insert("error", e.to_string());
map.insert("status", s.to_string());
serializer.collect_map(map)
}
Reply::Failed(e, None) => serializer.serialize_str(&e.to_string()),
}
}
}
impl<T: Display> Display for Reply<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Reply::Successful(t) => f.write_str(t.to_string().as_str()),
Reply::Failed(e, status) => {
if let Some(m) = e.message() {
f.write_str(format!("Failed request: {m}").as_str())?
} else {
f.write_str("Failed request")?
};
if let Some(status) = status {
f.write_str(format!("status: {status}").as_str())?
};
Ok(())
}
}
}
}
impl<T> Reply<T> {
#[track_caller]
pub fn success(self) -> Result<T> {
match self {
Reply::Successful(t) => Ok(t),
Reply::Failed(e, _) => Err(crate::Error::new(
Origin::Api,
Kind::Invalid,
e.message().unwrap_or("no message defined for this error"),
)),
}
}
#[track_caller]
pub fn error(&self) -> Result<Option<String>> {
match self {
Reply::Successful(_) => Ok(None),
Reply::Failed(e, _) => Ok(Some(
e.message()
.unwrap_or("no message defined for this error")
.to_string(),
)),
}
}
#[cfg(feature = "std")]
#[track_caller]
pub fn miette_success(self, request_kind: &str) -> Result<T, miette::Report> {
match self {
Reply::Successful(t) => Ok(t),
Reply::Failed(e, status) => {
let message = if let Some(message) = e.message {
format!("Failed request to {request_kind} ({message})")
} else {
format!("Failed request to {request_kind}")
};
let internal = internal::MietteError { message, status };
Err(miette::Report::from(internal))
}
}
}
#[track_caller]
pub fn found(self) -> Result<Option<T>> {
match self {
Reply::Successful(t) => Ok(Some(t)),
Reply::Failed(_, Some(Status::NotFound)) => Ok(None),
Reply::Failed(e, _) => Err(crate::Error::new(
Origin::Api,
Kind::Invalid,
e.message().unwrap_or("no message defined for this error"),
)),
}
}
}
#[derive(Debug, Default, Copy, Clone, Encode, Decode, CborLen, PartialEq, Eq, PartialOrd, Ord)]
#[cbor(transparent)]
pub struct Id(#[n(0)] u32);
#[derive(Debug, PartialEq, Eq, Copy, Clone, Encode, Decode, CborLen)]
#[rustfmt::skip]
#[cbor(index_only)]
pub enum Method {
#[n(0)] Get,
#[n(1)] Post,
#[n(2)] Put,
#[n(3)] Delete,
#[n(4)] Patch,
}
impl Display for Method {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Get => "GET",
Self::Post => "POST",
Self::Put => "PUT",
Self::Delete => "DELETE",
Self::Patch => "PATCH",
})
}
}
#[derive(Debug, Copy, Clone, Encode, Decode, CborLen, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
#[rustfmt::skip]
#[cbor(index_only)]
pub enum Status {
#[n(200)] Ok,
#[n(400)] BadRequest,
#[n(401)] Unauthorized,
#[n(403)] Forbidden,
#[n(404)] NotFound,
#[n(408)] Timeout,
#[n(409)] Conflict,
#[n(405)] MethodNotAllowed,
#[n(500)] InternalServerError,
#[n(501)] NotImplemented,
}
impl Display for Status {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(match self {
Status::Ok => "200 Ok",
Status::BadRequest => "400 BadRequest",
Status::Unauthorized => "401 Unauthorized",
Status::Forbidden => "403 Forbidden",
Status::NotFound => "404 NotFound",
Status::Timeout => "408 Timeout",
Status::Conflict => "409 Conflict",
Status::MethodNotAllowed => "405 MethodNotAllowed",
Status::InternalServerError => "500 InternalServerError",
Status::NotImplemented => "501 NotImplemented",
})
}
}
impl Id {
pub fn fresh() -> Self {
Id(rand::random::<u32>().saturating_add(1))
}
}
impl From<Id> for u32 {
fn from(n: Id) -> Self {
n.0
}
}
impl Display for Id {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:08x}", self.0)
}
}
impl RequestHeader {
pub fn id(&self) -> Id {
self.id
}
pub fn path(&self) -> &str {
&self.path
}
pub fn path_segments<const N: usize>(&self) -> Segments<N> {
Segments::parse(self.path())
}
pub fn method(&self) -> Option<Method> {
self.method
}
pub fn has_body(&self) -> bool {
self.has_body
}
}
impl ResponseHeader {
pub fn new(re: Id, status: Status, has_body: bool) -> Self {
ResponseHeader {
id: Id::fresh(),
re,
status: Some(status),
has_body,
}
}
pub fn id(&self) -> Id {
self.id
}
pub fn re(&self) -> Id {
self.re
}
pub fn status(&self) -> Option<Status> {
self.status
}
pub fn has_body(&self) -> bool {
self.has_body
}
}
#[derive(Debug, Clone, Default, Encode, Decode, CborLen, Message, PartialEq, Eq)]
#[rustfmt::skip]
#[cbor(map)]
pub struct Error {
#[n(1)] path: Option<String>,
#[n(2)] method: Option<Method>,
#[n(3)] message: Option<String>,
#[b(4)] cause: Option<Box<Error>>,
}
impl Encodable for Error {
fn encode(self) -> Result<Encoded> {
cbor_encode_preallocate(self)
}
}
impl Decodable for Error {
fn decode(e: &[u8]) -> Result<Self> {
Ok(minicbor::decode(e)?)
}
}
impl Error {
#[track_caller]
pub fn new(path: &str) -> Self {
Error {
method: None,
path: Some(path.to_string()),
message: None,
cause: None,
}
}
#[track_caller]
pub fn new_without_path() -> Self {
Error {
method: None,
path: None,
message: None,
cause: None,
}
}
#[track_caller]
pub fn from_failed_request(req: &RequestHeader, message: &str) -> Error {
let mut e = Error::new(req.path()).with_message(message);
if let Some(m) = req.method() {
e = e.with_method(m)
};
e
}
pub fn with_method(mut self, m: Method) -> Self {
self.method = Some(m);
self
}
pub fn set_method(&mut self, m: Method) {
self.method = Some(m);
}
pub fn with_message(mut self, m: impl AsRef<str>) -> Self {
self.message = Some(m.as_ref().to_string());
self
}
pub fn with_cause(mut self, e: Error) -> Self {
self.cause = Some(Box::new(e));
self
}
pub fn path(&self) -> Option<&str> {
self.path.as_deref()
}
pub fn method(&self) -> Option<Method> {
self.method
}
pub fn message(&self) -> Option<&str> {
self.message.as_deref()
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let fields: Vec<String> = vec![
self.message.clone().map(|m| format!("message: {m}")),
self.path.clone().map(|p| format!("path: {p}")),
self.method.map(|m| format!("method: {m}")),
self.cause.clone().map(|c| c.to_string()),
]
.into_iter()
.flatten()
.collect();
write!(f, "{}", fields.join(", "))
}
}
#[cfg(feature = "std")]
mod internal {
use crate::api::Status;
use core::fmt::{Debug, Display, Formatter};
impl std::error::Error for MietteError {}
pub(crate) struct MietteError {
pub(crate) message: String,
pub(crate) status: Option<Status>,
}
impl Display for MietteError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)?;
if let Some(status) = &self.status {
write!(f, " ({})", status)?;
}
Ok(())
}
}
impl Debug for MietteError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(
f,
"MietteError {{ message: {:?}, code: {:?} }}",
self.message, self.status
)
}
}
#[cfg(feature = "std")]
impl miette::Diagnostic for MietteError {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.status.map(|s| {
let s: Box<dyn Display> = Box::new(s);
s
})
}
}
}
impl From<crate::Error> for Error {
#[track_caller]
fn from(e: crate::Error) -> Self {
Error {
method: None,
path: None,
message: Some(e.to_string()),
cause: None,
}
}
}
impl From<crate::Error> for Response<Error> {
#[track_caller]
fn from(e: crate::Error) -> Self {
match e.code().kind {
Kind::NotFound => Response::not_found_no_request(&e.to_string()),
_ => Response::internal_error_no_request(&e.to_string()),
}
}
}
impl From<minicbor::decode::Error> for Response<Error> {
#[track_caller]
fn from(e: minicbor::decode::Error) -> Self {
Response::bad_request_no_request(&e.to_string())
}
}
pub struct Segments<'a, const N: usize>(ArrayVec<[&'a str; N]>);
impl<'a, const N: usize> Segments<'a, N> {
pub fn parse(s: &'a str) -> Self {
if s.starts_with('/') {
Self(s.trim_start_matches('/').splitn(N, '/').collect())
} else {
Self(s.splitn(N, '/').collect())
}
}
pub fn as_slice(&self) -> &[&'a str] {
&self.0[..]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Request<T = ()> {
header: RequestHeader,
body: Option<T>,
}
impl<T> Request<T> {
pub fn id(mut self, id: Id) -> Self {
self.header.id = id;
self
}
pub fn path<P: Into<String>>(mut self, path: P) -> Self {
self.header.path = path.into();
self
}
pub fn method(mut self, m: Method) -> Self {
self.header.method = Some(m);
self
}
pub fn header(&self) -> &RequestHeader {
&self.header
}
pub fn into_parts(self) -> (RequestHeader, Option<T>) {
(self.header, self.body)
}
pub fn has_body(&self) -> bool {
self.header.has_body
}
}
impl Request {
pub fn get<P: Into<String>>(path: P) -> Request {
Request::build(Method::Get, path)
}
pub fn post<P: Into<String>>(path: P) -> Request {
Request::build(Method::Post, path)
}
pub fn put<P: Into<String>>(path: P) -> Request {
Request::build(Method::Put, path)
}
pub fn delete<P: Into<String>>(path: P) -> Request {
Request::build(Method::Delete, path)
}
pub fn patch<P: Into<String>>(path: P) -> Request {
Request::build(Method::Patch, path)
}
pub fn build<P: Into<String>>(method: Method, path: P) -> Request {
Request {
header: RequestHeader::new(method, path, false),
body: None,
}
}
}
impl Request<()> {
pub fn body<T: Message>(self, b: T) -> Request<T> {
let mut b = Request {
header: self.header,
body: Some(b),
};
b.header.has_body = true;
b
}
}
impl<T: Encodable> Request<T> {
fn encode_request<W>(self, buf: W) -> Result<(), encode::Error<W::Error>>
where
W: Write,
{
let mut e = Encoder::new(buf);
e.encode(&self.header)?;
if let Some(b) = self.body {
e.writer_mut()
.write_all(&<T as Encodable>::encode(b).map_err(encode::Error::message)?)
.map_err(|_| encode::Error::message("encoding error"))?;
}
Ok(())
}
fn into_vec(self) -> Result<Vec<u8>, encode::Error<<Vec<u8> as Write>::Error>> {
let mut buf = Vec::new();
self.encode_request(&mut buf)?;
Ok(buf)
}
}
impl<T: Encodable> Encodable for Request<T> {
fn encode(self) -> Result<Encoded> {
serialize(self.into_vec()?)
}
}
impl<T: Decodable> Decodable for Request<T> {
fn decode(e: &[u8]) -> Result<Self> {
let deserialized = deserialize::<Vec<u8>>(e)?;
let mut dec = Decoder::new(&deserialized);
let header: RequestHeader = dec.decode()?;
if header.has_body() {
let body = dec
.input()
.get(dec.position()..deserialized.len())
.ok_or_else(|| {
crate::Error::new(
Origin::Api,
Kind::Internal,
format!(
"can't access the remaining input bytes: {}/{}",
dec.position(),
deserialized.len()
),
)
})?;
Ok(Request {
header,
body: Some(<T as Decodable>::decode(body)?),
})
} else {
Ok(Request { header, body: None })
}
}
}
impl<T: Message> Message for Request<T> {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response<T = ()> {
header: ResponseHeader,
body: Option<core::result::Result<T, Error>>,
}
impl<T> Response<T> {
pub fn id(mut self, id: Id) -> Self {
self.header.id = id;
self
}
pub fn re(mut self, re: Id) -> Self {
self.header.re = re;
self
}
pub fn status(mut self, s: Status) -> Self {
self.header.status = Some(s);
self
}
pub fn header(&self) -> &ResponseHeader {
&self.header
}
pub fn into_parts(self) -> (ResponseHeader, Option<core::result::Result<T, Error>>) {
(self.header, self.body)
}
pub fn with_headers(self, req: &RequestHeader) -> Self {
let id = req.id;
self.re(id)
}
pub fn is_ok(&self) -> bool {
self.header.is_ok()
}
pub fn has_body(&self) -> bool {
self.header.has_body()
}
pub fn get_body(self) -> Option<T> {
self.body.and_then(|b| b.ok())
}
pub fn get_error(self) -> Option<Error> {
self.body.and_then(|b| b.err())
}
}
impl<T: Encodable> Response<T> {
pub fn encode_response<W>(self, buf: W) -> Result<(), encode::Error<W::Error>>
where
W: Write,
{
let mut e = Encoder::new(buf);
e.encode(&self.header)?;
if let Some(b) = self.body {
match b {
Ok(t) => {
e.writer_mut()
.write_all(&<T as Encodable>::encode(t).map_err(encode::Error::message)?)
.map_err(|_| encode::Error::message("encoding error"))?;
}
Err(error) => {
e.writer_mut()
.write_all(
&<Error as Encodable>::encode(error).map_err(encode::Error::message)?,
)
.map_err(|_| encode::Error::message("encoding error"))?;
}
}
}
Ok(())
}
fn into_vec(self) -> Result<Vec<u8>, encode::Error<<Vec<u8> as Write>::Error>> {
let mut buf = Vec::new();
self.encode_response(&mut buf)?;
Ok(buf)
}
pub fn encode_body(self) -> Result<Response<Vec<u8>>> {
let (header, body) = self.into_parts();
let r = if let Some(b) = body {
match b {
Ok(t) => Response {
header,
body: Some(Ok(<T as Encodable>::encode(t)?)),
},
Err(e) => Response {
header,
body: Some(Err(e)),
},
}
} else {
Response { header, body: None }
};
Ok(r)
}
}
impl<T: Encodable> Encodable for Response<T> {
fn encode(self) -> Result<Encoded> {
serialize(self.into_vec()?)
}
}
impl<T: Decodable> Decodable for Response<T> {
fn decode(e: &[u8]) -> Result<Self> {
let deserialized = deserialize::<Vec<u8>>(e)?;
let mut dec = Decoder::new(&deserialized);
let header: ResponseHeader = dec.decode()?;
if header.is_ok() {
if header.has_body() {
let body = dec
.input()
.get(dec.position()..deserialized.len())
.ok_or_else(|| {
crate::Error::new(
Origin::Api,
Kind::Internal,
format!(
"can't access the remaining input bytes: {}/{}",
dec.position(),
deserialized.len()
),
)
})?;
Ok(Response {
header,
body: Some(Ok(<T as Decodable>::decode(body)?)),
})
} else {
Ok(Response { header, body: None })
}
} else {
let error = if matches!(dec.datatype(), Ok(Type::String)) {
dec.decode::<String>()
.map(|msg| Error::new_without_path().with_message(msg))
.unwrap_or_default()
} else if matches!(dec.datatype(), Ok(Type::Bytes)) {
if let Some(message) = dec
.decode::<ByteVec>()
.ok()
.and_then(|v| String::from_utf8(v.to_vec()).ok())
{
Error::new_without_path().with_message(message)
} else {
dec.decode::<Error>().unwrap_or_default()
}
} else {
dec.decode::<Error>().unwrap_or_default()
};
Ok(Response {
header,
body: Some(Err(error)),
})
}
}
}
impl<T: Message> Message for Response<T> {}
impl Response<Vec<u8>> {
pub fn to_reply<T: Decodable>(self) -> Result<Reply<T>> {
let (header, body) = self.into_parts();
if let Some(body) = body {
match body {
Ok(t) => match T::decode(t.as_slice()) {
Err(e) => Err(crate::Error::new(
Origin::Api,
Kind::Serialization,
format!("{e:?}"),
)),
Ok(r) => Ok(Reply::Successful(r)),
},
Err(error) => Ok(Reply::Failed(error, header.status())),
}
} else if header.is_ok() {
Err(crate::Error::new(
Origin::Api,
Kind::Serialization,
"expected a message body, got nothing".to_string(),
))
} else {
Err(crate::Error::new(
Origin::Api,
Kind::Serialization,
"expected an error message, got nothing".to_string(),
))
}
}
pub fn to_empty_reply(self, request_header: &RequestHeader) -> Result<Reply<()>> {
let status = self.header().status();
if !self.is_ok() {
Ok(Reply::Failed(
Error::from_failed_request(request_header, &self.parse_err_msg()),
status,
))
} else {
Ok(Reply::Successful(()))
}
}
}
impl Response<()> {
pub fn body<T: Encodable>(self, b: T) -> Response<T> {
let mut b = Response {
header: self.header,
body: Some(Ok(b)),
};
b.header.has_body = true;
b
}
}
impl Response {
fn with_status(re: Id, status: Status) -> Response {
Response {
header: ResponseHeader::new(re, status, false),
body: None,
}
}
pub fn with_status_no_request(status: Status) -> Response {
Response::with_status(Id::default(), status)
}
pub fn error(r: &RequestHeader, msg: &str, status: Status) -> Response<Error> {
let e = Error::from_failed_request(r, msg);
Response::with_status(r.id(), status).body(e)
}
pub fn ok() -> Response {
Response::with_status(Id::default(), Status::Ok)
}
pub fn bad_request_no_request(msg: &str) -> Response<Error> {
let e = Error::new_without_path().with_message(msg);
Response::with_status(Id::default(), Status::BadRequest).body(e)
}
pub fn bad_request(r: &RequestHeader, msg: &str) -> Response<Error> {
Self::error(r, msg, Status::BadRequest)
}
pub fn not_found(r: &RequestHeader, msg: &str) -> Response<Error> {
Self::error(r, msg, Status::NotFound)
}
pub fn not_found_no_request(msg: &str) -> Response<Error> {
let e = Error::new_without_path().with_message(msg);
Response::with_status(Id::default(), Status::NotFound).body(e)
}
pub fn not_implemented(re: Id) -> Response {
Response::with_status(re, Status::NotImplemented)
}
pub fn unauthorized(re: Id) -> Response {
Response::with_status(re, Status::Unauthorized)
}
pub fn unauthorized_no_request(msg: &str) -> Response<Error> {
let e = Error::new_without_path().with_message(msg);
Response::with_status(Id::default(), Status::Unauthorized).body(e)
}
pub fn forbidden_no_request(re: Id) -> Response {
Response::with_status(re, Status::Forbidden)
}
pub fn forbidden(r: &RequestHeader, m: &str) -> Response<Error> {
let mut e = Error::new(r.path()).with_message(m);
if let Some(m) = r.method() {
e = e.with_method(m)
}
Response::with_status(r.id(), Status::Forbidden).body(e)
}
pub fn internal_error_no_request(msg: &str) -> Response<Error> {
error!(%msg);
let e = Error::new_without_path().with_message(msg);
Response::with_status(Id::default(), Status::InternalServerError).body(e)
}
pub fn internal_error(r: &RequestHeader, msg: &str) -> Response<Error> {
let mut e = Error::new(r.path()).with_message(msg);
if let Some(m) = r.method() {
e = e.with_method(m)
}
Response::with_status(r.id(), Status::InternalServerError).body(e)
}
pub fn unknown_path(r: &RequestHeader) -> Response<Error> {
Self::bad_request(r, "unknown path")
}
pub fn invalid_method(r: &RequestHeader) -> Response<Error> {
match r.method() {
Some(m) => {
let e = Error::new(r.path()).with_method(m);
Response::with_status(r.id(), Status::MethodNotAllowed).body(e)
}
None => {
let e = Error::new(r.path()).with_message("unknown method");
Response::not_implemented(r.id()).body(e)
}
}
}
}
impl Response<Vec<u8>> {
pub fn parse_error<T>(self) -> Result<Reply<T>> {
let status = self.header().status;
match self.body {
None => Err(crate::Error::new(
Origin::Api,
Kind::Serialization,
"missing body",
)),
Some(Ok(b)) => {
let error = if let Ok(msg) = <String as Decodable>::decode(&b) {
Ok(Error::new_without_path().with_message(msg))
} else {
<Error as Decodable>::decode(&b)
};
match error {
Ok(e) => Ok(Reply::Failed(e, status)),
Err(e) => Err(crate::Error::new(Origin::Api, Kind::Serialization, e)),
}
}
Some(Err(error)) => Ok(Reply::Failed(error, status)),
}
}
pub fn parse_err_msg(self) -> String {
let status = self.header().status;
let has_body = self.has_body();
match status {
Some(status) if has_body => match self.body {
None => {
format!(
"No error message could be found in the response. Status code: {status}"
)
}
Some(Ok(b)) => {
let error = if let Ok(msg) = <String as Decodable>::decode(&b) {
Ok(format!("Message: {msg}"))
} else {
<Error as Decodable>::decode(&b).map(|msg| format!("Message: {msg}"))
};
match error {
Ok(err) => {
format!(
"An error occurred while processing the request. Status code: {status}. {err}"
)
}
Err(err) => {
let msg =
format!("No error message could be read from the response: {err}");
error!(msg);
msg
}
}
}
Some(Err(err)) => {
format!(
"An error occurred while processing the request. Status code: {status}. {err}"
)
}
},
Some(status) => {
format!("An error occurred while processing the request. Status code: {status}")
}
None => "No status code found in response".to_string(),
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Cbor<'a>(pub &'a [u8]);
impl<C> Encode<C> for Cbor<'_> {
fn encode<W>(&self, e: &mut Encoder<W>, _: &mut C) -> Result<(), encode::Error<W::Error>>
where
W: Write,
{
e.writer_mut()
.write_all(self.0)
.map_err(encode::Error::write)
}
}
#[cfg(test)]
mod tests {
use quickcheck::{quickcheck, Arbitrary, Gen, TestResult};
use crate::cbor::schema::tests::validate_with_schema;
use crate::cbor_encode_preallocate;
use super::*;
quickcheck! {
fn request(r: RequestHeader) -> TestResult {
validate_with_schema("request", r)
}
fn response(r: ResponseHeader) -> TestResult {
validate_with_schema("response", r)
}
fn error(e: Error) -> TestResult {
validate_with_schema("error", e)
}
fn type_check(a: RequestHeader, b: ResponseHeader, c: Error) -> TestResult {
let cbor_a = cbor_encode_preallocate(a).unwrap();
let cbor_b = cbor_encode_preallocate(b).unwrap();
let cbor_c = cbor_encode_preallocate(c).unwrap();
assert!(minicbor::decode::<ResponseHeader>(&cbor_a).is_err());
assert!(minicbor::decode::<Error>(&cbor_a).is_err());
assert!(minicbor::decode::<RequestHeader>(&cbor_b).is_err());
assert!(minicbor::decode::<Error>(&cbor_b).is_err());
assert!(minicbor::decode::<RequestHeader>(&cbor_c).is_err());
assert!(minicbor::decode::<ResponseHeader>(&cbor_c).is_err());
TestResult::passed()
}
}
#[test]
fn test_roundtrip_request() {
let request = Request::post("path");
assert_eq!(
Request::decode(&Request::encode(request.clone()).unwrap()).ok(),
Some(request)
);
let request = Request::post("path").body(());
assert_eq!(
Request::decode(&Request::encode(request.clone()).unwrap()).ok(),
Some(request)
);
let person = Person {
name: "me".into(),
age: 42,
};
let request = Request::post("path").body(person.clone());
assert_eq!(
Request::decode(&Request::encode(request.clone()).unwrap()).ok(),
Some(request)
);
let request = Request::post("path").body(person.clone());
let decoded: Request<Vec<u8>> =
Request::decode(&Request::encode(request.clone()).unwrap()).unwrap();
let decoded_person = <Person as Decodable>::decode(&decoded.body.unwrap()).ok();
assert_eq!(decoded_person, Some(person));
}
#[test]
fn test_roundtrip_response() {
let response = Response::ok();
assert_eq!(
Response::decode(&Response::encode(response.clone()).unwrap()).ok(),
Some(response)
);
let response = Response::ok().body(());
assert_eq!(
Response::decode(&Response::encode(response.clone()).unwrap()).ok(),
Some(response)
);
let person = Person {
name: "me".into(),
age: 42,
};
let response = Response::ok().body(person.clone());
assert_eq!(
Response::decode(&Response::encode(response.clone()).unwrap()).ok(),
Some(response)
);
let request = Response::ok().body(person.clone());
let decoded: Response<Vec<u8>> =
Response::decode(&Response::encode(request.clone()).unwrap()).unwrap();
let decoded_person = <Person as Decodable>::decode(&decoded.body.unwrap().unwrap()).ok();
assert_eq!(decoded_person, Some(person));
}
#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, CborLen, Message)]
struct Person {
#[n(1)]
name: String,
#[n(2)]
age: u8,
}
impl Encodable for Person {
fn encode(self) -> Result<Encoded> {
cbor_encode_preallocate(self)
}
}
impl Decodable for Person {
fn decode(encoded: &[u8]) -> Result<Self> {
Ok(minicbor::decode(encoded)?)
}
}
impl Arbitrary for RequestHeader {
fn arbitrary(g: &mut Gen) -> Self {
RequestHeader::new(
*g.choose(METHODS).unwrap(),
String::arbitrary(g),
bool::arbitrary(g),
)
}
}
impl Arbitrary for ResponseHeader {
fn arbitrary(g: &mut Gen) -> Self {
ResponseHeader::new(Id::fresh(), *g.choose(STATUS).unwrap(), bool::arbitrary(g))
}
}
impl Arbitrary for Error {
fn arbitrary(g: &mut Gen) -> Self {
let mut e = Error::new(&String::arbitrary(g));
if bool::arbitrary(g) {
e = e.with_method(*g.choose(METHODS).unwrap())
}
if bool::arbitrary(g) {
e = e.with_message(String::arbitrary(g))
}
e
}
}
const METHODS: &[Method] = &[
Method::Get,
Method::Post,
Method::Put,
Method::Delete,
Method::Patch,
];
const STATUS: &[Status] = &[
Status::Ok,
Status::BadRequest,
Status::NotFound,
Status::MethodNotAllowed,
Status::InternalServerError,
Status::NotImplemented,
];
}