micropb 0.6.0

Rust Protobuf library targetting embedded systems and no_std environments
Documentation
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
//! This crate provides a thin "library layer" used by the code generated by `micropb-gen`. Its
//! main purpose is to provide traits and APIs for encoding and decoding Protobuf messages.
//!
//! **For info on the generated code, see [`micropb-gen`](https://docs.rs/micropb-gen/latest/micropb_gen/).**
//!
//! # Decoder and Encoder
//!
//! `micropb` does not force a specific implementation for Protobuf data streams. Instead, streams
//! are represented as the [`PbRead`] and [`PbWrite`] traits, similar to
//! [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and
//! [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) from the standard library.
//!
//! In addition, `micropb` provides [`PbDecoder`] and [`PbEncoder`], which wrap input and output
//! streams respectively. Their job is to read and write the generated Rust types into Protobuf
//! data streams.
//!
//! ## Message traits
//!
//! Rust message structs generated by `micropb-gen` all implement [`MessageDecode`] and
//! [`MessageEncode`], which provide logic to read or write the message onto the data stream.
//!
//! ```rust
//! use micropb::{PbRead, PbDecoder, PbWrite, PbEncoder, MessageEncode, MessageDecode};
//! # use heapless_0_9 as heapless;
//!
//! # #[derive(Default)]
//! # struct ProtoMessage;
//! # impl MessageDecode for ProtoMessage {
//! #     fn decode<R: PbRead>(
//! #         &mut self,
//! #         decoder: &mut PbDecoder<R>,
//! #         len: usize,
//! #     ) -> Result<(), micropb::DecodeError<R::Error>> {
//! #         Ok(())
//! #     }
//! # }
//! # impl MessageEncode for ProtoMessage {
//! #     const MAX_SIZE: Result<usize, &str> = Ok(0);
//! #
//! #     fn encode<W: PbWrite>(&self, encoder: &mut PbEncoder<W>) -> Result<(), W::Error> {
//! #         Ok(())
//! #     }
//! #
//! #     fn compute_size(&self) -> usize {
//! #         0
//! #     }
//! # }
//!
//! // ProtoMessage was generated by micropb
//! let mut message = ProtoMessage::default();
//!                                                                                                                                       
//! // Create decoder out of a byte slice, which is our input data stream
//! let data = [0x08, 0x96, 0x01, /* additional bytes */];
//! let mut decoder = PbDecoder::new(data.as_slice());
//! // Decode an instance of `ProtoMessage` from the decoder
//! message.decode(&mut decoder, data.len()).unwrap();
//! // Or, decode directly from the bytes
//! message.decode_from_bytes(data.as_slice()).unwrap();
//!
//! // Use heapless::Vec as the output stream and build an encoder around it
//! let mut output = heapless::Vec::<u8, 16>::new();
//! let mut encoder = PbEncoder::new(&mut output);
//! // Encode a `ProtoMessage` to the encoder
//! message.encode(&mut encoder).unwrap();
//! ```
//!
//! # Container Traits
//!
//! `micropb` provides several options out of the box for handling "collection fields" that have
//! multiple elements, such as strings and repeated fields. These include containers from
//! [`heapless`](https://docs.rs/heapless/latest/heapless) and
//! [`arrayvec`](https://docs.rs/arrayvec/latest/arrayvec), as well as the ol' reliable [`Vec`],
//! [`String`], and [`Cow`](std::borrow::Cow) for environments with allocators.
//!
//! However, if you need to use your own container types, you'll have to implement the traits in
//! the [`container`] module yourself. For more info on configuring container types, refer to the
//! [`micropb-gen` docs](https://docs.rs/micropb-gen/latest/micropb_gen/#repeated-map-string-and-bytes-fields).
//!
//! # Feature Flags
//!
//! - **encode**: Enable support for encoding and computing the size of messages. If disabled, the
//!   generator should be configured to not generate encoding logic via
//!   [`Generator::encode_decode`](https://docs.rs/micropb-gen/latest/micropb_gen/struct.Generator.html#method.encode_decode).
//!   Enabled by default.
//!
//! - **decode**: Enable support for decoding messages. If disabled, the generator should be
//!   configured to not generate decoding logic via
//!   [`Generator::encode_decode`](https://docs.rs/micropb-gen/latest/micropb_gen/struct.Generator.html#method.encode_decode).
//!   Enabled by default.
//!
//! - **enable-64bit**: Enable 64-bit integer operations. If disabled, then 64-bit fields such as
//!   `int64` or `sint64` should have
//!   [`Config::int_size`](https://docs.rs/micropb-gen/latest/micropb_gen/config/struct.Config.html#method.int_size)
//!   set to 32 bits or less. Has no effect on `double` fields. Enabled by default.
//!
//! - **alloc**: Implements container traits on `Vec`, `String`, and `BTreeMap` from
//!   [`alloc`](https://doc.rust-lang.org/alloc), allowing them to be used as container fields.
//!   Corresponds with
//!   [`Generator::use_container_alloc`](https://docs.rs/micropb-gen/latest/micropb_gen/struct.Generator.html#method.use_container_alloc)
//!   from `micropb-gen`.
//!
//! - **std**: Enables standard library and implements [`PbMap`] on `HashMap`.
//!   Corresponds with
//!   [`Generator::use_container_std`](https://docs.rs/micropb-gen/latest/micropb_gen/struct.Generator.html#method.use_container_std)
//!   from `micropb-gen`.
//!
//! - **container-heapless-0-9**: Implements container traits on `Vec`, `String`, and `IndexMap` from
//!   [`heapless`](https://docs.rs/heapless/latest/heapless) v0.9, allowing them to be used as container
//!   fields. Corresponds with
//!   [`Generator::use_container_heapless`](https://docs.rs/micropb-gen/latest/micropb_gen/struct.Generator.html#method.use_container_heapless)
//!   from `micropb-gen`.
//!
//! - **container-heapless-0-8**: Implements container traits on types from `heapless` v0.8. Corresponds with
//!   [`Generator::use_container_heapless`](https://docs.rs/micropb-gen/latest/micropb_gen/struct.Generator.html#method.use_container_heapless)
//!   from `micropb-gen`.
//!
//! - **container-arrayvec-0-7**: Implements container traits on `ArrayVec` and `ArrayString` from
//!   [`arrayvec`](https://docs.rs/arrayvec/latest/arrayvec) v0.7, allowing them to be used as container
//!   fields. Corresponds with
//!   [`Generator::use_container_arrayvec`](https://docs.rs/micropb-gen/latest/micropb_gen/struct.Generator.html#method.use_container_arrayvec)
//!   from `micropb-gen`.

