pub trait MinimalWritableMessage {
type Code: Code;
type OptionNumber: OptionNumber;
type AddOptionError: RenderableOnMinimal + Debug;
type SetPayloadError: RenderableOnMinimal + Debug;
type UnionError: RenderableOnMinimal + Debug + From<Self::AddOptionError> + From<Self::SetPayloadError> + From<<Self::Code as Code>::Error> + From<<Self::OptionNumber as OptionNumber>::Error>;
// Required methods
fn set_code(&mut self, code: Self::Code);
fn add_option(
&mut self,
number: Self::OptionNumber,
value: &[u8],
) -> Result<(), Self::AddOptionError>;
fn set_payload(&mut self, data: &[u8]) -> Result<(), Self::SetPayloadError>;
// Provided methods
fn set_from_message<M>(&mut self, msg: &M) -> Result<(), Self::UnionError>
where M: ReadableMessage + WithSortedOptions { ... }
fn add_option_str(
&mut self,
number: Self::OptionNumber,
value: &str,
) -> Result<(), Self::AddOptionError> { ... }
fn add_option_uint<U: Unsigned + ToBytes>(
&mut self,
number: Self::OptionNumber,
value: U,
) -> Result<(), Self::AddOptionError> { ... }
fn with_static_type_annotation(
&mut self,
) -> Option<RefMutWithStaticType<'_, Self>> { ... }
fn promote_to_mutable_writable_message(
&mut self,
) -> Option<&mut (impl MinimalWritableMessage<Code = Self::Code, OptionNumber = Self::OptionNumber, AddOptionError = Self::AddOptionError, SetPayloadError = Self::SetPayloadError, UnionError = Self::UnionError> + MutableWritableMessage)> { ... }
fn convert_code_error(e: <Self::Code as Code>::Error) -> Self::UnionError { ... }
fn convert_option_number_error(
e: <Self::OptionNumber as OptionNumber>::Error,
) -> Self::UnionError { ... }
fn convert_add_option_error(e: Self::AddOptionError) -> Self::UnionError { ... }
fn convert_set_payload_error(e: Self::SetPayloadError) -> Self::UnionError { ... }
}
Expand description
A message that can be written to, creating a CoAP request or response.
This is the bare minimum a message type needs to provide to generic applications. It is up to the user to ensure this valid sequence of operations:
- Exactly one call to
set_code
- Any number of calls to
add_option
, with monotonically increasing option numbers - Zero or one call to
set_payload
Steps that can reasonably fail at runtime are fallible – for example, a payload to be set may simply not fit within the message size. Adding options in the wrong sequence is also an expected source, eg. when code paths are triggered that were not tested in that combination.
Other errors violating the call sequence, such as failure to call set_code
, or adding an
option after the payload has been set, may be implemented in a panic. (When occurring in a
fallible operation, the implementation may also choose to report an error instead).
Failed operations may be retried (eg. with shorter values); the failed attempt must not have an effect on the message.
Implementations may tolerate erroneous call sequences as long as they can produce messages that are likely to match the caller’s expectations – no need to keep track of usage errors just to produce correct errors. Users may wrap messages in dedicated checkers for more strictness.
Required Associated Types§
Sourcetype Code: Code
type Code: Code
See Self::set_code()
Sourcetype AddOptionError: RenderableOnMinimal + Debug
type AddOptionError: RenderableOnMinimal + Debug
Error returned when an option can not be added (eg. for lack of space, or because an option of a higher number or even the payload was already set)
Sourcetype SetPayloadError: RenderableOnMinimal + Debug
type SetPayloadError: RenderableOnMinimal + Debug
Error returned when setting the payload (eg. for lack of space, or when a message of that type does not take a payload)
Sourcetype UnionError: RenderableOnMinimal + Debug + From<Self::AddOptionError> + From<Self::SetPayloadError> + From<<Self::Code as Code>::Error> + From<<Self::OptionNumber as OptionNumber>::Error>
type UnionError: RenderableOnMinimal + Debug + From<Self::AddOptionError> + From<Self::SetPayloadError> + From<<Self::Code as Code>::Error> + From<<Self::OptionNumber as OptionNumber>::Error>
Error type into which either of the other errors, as well as the errors for conversion of
the Code
and OptionNumber
, can be .into()
ed.
For many implementations it can make sense to use a single error type for all of those, in which case the From bounds are trivially fulfilled.
Required Methods§
Sourcefn set_code(&mut self, code: Self::Code)
fn set_code(&mut self, code: Self::Code)
Set the CoAP code of the message (in a request, that is the request method)
Sourcefn add_option(
&mut self,
number: Self::OptionNumber,
value: &[u8],
) -> Result<(), Self::AddOptionError>
fn add_option( &mut self, number: Self::OptionNumber, value: &[u8], ) -> Result<(), Self::AddOptionError>
Add an option to the message
Calls to this method need to happen in ascending numeric sequence.
The option number is pre-encoded in the Self::OptionNumber type. The value is provided in its serialized form. Under the aspect of option value formats, this adds opaque options (but may just as well be used for adding options in another format when they are pre-encoded).
Sourcefn set_payload(&mut self, data: &[u8]) -> Result<(), Self::SetPayloadError>
fn set_payload(&mut self, data: &[u8]) -> Result<(), Self::SetPayloadError>
Set the payload to the message
This must be called only once.
Provided Methods§
Sourcefn set_from_message<M>(&mut self, msg: &M) -> Result<(), Self::UnionError>where
M: ReadableMessage + WithSortedOptions,
fn set_from_message<M>(&mut self, msg: &M) -> Result<(), Self::UnionError>where
M: ReadableMessage + WithSortedOptions,
Copy code, options and payload in from a readable message
Sourcefn add_option_str(
&mut self,
number: Self::OptionNumber,
value: &str,
) -> Result<(), Self::AddOptionError>
fn add_option_str( &mut self, number: Self::OptionNumber, value: &str, ) -> Result<(), Self::AddOptionError>
Shortcut for add_option(self, number, value.as_bytes())
.
Implementations with type checked options can provide more efficient implementations (ie.
ones that don’t need to UTF-8-check when they feed the resulting bytes back into a string
field), but must still accept string options via the generic
add_option()
method.
Sourcefn add_option_uint<U: Unsigned + ToBytes>(
&mut self,
number: Self::OptionNumber,
value: U,
) -> Result<(), Self::AddOptionError>
fn add_option_uint<U: Unsigned + ToBytes>( &mut self, number: Self::OptionNumber, value: U, ) -> Result<(), Self::AddOptionError>
Shortcut for add_option
on a buffer containing the uint encoded value
Implementations with type checked options can provide more efficient implementations (ie.
ones that don’t need to decode the uint when reading it into a uint field), but must still
accept integer options via the generic add_option()
method.
Sourcefn with_static_type_annotation(
&mut self,
) -> Option<RefMutWithStaticType<'_, Self>>
fn with_static_type_annotation( &mut self, ) -> Option<RefMutWithStaticType<'_, Self>>
Type ID of Self or a ’static version of Self
This is not useful on its own, and the provided implementation merely returns None.
It can be used by concrete implementations of MinimalWritableMessage that then provide a
way to downcast a &mut impl MinimalWritableMessage
into a a &mut Self
. This is only
possible for types that are either 'static
or covariant over their lifetimes. It is up to
the implementations to implement that safely.
Using such downcasts is not generally recommended: It breaks the portability that using coap-message affords. It may still be useful in two kinds of cases:
-
When an implementation specific tool is used deeply within a CoAP handler after using generic middleware. Beware that middleware generally does not make any semver promises on the types it forwards – while it may send on the outermost
impl MinimalWritableMessage
type as-is to its inner handlers, it may just as well wrap them arbitrarily. -
While exploring the evolution of this crate’s traits, these provide an easy hatch.
Sourcefn promote_to_mutable_writable_message(
&mut self,
) -> Option<&mut (impl MinimalWritableMessage<Code = Self::Code, OptionNumber = Self::OptionNumber, AddOptionError = Self::AddOptionError, SetPayloadError = Self::SetPayloadError, UnionError = Self::UnionError> + MutableWritableMessage)>
fn promote_to_mutable_writable_message( &mut self, ) -> Option<&mut (impl MinimalWritableMessage<Code = Self::Code, OptionNumber = Self::OptionNumber, AddOptionError = Self::AddOptionError, SetPayloadError = Self::SetPayloadError, UnionError = Self::UnionError> + MutableWritableMessage)>
Tries to obtain a MutableWritableMessage from self.
This is used where a tool writing to a message might perform better (use fewer local resources) or can provide a higher quality representation if the advanced writability features are present.
§Implementation guidance
If possible, the typical implementation is Some(self)
. It is recommended to #[inline]
it (so that the branches for a None case are not even emitted).
It makes a lot of sense to specify the return value as Option<&mut Self>
. An
#[allow(refining_impl_trait_reachable)]
acknowledges that this constrains the
implementation’s future development, but given that the return type will also implement
MinimalWritableMessage
as part of the trait hierarchy, chances are that that will always
be the return type in any given library.
§Future development
As soon as trait methods can be const, this one should be. With that, it would be an option
to simplify the Handler interface to take a MinimalWritableMessage, and for the handlers
that need it to const { message.promote_to_mutable_writable_message().expect("Handler XY needs mutable messages") }
for compile time errors on non-matching handlers.
It was considered whether using a Result<impl MutableWritableMessage<...>, impl Any>
would bring any benefits. Given that there is no way known to the author to create a const
panic based on whether or not a type is inhabited, let’s wait for const trait methods
(which will bring an API change anyway), and rely on inlining and dead code elimination for
the time being.
Sourcefn convert_code_error(e: <Self::Code as Code>::Error) -> Self::UnionError
fn convert_code_error(e: <Self::Code as Code>::Error) -> Self::UnionError
Auxiliary function for converting Self::Code::Error
This should really not be needed, but serves well in allowing coap-request-imlementations to convert errors found during writing into their RequestUnionError that can be returned.
Sourcefn convert_option_number_error(
e: <Self::OptionNumber as OptionNumber>::Error,
) -> Self::UnionError
fn convert_option_number_error( e: <Self::OptionNumber as OptionNumber>::Error, ) -> Self::UnionError
Auxiliary function for converting Self::OptionNumber::Error
This should really not be needed, but serves well in allowing coap-request-imlementations to convert errors found during writing into their RequestUnionError that can be returned.
Sourcefn convert_add_option_error(e: Self::AddOptionError) -> Self::UnionError
fn convert_add_option_error(e: Self::AddOptionError) -> Self::UnionError
Auxiliary function for converting Self::AddOptionError
This should really not be needed, but serves well in allowing coap-request-imlementations to convert errors found during writing into their RequestUnionError that can be returned.
Sourcefn convert_set_payload_error(e: Self::SetPayloadError) -> Self::UnionError
fn convert_set_payload_error(e: Self::SetPayloadError) -> Self::UnionError
Auxiliary function for converting Self::SetPayloadError
This should really not be needed, but serves well in allowing coap-request-imlementations to convert errors found during writing into their RequestUnionError that can be returned.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.