//! Conversion to/from binary for arbitrary types. With `no_std` and `no_alloc` support.
//!
//! For more information about `#[derive(BitDecode, BitEncode)]` and its attributes, see
//! [`macro@BitDecode`] or [`macro@BitEncode`].
//!
//! # Example
//!
//! ```
//! # #[cfg(all(feature = "derive", feature = "alloc"))]
//! # {
//! # use bin_proto::{BitDecode, BitEncode, BitCodec};
//! #[derive(Debug, BitDecode, BitEncode, PartialEq)]
//! #[bin_proto(discriminant_type = u8)]
//! #[bin_proto(bits = 4)]
//! enum E {
//!     V1 = 1,
//!     #[bin_proto(discriminant = 4)]
//!     V4,
//! }
//!
//! #[derive(Debug, BitDecode, BitEncode, PartialEq)]
//! struct S {
//!     #[bin_proto(bits = 1)]
//!     bitflag: bool,
//!     #[bin_proto(bits = 3)]
//!     bitfield: u8,
//!     enum_: E,
//!     #[bin_proto(write_value = self.arr.len() as u8)]
//!     arr_len: u8,
//!     #[bin_proto(tag = arr_len as usize)]
//!     arr: Vec<u8>,
//!     #[bin_proto(tag_type = u16, tag_value = self.prefixed_arr.len() as u16)]
//!     prefixed_arr: Vec<u8>,
//!     #[bin_proto(untagged)]
//!     read_to_end: Vec<u8>,
//! }
//!
//! assert_eq!(
//!     S::decode_bytes(&[
//!         0b1000_0000 // bitflag: true (1)
//!        | 0b101_0000 // bitfield: 5 (101)
//!            | 0b0001, // enum_: V1 (0001)
//!         0x02, // arr_len: 2
//!         0x21, 0x37, // arr: [0x21, 0x37]
//!         0x00, 0x01, 0x33, // prefixed_arr: [0x33]
//!         0x01, 0x02, 0x03, // read_to_end: [0x01, 0x02, 0x03]
//!     ], bin_proto::BigEndian).unwrap().0,
//!     S {
//!         bitflag: true,
//!         bitfield: 5,
//!         enum_: E::V1,
//!         arr_len: 2,
//!         arr: vec![0x21, 0x37],
//!         prefixed_arr: vec![0x33],
//!         read_to_end: vec![0x01, 0x02, 0x03],
//!     }
//! );
//! # }
//! ```
//!
//! # Manual Implementations
//!
//! The [`macro@BitDecode`] and [`macro@BitEncode`] derive macros support the most common use-cases,
//! but it may sometimes be necessary to manually implement [`BitEncode`] or [`BitDecode`]. Both
//! traits have two generic parameters:
//! - `Ctx`: A mutable variable passed recursively down the codec chain
//! - `Tag`: A tag for specifying additional behavior
//!
//! `Tag` can have any type. The following are used throughout `bin-proto` and ensure
//! interoperability:
//! - [`Tag`]: Specifies that an additional tag is required during decoding, such as a length prefix
//!   for a [`Vec`](::alloc::vec::Vec), or a discriminant of an `enum`
//! - [`Untagged`]: Specifies that the type has a tag used during decoding, but this tag is not
//!   written during encoding
//! - [`Bits`]: Specified that the type is a bitfield, and can have a variable number of bits
//!
//! # Features
//!
//! - `std` — Enables support for types in the standard library.
//! - `alloc` — Enables support for types in the `alloc` crate.
//! - `derive` — Provides procedural macros for deriving traits [`BitEncode`] and [`BitDecode`].
//! - `prepend-tags` — Enables tag prepending for common types ([`Option`], [`str`], etc.), removing
//!   the need for explicit tag specification for encoding/decoding. **WARNING**: length tags are
//!   encoded as `usize`, meaning they may vary if targets have different pointer widths.

#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(rustdoc_internals))]
#![cfg_attr(docsrs, allow(internal_features))]
#![no_std]
#![deny(
    missing_docs,
    clippy::pedantic,
    clippy::nursery,
    clippy::cargo,
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::suspicious,
    clippy::complexity,
    clippy::perf,
    clippy::style
)]
#![allow(clippy::module_name_repetitions, clippy::missing_errors_doc)]

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

pub use self::codec::BitCodec;
pub use self::codec::{BitDecode, BitDecodeExt, BitEncode, BitEncodeExt};
pub use self::discriminable::Discriminable;
pub use self::error::{Error, Result};
pub use bitstream_io::{BigEndian, BitRead, BitWrite, Endianness, LittleEndian};

