#[derive(Debug)]
pub struct Error {
code: u8,
#[cfg(feature = "error_unprocessed_coap_option")]
unprocessed_option: Option<core::num::NonZeroU16>,
#[cfg(feature = "error_request_body_error_position")]
request_body_error_position: Option<u32>,
#[cfg(feature = "error_max_age")]
max_age: Option<u32>,
#[allow(dead_code)]
#[cfg(any(feature = "error_title", debug_assertions))]
title: Option<&'static str>,
}
impl Error {
const MAX_ENCODED_LEN: usize = {
let mut count = 0;
count += 1; if cfg!(feature = "error_unprocessed_coap_option") {
count += 1 + 3;
}
if cfg!(feature = "error_request_body_error_position") {
count += 2 + 5;
}
count
};
pub fn bad_option(unprocessed_option: u16) -> Self {
let special_code = match unprocessed_option {
coap_numbers::option::ACCEPT => Some(coap_numbers::code::NOT_ACCEPTABLE),
coap_numbers::option::PROXY_URI | coap_numbers::option::PROXY_SCHEME => {
Some(coap_numbers::code::PROXYING_NOT_SUPPORTED)
}
coap_numbers::option::CONTENT_FORMAT => {
Some(coap_numbers::code::UNSUPPORTED_CONTENT_FORMAT)
}
_ => None,
};
#[allow(unused)]
let unprocessed_option = if special_code.is_some() {
None
} else {
core::num::NonZeroU16::try_from(unprocessed_option).ok()
};
let code = special_code.unwrap_or(coap_numbers::code::BAD_OPTION);
#[allow(clippy::needless_update)]
Self {
code,
#[cfg(feature = "error_unprocessed_coap_option")]
unprocessed_option,
..Self::otherwise_empty()
}
}
pub fn bad_request_with_rbep(#[allow(unused)] byte: usize) -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::BAD_REQUEST,
#[cfg(feature = "error_request_body_error_position")]
request_body_error_position: byte.try_into().ok(),
..Self::otherwise_empty()
}
}
#[inline(always)]
fn problem_details_count(&self) -> u8 {
#[allow(unused_mut)]
let mut count = 0;
#[cfg(feature = "error_unprocessed_coap_option")]
if self.unprocessed_option.is_some() {
count += 1;
}
#[cfg(feature = "error_request_body_error_position")]
if self.request_body_error_position.is_some() {
count += 1;
}
count
}
pub fn from_unionerror(
_err: impl core::fmt::Debug + coap_message::error::RenderableOnMinimal,
) -> Self {
Self::internal_server_error()
}
pub fn bad_request() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::BAD_REQUEST,
..Self::otherwise_empty()
}
}
pub fn unauthorized() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::UNAUTHORIZED,
..Self::otherwise_empty()
}
}
pub fn forbidden() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::FORBIDDEN,
..Self::otherwise_empty()
}
}
pub fn not_found() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::NOT_FOUND,
..Self::otherwise_empty()
}
}
pub fn method_not_allowed() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::METHOD_NOT_ALLOWED,
..Self::otherwise_empty()
}
}
pub fn not_acceptable() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::NOT_ACCEPTABLE,
..Self::otherwise_empty()
}
}
pub fn unsupported_content_format() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::UNSUPPORTED_CONTENT_FORMAT,
..Self::otherwise_empty()
}
}
pub fn internal_server_error() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::INTERNAL_SERVER_ERROR,
..Self::otherwise_empty()
}
}
pub fn service_unavailable() -> Self {
#[allow(clippy::needless_update)]
Self {
code: coap_numbers::code::SERVICE_UNAVAILABLE,
..Self::otherwise_empty()
}
}
#[cfg(feature = "error_max_age")]
pub fn with_max_age(self, max_age: u32) -> Self {
Self {
max_age: Some(max_age),
..self
}
}
pub fn with_title(self, title: &'static str) -> Self {
Self {
#[cfg(any(feature = "error_title", debug_assertions))]
title: Some(title),
..self
}
}
#[inline]
fn otherwise_empty() -> Self {
Self {
code: 0,
#[cfg(feature = "error_unprocessed_coap_option")]
unprocessed_option: None,
#[cfg(feature = "error_request_body_error_position")]
request_body_error_position: None,
#[cfg(feature = "error_max_age")]
max_age: None,
#[cfg(any(feature = "error_title", debug_assertions))]
title: None,
}
}
}
impl coap_message::error::RenderableOnMinimal for Error {
type Error<IE: coap_message::error::RenderableOnMinimal + core::fmt::Debug> = IE;
fn render<M: coap_message::MinimalWritableMessage>(
self,
message: &mut M,
) -> Result<(), Self::Error<M::UnionError>> {
use coap_message::{Code, OptionNumber};
message.set_code(M::Code::new(self.code)?);
let mut pd_count = self.problem_details_count();
if pd_count > 0 {
const PROBLEM_DETAILS: Option<u16> =
coap_numbers::content_format::from_str("application/concise-problem-details+cbor");
let cfopt = M::OptionNumber::new(coap_numbers::option::CONTENT_FORMAT);
if let Some((pd, cfopt)) = PROBLEM_DETAILS.zip(cfopt.ok()) {
if message.add_option_uint(cfopt, pd).is_err() {
pd_count = 0;
}
}
};
#[cfg(feature = "error_max_age")]
if let Some(max_age) = self.max_age {
message.add_option_uint(
M::OptionNumber::new(coap_numbers::option::MAX_AGE)?,
max_age,
)?;
}
if pd_count > 0 {
let mut buf = [0u8; Self::MAX_ENCODED_LEN];
if let Ok(written) = (|| {
let mut cursor = minicbor::encode::write::Cursor::new(buf.as_mut());
let mut encoder = minicbor::Encoder::new(&mut cursor);
#[allow(unused_mut)]
let mut encoder = encoder.map(pd_count.into())?;
#[cfg(feature = "error_unprocessed_coap_option")]
if let Some(unprocessed_option) = self.unprocessed_option {
encoder = encoder.i8(-8)?;
encoder = encoder.u16(unprocessed_option.into())?;
}
#[cfg(feature = "error_request_body_error_position")]
if let Some(position) = self.request_body_error_position {
encoder = encoder.i8(-25)?;
encoder = encoder.u32(position)?;
}
let _ = encoder;
let written = cursor.position();
Ok::<_, minicbor::encode::Error<_>>(written)
})() {
message.set_payload(&buf[..written])?;
}
} else {
#[cfg(feature = "error_title")]
if let Some(title) = self.title {
message.set_payload(title.as_bytes())?;
}
}
Ok(())
}
}