micropb 0.6.0

Rust Protobuf library targetting embedded systems and no_std environments
Documentation
//! Traits for decoding and encoding Protobuf fields.
//!
//! Users can substitute their own field types into message structs generated by `micropb`. These
//! custom fields must implement the traits in this module, so that the generated code knows how to
//! encode and decode them. As such, these traits are implemented by users.

#[cfg(feature = "decode")]
use crate::decode::{DecodeError, PbDecoder, PbRead};
#[cfg(feature = "encode")]
use crate::encode::{PbEncoder, PbWrite};

#[cfg(feature = "decode")]
/// One or more Protobuf fields that can be decoded from the wire.
///
/// If multiple fields are included, then the fields will be decoded one at a time.
///
/// # Example
///
/// Example implementation on custom key-value pair:
/// ```no_run
/// use micropb::{PbRead, PbDecoder, Tag, DecodeError};
/// use micropb::field::FieldDecode;
///
/// // Custom field type consisting of 2 fields
/// struct KeyValuePair {
///     // Field 1 (int32)
///     key: i32,
///     // Field 2 (float)
///     val: f32,
/// }
///
/// impl FieldDecode for KeyValuePair {
///     fn decode_field<R: PbRead>(
///         &mut self,
///         tag: Tag,
///         decoder: &mut PbDecoder<R>,
///     ) -> Result<bool, DecodeError<R::Error>> {
///         // Use field number to determine which field to decode
///         match tag.field_num() {
///             1 => self.key = decoder.decode_int32()?,
///             2 => self.val = decoder.decode_float()?,
///             // Return false if field number is unrecognized
///             _ => return Ok(false),
///         }
///         Ok(true)
///     }
/// }
/// ```
pub trait FieldDecode {
    /// Decode one single field.
    ///
    /// The field being decoded is denoted by `tag`. If the field number of `tag` is unrecognized,
    /// return `false`. Otherwise, decode the field from the decoder and return `true`.
    ///
    /// This function can be called multiple times during decoding, once for each matching field on
    /// the wire. This is true even if the implementer only consists of one field, because the same
    /// field number can repeat on the wire.
    fn decode_field<R: PbRead>(
        &mut self,
        tag: crate::Tag,
        decoder: &mut PbDecoder<R>,
    ) -> Result<bool, DecodeError<R::Error>>;
}

#[cfg(feature = "decode")]
impl<T: FieldDecode> FieldDecode for &mut T {
    fn decode_field<R: PbRead>(
        &mut self,
        tag: crate::Tag,
        decoder: &mut PbDecoder<R>,
    ) -> Result<bool, DecodeError<R::Error>> {
        (*self).decode_field(tag, decoder)
    }
}

#[cfg(feature = "decode")]
/// Convenience implementation for fields wrapped in `Option`. If field is `None` when decoding,
/// the field will first be set to the default before decoding.
impl<T: Default + FieldDecode> FieldDecode for Option<T> {
    fn decode_field<R: PbRead>(
        &mut self,
        tag: crate::Tag,
        decoder: &mut PbDecoder<R>,
    ) -> Result<bool, DecodeError<R::Error>> {
        let f = self.get_or_insert_with(Default::default);
        f.decode_field(tag, decoder)
    }
}

#[cfg(feature = "encode")]
/// One or more Protobuf fields that can be encoded onto the wire.
///
/// If multiple fields are included, then all fields will be encoded at once.
///
/// # Example
///
/// Example implementation on custom bitfield:
/// ```no_run
/// use micropb::{PbWrite, PbEncoder, Tag, WIRE_TYPE_VARINT};
/// use micropb::field::FieldEncode;
///
/// // Custom field type consisting of 8 bools, stored as a bitfield
/// struct Bits(u8);
///
/// impl FieldEncode for Bits {
///     const MAX_SIZE: Result<usize, &str> = Ok(2 * 8);
///
///     fn encode_fields<W: PbWrite>(&self, encoder: &mut PbEncoder<W>) -> Result<(), W::Error> {
///         // Encode each of the 8 bits using field numbers 1 to 8
///         for i in 0..8 {
///             let b = (self.0 & (1 << i)) != 0;
///             encoder.encode_tag(Tag::from_parts(i + 1, WIRE_TYPE_VARINT))?;
///             encoder.encode_bool(b)?;
///         }
///         Ok(())
///     }
///
///     fn compute_fields_size(&self) -> usize {
///         2 * 8 // 8 fields, each one consisting of a tag and a bool
///     }
/// }
/// ```
pub trait FieldEncode {
    /// Maximum encoded size of the field on the wire.
    ///
    /// Used to calculate the max size of messages on the wire. Set this to `Err` if the size of
    /// the field is unbounded or if you don't care about calculating the max message size.
    const MAX_SIZE: Result<usize, &str>;

    /// Encode all fields, including the tags.
    ///
    /// Unlike `FieldDecode::decode_field`, this call is expected to write out complete fields,
    /// including the tags. It should also write out all fields as once, since it will only be
    /// called once. For non-packed repeated fields, each element is written out as its own field.
    fn encode_fields<W: PbWrite>(&self, encoder: &mut PbEncoder<W>) -> Result<(), W::Error>;

    /// Compute size of all fields, including the tags.
    fn compute_fields_size(&self) -> usize;
}

#[cfg(feature = "encode")]
impl<T: FieldEncode> FieldEncode for &T {
    const MAX_SIZE: Result<usize, &'static str> = T::MAX_SIZE;

    fn encode_fields<W: PbWrite>(&self, encoder: &mut PbEncoder<W>) -> Result<(), W::Error> {
        (*self).encode_fields(encoder)
    }

    fn compute_fields_size(&self) -> usize {
        (*self).compute_fields_size()
    }
}

#[cfg(feature = "encode")]
/// Convenience implementation for fields wrapped in `Option`. If the value is `None`, then the
/// field isn't encoded at all.
impl<T: FieldEncode> FieldEncode for Option<T> {
    const MAX_SIZE: Result<usize, &str> = T::MAX_SIZE;

    fn encode_fields<W: PbWrite>(&self, encoder: &mut PbEncoder<W>) -> Result<(), W::Error> {
        if let Some(f) = self {
            f.encode_fields(encoder)?;
        }
        Ok(())
    }

    fn compute_fields_size(&self) -> usize {
        if let Some(f) = self {
            f.compute_fields_size();
        }
        0
    }
}