/// Derive the [`BitDecode`] and [`BitEncode`] traits.
///
/// # Scopes
///
/// ```ignore
/// #[container, enum]
/// enum Enum {
///     #[variant]
///     Variant {
///         #[field]
///         field: Type,
///     }
/// }
///
/// #[container, struct]
/// struct Struct {
///     #[field]
///     field: Type,
/// }
/// ```
///
/// # Using Context
///
/// The [`ctx`](#ctx) and [`ctx_bounds`](#ctx_bounds) attributes can be used to specify additional
/// context used during codec. The context can be accessed as `__ctx`, and the tag as `__tag` in any
/// attribute macro's `<expr>`, for example in [`tag`](#tag).
///
/// ```
/// # #[cfg(feature = "alloc")]
/// # {
/// # use bin_proto::{BitDecode, BitEncode};
/// struct Ctx {
///     n: u32,
/// }
///
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(ctx = Ctx)]
/// struct WithElementsLength {
///     count: u32,
///     foo: bool,
///     #[bin_proto(tag = count * __ctx.n)]
///     data: Vec<u32>,
/// }
/// # }
/// ```
///
/// # Attributes
///
/// | Attribute | Scope | Applicability |
/// |-|-|-|
/// | [`discriminant_type`](#discriminant_type) | enum | rw |
/// | [`discriminant`](#discriminant) | variant | rw |
/// | [`other`](#other) | variant | r |
/// | [`bits`](#bits) | field, enum | rw |
/// | [`untagged`](#untagged) | field | rw |
/// | [`tag`](#tag) | field | rw |
/// | [`tag_type`](#tag_type) | field | rw |
/// | [`write_value`](#write_value) | field | w |
/// | [`ctx`](#ctx) | container | rw |
/// | [`ctx_bounds`](#ctx_bounds) | container | rw |
/// | [`skip_encode`](#skip_encode) | field, variant | w |
/// | [`skip_decode`](#skip_decode) | field, variant | r |
/// | [`skip`](#skip) | field, variant | rw |
/// | [`pad_before`](#pad_before) | field, struct | rw |
/// | [`pad_after`](#pad_after) | field, struct | rw |
/// | [`magic`](#magic) | field, struct | rw |
/// | [`crate`](#crate) | struct, enum | rw |
///
/// ## `discriminant_type`
/// `#[bin_proto(discriminant_type = <type>)]`
/// - `<type>`: an arbitrary type that implements [`BitDecode`] or [`BitEncode`]
///
/// Specify if enum variant should be determined by a string or interger representation of its
/// discriminant.
///
/// Falls back to the type specified in `#[repr(...)]` if not present.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(discriminant_type = u8)]
/// enum Example {
///     Variant1 = 1,
///     Variant5 = 5,
/// }
/// ```
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// #[repr(u8)]
/// enum Example {
///     Variant1 = 1,
///     Variant5 = 5,
/// }
/// ```
///
/// ## `discriminant`
/// `#[bin_proto(discriminant = <value>)]`
/// - `<value>`: unique value of the discriminant's type
///
/// Specify the discriminant for a variant.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(discriminant_type = u8)]
/// enum Example {
///     #[bin_proto(discriminant = 1)]
///     Variant1,
///     Variant5 = 5,
/// }
/// ```
///
/// ## `other`
/// `#[bin_proto(other)]`
///
/// Decode the specified variant if the discriminant doesn't match any other variants. A
/// discriminant value can still be provided for the variant, and will be used when encoding.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(discriminant_type = u8)]
/// enum Example {
///     #[bin_proto(discriminant = 1)]
///     Variant1,
///     #[bin_proto(discriminant = 2, other)]
///     CatchAll,
/// }
/// ```
///
/// ## `bits`
/// `#[bin_proto(bits = <width>)]`
///
/// Determine width of field in bits.
///
/// **WARNING**: Bitfields disregard endianness and instead have the same endianness as the
/// underlying [`BitRead`] / [`BitWrite`] instance. If you're using bitfields, you almost always
/// want a big endian stream.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct Nibble(#[bin_proto(bits = 4)] u8);
/// ```
///
/// ## `untagged`
/// `#[bin_proto(untagged)]`
///
/// Variable-length field is final field in container, hence lacks a length prefix and should be
/// read until eof.
///
/// ```
/// # #[cfg(feature = "alloc")]
/// # {
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct ReadToEnd(#[bin_proto(untagged)] Vec<u8>);
/// # }
/// ```
///
/// ## `tag`
/// `#[bin_proto(tag = <expr>)]`
/// - `<expr>`: arbitrary expression. Fields in parent container can be used without prefixing them
///   with `self`.
///
/// Specify tag of field. The tag represents a length prefix for variable-length fields, and a
/// boolean for [`Option`].
///
/// ```
/// # #[cfg(feature = "alloc")]
/// # {
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct WithElementsLength {
///     count: u32,
///     foo: bool,
///     #[bin_proto(tag = count as usize)]
///     data: Vec<u32>,
/// }
/// # }
/// ```
///
/// ## `tag_type`
/// `#[bin_proto(tag_type = <type>[, tag_value = <expr>]?[, tag_bits = <expr>]?)]`
/// - `<type>`: tag's type
/// - `<expr>`: arbitrary expression. Fields in parent container should be prefixed with `self`.
///
/// Specify tag of field. The tag represents a length prefix for variable-length fields, and a
/// boolean for [`Option`]. The tag is placed directly before the field. The `tag_value` only has
/// to be specified when deriving [`BitEncode`].
///
/// ```
/// # #[cfg(feature = "alloc")]
/// # {
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct WithElementsLength {
///     #[bin_proto(tag_type = u16, tag_value = self.data.len() as u16, tag_bits = 13)]
///     data: Vec<u32>,
/// }
/// # }
/// ```
///
/// ## `write_value`
/// `#[bin_proto(write_value = <expr>)]`
/// - `<expr>`: An expression that can be coerced to the field type. Fields in parent container
///   should be prefixed with `self`.
///
/// Specify an expression that should be used as the field's value for writing.
///
/// ```
/// # #[cfg(feature = "alloc")]
/// # {
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct WithElementsLengthAuto {
///     #[bin_proto(write_value = self.data.len() as u32)]
///     count: u32,
///     foo: bool,
///     #[bin_proto(tag = count as usize)]
///     data: Vec<u32>,
/// }
/// # }
/// ```
///
/// ## `ctx`
/// `#[bin_proto(ctx = <type>)[, ctx_generics(<generic>[, <generic>]*)]?]`
/// - `<type>`: The type of the context. Either a concrete type, or one of the container's generics
/// - `<generic>`: Any generics used by the context type, with optional bounds. E.g.
///   `T: Copy` for a [`Vec<T>`](alloc::vec::Vec) context.
///
/// Specify the type of context that will be passed to codec functions.
///
/// ```
/// # #[cfg(feature = "alloc")]
/// # {
/// # use bin_proto::{BitDecode, BitEncode, BitEncodeExt};
/// struct Ctx;
///
/// struct NeedsCtx;
///
/// impl BitDecode<Ctx> for NeedsCtx {
///     fn decode<R, E>(
///         _read: &mut R,
///         _ctx: &mut Ctx,
///         _tag: (),
///     ) -> bin_proto::Result<Self>
///     where
///         R: bin_proto::BitRead,
///         E: bin_proto::Endianness,
///     {
///         // Use ctx here
///         Ok(Self)
///     }
/// }
///
/// impl BitEncode<Ctx> for NeedsCtx {
///     fn encode<W, E>(
///         &self,
///         _write: &mut W,
///         _ctx: &mut Ctx,
///         _tag: (),
///     ) -> bin_proto::Result<()>
///     where
///         W: bin_proto::BitWrite,
///         E: bin_proto::Endianness,
///     {
///         // Use ctx here
///         Ok(())
///     }
/// }
///
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(ctx = Ctx)]
/// struct WithCtx(NeedsCtx);
///
/// WithCtx(NeedsCtx)
///     .encode_bytes_ctx(bin_proto::BigEndian, &mut Ctx, ())
///     .unwrap();
/// # }
/// ```
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// # use std::marker::PhantomData;
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(ctx = Ctx)]
/// struct NestedCodec<Ctx, A: BitDecode<Ctx> + BitEncode<Ctx>>(A, PhantomData<Ctx>);
/// ```
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// struct Ctx<'a, T: Copy>(&'a T);
///
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(ctx = Ctx<'a, T>, ctx_generics('a, T: Copy))]
/// struct WithCtx;
/// ```
///
/// ## `ctx_bounds`
/// `#[bin_proto(ctx_bounds(<bound>[, <bound>]*)[, ctx_generics(<generic>[, <generic>]*)]?)]`
/// - `<bounds>`: Trait bounds that must be satisfied by the context
/// - `<generic>`: Any generics used by the context type. E.g. `'a` for a context with a
///   [`From<&'a i32>`](From) bound.
///
/// Specify the trait bounds of context that will be passed to codec functions.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// trait CtxTrait {};
///
/// struct NeedsCtx;
///
/// impl<Ctx: CtxTrait> BitDecode<Ctx> for NeedsCtx {
///     fn decode<R, E>(
///         _read: &mut R,
///         _ctx: &mut Ctx,
///         _tag: (),
///     ) -> bin_proto::Result<Self>
///     where
///         R: bin_proto::BitRead,
///         E: bin_proto::Endianness,
///     {
///         // Use ctx here
///         Ok(Self)
///     }
///}
///
/// impl<Ctx: CtxTrait> BitEncode<Ctx> for NeedsCtx {
///     fn encode<W, E>(
///         &self,
///         _write: &mut W,
///         _ctx: &mut Ctx,
///         _tag: (),
///     ) -> bin_proto::Result<()>
///     where
///         W: bin_proto::BitWrite,
///         E: bin_proto::Endianness,
///     {
///         // Use ctx here
///         Ok(())
///     }
/// }
///
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(ctx_bounds(CtxTrait))]
/// struct WithCtx(NeedsCtx);
/// ```
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(ctx_bounds(From<&'a i32>), ctx_generics('a))]
/// struct WithCtx;
/// ```
///
/// ## `skip_encode`
/// `#[bin_proto(skip_encode)]`
///
/// If applied to a field, skip the field when encoding. If applied to an enum variant, return an
/// Error if the variant is attempted to be encoded.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct Struct(#[bin_proto(skip_encode)] u8);
/// ```
///
/// ```
/// # use bin_proto::BitEncode;
/// #[derive(BitEncode)]
/// #[bin_proto(discriminant_type = u8)]
/// enum Enum {
///     #[bin_proto(skip_encode)]
///     Skip
/// }
/// ```
///
/// ## `skip_decode`
/// `#[bin_proto(skip_decode)]`
///
/// If applied to a field, use [`Default::default`] instead of attempting to read field. If applied
/// to an enum variant, don't generate code for decoding.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct Struct(#[bin_proto(skip_decode)] u8);
/// ```
///
/// ```
/// # use bin_proto::BitDecode;
/// #[derive(BitDecode)]
/// #[bin_proto(discriminant_type = u8)]
/// enum Enum {
///     #[bin_proto(skip_decode)]
///     Skip
/// }
/// ```
///
/// ## `skip`
/// `#[bin_proto(skip)]`
///
/// Equivalent to combining [`skip_encode`](#skip_encode) and [`skip_decode`](#skip_decode).
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct Struct(#[bin_proto(skip)] u8);
/// ```
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(discriminant_type = u8)]
/// enum Enum {
///     #[bin_proto(skip)]
///     Skip
/// }
/// ```
///
/// ## `pad_before`
/// `#[bin_proto(pad_before = <expr>)]`
///
/// Insert 0 bits when writing and skip bits when reading, prior to processing the field.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct Struct(#[bin_proto(pad_before = 3)] u8);
/// ```
///
/// ## `pad_after`
/// `#[bin_proto(pad_after = <expr>)]`
///
/// Insert 0 bits when writing and skip bits when reading, after processing the field.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct Struct(#[bin_proto(pad_after = 3)] u8);
/// ```
///
/// ## `magic`
/// `#[bin_proto(magic = <expr>)]`
/// - `<expr>`: Must evaluate to `&[u8; _]`
///
/// Indicates that the value must be present immediately preceding the field or struct.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(magic = &[0x01, 0x02, 0x03])]
/// struct Magic(#[bin_proto(magic = b"123")] u8);
/// ```
///
/// ## `crate`
/// `#[bin_proto(crate = <path>)]`
///
/// Specify the path to the `bin-proto` crate to be used in generated code. This is typically
/// only applicable when invoking re-exported derives from a public macro in a different crate.
///
/// ```
/// # use bin_proto::{BitDecode, BitEncode};
/// use bin_proto as renamed;
///
/// #[derive(BitDecode, BitEncode)]
/// #[bin_proto(crate = renamed)]
/// struct Struct;
/// ```
#[cfg(feature = "derive")]
pub use bin_proto_derive::{BitDecode, BitEncode};

#[macro_use]
mod codec;

mod discriminable;
mod error;
mod impls;
pub mod util;

pub extern crate bitstream_io;

/// A marker for [`BitEncode`] implementors that don't prepend their tag, and [`BitDecode`]
/// implementors that usually have a tag, but can be read to EOF
pub struct Untagged;

/// A marker for [`BitDecode`] implementors that require a tag.
pub struct Tag<T>(pub T);

/// A marker for [`BitDecode`] and [`BitEncode`] implementors that support bitfield operations.
pub struct Bits<const C: u32>;

/// ```compile_fail
/// # use bin_proto::{BitDecode, BitEncode};
/// #[derive(BitDecode, BitEncode)]
/// struct MutuallyExclusiveAttrs {
///     pub length: u8,
///     #[bin_proto(untagged)]
///     #[bin_proto(tag = length as usize)]
///     pub reason: alloc::string::String,
/// }
/// ```
#[cfg(all(feature = "derive", feature = "alloc", doctest))]
#[allow(unused)]
fn compile_fail_if_multiple_exclusive_attrs() {}
