262 lines
7.2 KiB
Rust
262 lines
7.2 KiB
Rust
//! Conditional trait implementations for external libraries.
|
|
|
|
/*
|
|
How do I support a new external library?
|
|
|
|
Let's say we want to add support for `my_library`.
|
|
|
|
First, we create a module under `external`, like `serde` with any specialized code.
|
|
Ideally, any utilities in here should just work off the `Flags` trait and maybe a
|
|
few other assumed bounds.
|
|
|
|
Next, re-export the library from the `__private` module here.
|
|
|
|
Next, define a macro like so:
|
|
|
|
```rust
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(feature = "serde")]
|
|
macro_rules! __impl_external_bitflags_my_library {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {
|
|
// Implementation goes here
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(not(feature = "my_library"))]
|
|
macro_rules! __impl_external_bitflags_my_library {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {};
|
|
}
|
|
```
|
|
|
|
Note that the macro is actually defined twice; once for when the `my_library` feature
|
|
is available, and once for when it's not. This is because the `__impl_external_bitflags_my_library`
|
|
macro is called in an end-user's library, not in `bitflags`. In an end-user's library we don't
|
|
know whether or not a particular feature of `bitflags` is enabled, so we unconditionally call
|
|
the macro, where the body of that macro depends on the feature flag.
|
|
|
|
Now, we add our macro call to the `__impl_external_bitflags` macro body:
|
|
|
|
```rust
|
|
__impl_external_bitflags_my_library! {
|
|
$InternalBitFlags: $T, $PublicBitFlags {
|
|
$(
|
|
$(#[$inner $($args)*])*
|
|
const $Flag;
|
|
)*
|
|
}
|
|
}
|
|
```
|
|
*/
|
|
|
|
pub(crate) mod __private {
|
|
#[cfg(feature = "serde")]
|
|
pub use serde;
|
|
|
|
#[cfg(feature = "arbitrary")]
|
|
pub use arbitrary;
|
|
|
|
#[cfg(feature = "bytemuck")]
|
|
pub use bytemuck;
|
|
}
|
|
|
|
/// Implements traits from external libraries for the internal bitflags type.
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
macro_rules! __impl_external_bitflags {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {
|
|
// Any new library traits impls should be added here
|
|
// Use `serde` as an example: generate code when the feature is available,
|
|
// and a no-op when it isn't
|
|
|
|
$crate::__impl_external_bitflags_serde! {
|
|
$InternalBitFlags: $T, $PublicBitFlags {
|
|
$(
|
|
$(#[$inner $($args)*])*
|
|
const $Flag;
|
|
)*
|
|
}
|
|
}
|
|
|
|
$crate::__impl_external_bitflags_arbitrary! {
|
|
$InternalBitFlags: $T, $PublicBitFlags {
|
|
$(
|
|
$(#[$inner $($args)*])*
|
|
const $Flag;
|
|
)*
|
|
}
|
|
}
|
|
|
|
$crate::__impl_external_bitflags_bytemuck! {
|
|
$InternalBitFlags: $T, $PublicBitFlags {
|
|
$(
|
|
$(#[$inner $($args)*])*
|
|
const $Flag;
|
|
)*
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(feature = "serde")]
|
|
pub mod serde;
|
|
|
|
/// Implement `Serialize` and `Deserialize` for the internal bitflags type.
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(feature = "serde")]
|
|
macro_rules! __impl_external_bitflags_serde {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {
|
|
impl $crate::__private::serde::Serialize for $InternalBitFlags {
|
|
fn serialize<S: $crate::__private::serde::Serializer>(
|
|
&self,
|
|
serializer: S,
|
|
) -> $crate::__private::core::result::Result<S::Ok, S::Error> {
|
|
$crate::serde::serialize(
|
|
&$PublicBitFlags::from_bits_retain(self.bits()),
|
|
serializer,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<'de> $crate::__private::serde::Deserialize<'de> for $InternalBitFlags {
|
|
fn deserialize<D: $crate::__private::serde::Deserializer<'de>>(
|
|
deserializer: D,
|
|
) -> $crate::__private::core::result::Result<Self, D::Error> {
|
|
let flags: $PublicBitFlags = $crate::serde::deserialize(deserializer)?;
|
|
|
|
Ok(flags.0)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(not(feature = "serde"))]
|
|
macro_rules! __impl_external_bitflags_serde {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {};
|
|
}
|
|
|
|
#[cfg(feature = "arbitrary")]
|
|
pub mod arbitrary;
|
|
|
|
#[cfg(feature = "bytemuck")]
|
|
mod bytemuck;
|
|
|
|
/// Implement `Arbitrary` for the internal bitflags type.
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(feature = "arbitrary")]
|
|
macro_rules! __impl_external_bitflags_arbitrary {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {
|
|
impl<'a> $crate::__private::arbitrary::Arbitrary<'a> for $InternalBitFlags {
|
|
fn arbitrary(
|
|
u: &mut $crate::__private::arbitrary::Unstructured<'a>,
|
|
) -> $crate::__private::arbitrary::Result<Self> {
|
|
$crate::arbitrary::arbitrary::<$PublicBitFlags>(u).map(|flags| flags.0)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(not(feature = "arbitrary"))]
|
|
macro_rules! __impl_external_bitflags_arbitrary {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {};
|
|
}
|
|
|
|
/// Implement `Pod` and `Zeroable` for the internal bitflags type.
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(feature = "bytemuck")]
|
|
macro_rules! __impl_external_bitflags_bytemuck {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {
|
|
// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
|
|
// and $T implements Pod
|
|
unsafe impl $crate::__private::bytemuck::Pod for $InternalBitFlags where
|
|
$T: $crate::__private::bytemuck::Pod
|
|
{
|
|
}
|
|
|
|
// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
|
|
// and $T implements Zeroable
|
|
unsafe impl $crate::__private::bytemuck::Zeroable for $InternalBitFlags where
|
|
$T: $crate::__private::bytemuck::Zeroable
|
|
{
|
|
}
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
#[doc(hidden)]
|
|
#[cfg(not(feature = "bytemuck"))]
|
|
macro_rules! __impl_external_bitflags_bytemuck {
|
|
(
|
|
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
|
|
$(
|
|
$(#[$inner:ident $($args:tt)*])*
|
|
const $Flag:tt;
|
|
)*
|
|
}
|
|
) => {};
|
|
}
|