bilge 0.4.0

Use bitsized types as if they were a feature of rust.
Documentation
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
#![no_std]

use core::fmt;

#[doc(no_inline)]
pub use arbitrary_int;
#[cfg(feature = "schemars")]
#[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
pub use bilge_impl::JsonSchemaBits;
#[doc(hidden)]
pub use bilge_impl::bitsize_internal;
pub use bilge_impl::{BinaryBits, DebugBits, DefaultBits, FromBits, TryFromBits, bitsize};
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub use bilge_impl::{DeserializeBits, SerializeBits};

/// used for `use bilge::prelude::*;`
pub mod prelude {
    #[rustfmt::skip]
    #[doc(no_inline)]
    pub use super::{
        bitsize, Bitsized,
        FromBits, TryFromBits, DebugBits, BinaryBits, DefaultBits,
        // we control the version, so this should not be a problem
        arbitrary_int::prelude::*,
    };
    #[cfg(feature = "schemars")]
    #[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
    pub use super::JsonSchemaBits;
    #[cfg(feature = "serde")]
    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
    pub use super::{DeserializeBits, SerializeBits};
}

/// Used by generated code to talk about a bitfield's backing integer.
pub trait Bitsized {
    /// The arbitrary_int type, used internally to 'generically' access its methods.
    type ArbitraryInt;
    /// The number of bits this type uses.
    const BITS: usize;
    /// The maximum value this type can hold.
    const MAX: Self::ArbitraryInt;
}

/// Internally used marker trait.
///
/// # Safety
///
/// Avoid implementing this for your types. Implementing this trait could break invariants.
#[doc(hidden)]
pub unsafe trait Filled: Bitsized {}
#[doc(hidden)]
unsafe impl<T> Filled for T where T: Bitsized + From<<T as Bitsized>::ArbitraryInt> {}

/// This is generated to statically validate that a type implements `FromBits`.
#[doc(hidden)]
pub const fn assume_filled<T: Filled>() {}

/// The error type used for `TryFromBits`.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BitsError;

impl fmt::Display for BitsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "unable to parse bit pattern")
    }
}

impl core::error::Error for BitsError {}

/// Internally used for generating the `Result::Err` type in `TryFrom`.
///
/// This is needed since we don't want users to be able to create `BitsError` right now.
/// We'll be able to turn `BitsError` into an enum later, or anything else really.
#[doc(hidden)]
pub const fn give_me_error() -> BitsError {
    BitsError
}

/// Only basing this on Integer did not work, as bool and others are not Integer.
/// We could remove the whole macro_rules thing if it worked, though.
/// Maybe there is some way to do this, I'm not deep into types.
/// Finding some way to combine Integer and Bitsized would be good as well.
impl<BaseType, const BITS: usize> Bitsized for arbitrary_int::UInt<BaseType, BITS>
where
    BaseType: arbitrary_int::traits::UnsignedInteger + arbitrary_int::traits::BuiltinInteger,
    arbitrary_int::UInt<BaseType, BITS>: arbitrary_int::traits::UnsignedInteger,
{
    type ArbitraryInt = Self;
    const BITS: usize = BITS;
    const MAX: Self::ArbitraryInt = <Self as arbitrary_int::traits::Integer>::MAX;
}

impl<BaseType, const BITS: usize> Bitsized for arbitrary_int::Int<BaseType, BITS>
where
    BaseType: arbitrary_int::traits::SignedInteger + arbitrary_int::traits::BuiltinInteger,
    arbitrary_int::Int<BaseType, BITS>: arbitrary_int::traits::SignedInteger,
{
    type ArbitraryInt = Self;
    const BITS: usize = BITS;
    const MAX: Self::ArbitraryInt = <Self as arbitrary_int::traits::Integer>::MAX;
}

macro_rules! bitsized_impl {
    ($(($name:ident, $bits:expr)),+) => {
        $(
            impl Bitsized for $name {
                type ArbitraryInt = Self;
                const BITS: usize = $bits;
                const MAX: Self::ArbitraryInt = <Self as arbitrary_int::traits::Integer>::MAX;
            }
        )+
    };
}
bitsized_impl!((u8, 8), (u16, 16), (u32, 32), (u64, 64), (u128, 128));
bitsized_impl!((i8, 8), (i16, 16), (i32, 32), (i64, 64), (i128, 128));

/// Handle bool as a u1
impl Bitsized for bool {
    type ArbitraryInt = arbitrary_int::u1;
    const BITS: usize = 1;
    const MAX: Self::ArbitraryInt = <arbitrary_int::u1 as arbitrary_int::traits::Integer>::MAX;
}