#[cfg(feature = "alloc")]
extern crate alloc;

pub mod container;
#[cfg(feature = "decode")]
mod decode;
#[cfg(feature = "encode")]
mod encode;
pub mod field;
mod message;
mod misc;
#[cfg(feature = "encode")]
pub mod size;

pub use container::impl_fixed_len::FixedLenString;
pub use container::{PbBytes, PbMap, PbString, PbVec};
#[cfg(all(feature = "decode", feature = "std"))]
pub use decode::StdReader;
#[cfg(feature = "decode")]
pub use decode::{DecodeError, PbDecoder, PbRead};
#[cfg(all(feature = "encode", feature = "std"))]
pub use encode::StdWriter;
#[cfg(feature = "encode")]
pub use encode::{PbEncoder, PbWrite};
#[cfg(feature = "decode")]
pub use field::FieldDecode;
#[cfg(feature = "encode")]
pub use field::FieldEncode;
#[cfg(feature = "decode")]
pub use message::MessageDecode;
#[cfg(feature = "encode")]
pub use message::{MessageEncode, MessageEncodeCached};

#[macro_export]
#[doc(hidden)]
macro_rules! const_map {
    ($res:expr, |$val:ident| $body:expr) => {
        match $res {
            ::core::result::Result::Ok($val) => ::core::result::Result::Ok($body),
            ::core::result::Result::Err(err) => ::core::result::Result::Err(err),
        }
    };
}

/// Protobuf wire type for varints.
pub const WIRE_TYPE_VARINT: u8 = 0;
/// Protobuf wire type for fixed 64-bit values.
pub const WIRE_TYPE_I64: u8 = 1;
/// Protobuf wire type for length-delimited records.
pub const WIRE_TYPE_LEN: u8 = 2;
/// Protobuf wire type for fixed 32-bit values.
pub const WIRE_TYPE_I32: u8 = 5;

#[derive(Debug, PartialEq, Clone, Copy)]
/// Protobuf tag, consisting of the field number and wire type.
pub struct Tag(u32);

impl Tag {
    #[inline]
    /// Create a tag from a field number and wire type.
    pub const fn from_parts(field_num: u32, wire_type: u8) -> Self {
        debug_assert!(wire_type <= 7);
        Self((field_num << 3) | (wire_type as u32))
    }

    #[inline]
    /// Get the wire type of the tag.
    pub const fn wire_type(&self) -> u8 {
        (self.0 & 0b111) as u8
    }

    #[inline]
    /// Get the field number of the tag.
    pub const fn field_num(&self) -> u32 {
        self.0 >> 3
    }

    #[inline]
    /// Return the integer representation of the tag.
    pub const fn varint(&self) -> u32 {
        self.0
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Field presence discipline
pub enum Presence {
    /// Implicit presence. Fields don't have flag to track presence, and default values are treated
    /// as not present.
    ///
    /// Used for Proto3 fields.
    Implicit,
    /// Explicit presence. Fields have flags to track presense.
    ///
    /// Use for optional fields.
    Explicit,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn tag() {
        let tag = Tag::from_parts(5, 4);
        assert_eq!(tag.varint(), 0x2C);
        assert_eq!(tag.field_num(), 5);
        assert_eq!(tag.wire_type(), 4);

        let tag = Tag::from_parts(0, 0);
        assert_eq!(tag.varint(), 0);
        assert_eq!(tag.field_num(), 0);
        assert_eq!(tag.wire_type(), 0);
    }

    #[test]
    fn const_macro() {
        const C1: Result<usize, &'static str> = const_map!(Ok(6), |n| n + 5);
        const C2: Result<usize, &'static str> = const_map!(Err::<usize, _>("test"), |n| n + 5);
        assert_eq!(C1, Ok(11));
        assert_eq!(C2, Err("test"));
    }
}