Skip to content

Commit

Permalink
add support for Display and FromStr
Browse files Browse the repository at this point in the history
  • Loading branch information
KodrAus committed Feb 5, 2025
1 parent 8b3cff9 commit 046c3ee
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 8 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ extern crate bitflags;
extern crate bitflags_derive;

bitflags! {
#[derive(FlagsDebug)]
struct Flags: u8 {
#[derive(FlagsDisplay, FlagsFromStr)]
struct MyFlags: u8 {
const A = 1;
const B = 1 << 1;
const C = 1 << 2;
}
}

// The regular `#[derive(Debug)]` would produce "Flags(A | B)" here
assert_eq!("A | B", format!("{:?}", Flags::A | Flags::B));
let flags = "A | B".parse::<MyFlags>()?;

assert_eq!("A | B", flags.to_string());
```

See [the docs](https://docs.rs/bitflags-derive) for details on all supported attributes.
14 changes: 14 additions & 0 deletions macros/src/display.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use proc_macro2::TokenStream;

pub(crate) fn expand(item: syn::DeriveInput) -> Result<TokenStream, syn::Error> {
let ident = item.ident;
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

Ok(
quote!(impl #impl_generics bitflags_derive::__private::core::fmt::Display for #ident #ty_generics #where_clause {
fn fmt(&self, f: &mut bitflags_derive::__private::core::fmt::Formatter) -> bitflags_derive::__private::core::fmt::Result {
bitflags_derive::__private::bitflags::parser::to_writer(self, f)
}
}),
)
}
16 changes: 16 additions & 0 deletions macros/src/from_str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use proc_macro2::TokenStream;

pub(crate) fn expand(item: syn::DeriveInput) -> Result<TokenStream, syn::Error> {
let ident = item.ident;
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

Ok(
quote!(impl #impl_generics bitflags_derive::__private::core::str::FromStr for #ident #ty_generics #where_clause {
type Err = bitflags_derive::__private::bitflags::parser::ParseError;

fn from_str(v: &str) -> bitflags_derive::__private::core::result::Result<Self, bitflags_derive::__private::bitflags::parser::ParseError> {
bitflags_derive::__private::bitflags::parser::from_str(v)
}
}),
)
}
26 changes: 25 additions & 1 deletion macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ extern crate proc_macro;
extern crate quote;

mod debug;
mod display;
mod from_str;

/**
Derive [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) for a flags type.
Derive [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html).
This macro will use [`to_writer`](https://docs.rs/bitflags/latest/bitflags/parser/fn.to_writer.html) to
format flags values.
Expand All @@ -24,6 +26,28 @@ pub fn derive_bitflags_debug(item: proc_macro::TokenStream) -> proc_macro::Token
debug::expand(syn::parse_macro_input!(item as syn::DeriveInput)).unwrap_or_compile_error()
}

/**
Derive [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html).
This macro will use [`to_writer`](https://docs.rs/bitflags/latest/bitflags/parser/fn.to_writer.html) to
format flags values.
*/
#[proc_macro_derive(FlagsDisplay)]
pub fn derive_bitflags_display(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
display::expand(syn::parse_macro_input!(item as syn::DeriveInput)).unwrap_or_compile_error()
}

/**
Derive [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html).
This macro will use [`from_str`](https://docs.rs/bitflags/latest/bitflags/parser/fn.from_str.html) to
parse flags values.
*/
#[proc_macro_derive(FlagsFromStr)]
pub fn derive_bitflags_from_str(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
from_str::expand(syn::parse_macro_input!(item as syn::DeriveInput)).unwrap_or_compile_error()
}

trait ResultExt {
fn unwrap_or_compile_error(self) -> proc_macro::TokenStream;
}
Expand Down
15 changes: 12 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,27 @@ extern crate bitflags;
#[macro_use]
extern crate bitflags_derive;
# fn main() -> Result<(), bitflags::parser::ParseError> {
bitflags! {
#[derive(FlagsDebug)]
struct Flags: u8 {
#[derive(FlagsDisplay, FlagsFromStr)]
struct MyFlags: u8 {
const A = 1;
const B = 1 << 1;
const C = 1 << 2;
}
}
# fn main() {}
let flags = "A | B".parse::<MyFlags>()?;
assert_eq!("A | B", flags.to_string());
# Ok(())
# }
```
These derives work for any type that implements the [`Flags`](https://docs.rs/bitflags/latest/bitflags/trait.Flags.html) trait.
*/

#![no_std]
#![deny(missing_docs)]

#[doc(inline)]
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/src/display.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#[test]
fn derive_display() {
bitflags! {
#[derive(FlagsDisplay)]
struct Flags: u8 {
const A = 1;
const B = 1 << 1;
const C = 1 << 2;
}
}

assert_eq!("A | B", (Flags::A | Flags::B).to_string());
}
13 changes: 13 additions & 0 deletions tests/ui/src/from_str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#[test]
fn derive_from_str() {
bitflags! {
#[derive(FlagsFromStr, PartialEq, Eq, Debug)]
struct Flags: u8 {
const A = 1;
const B = 1 << 1;
const C = 1 << 2;
}
}

assert_eq!("A | B".parse::<Flags>().unwrap(), Flags::A | Flags::B);
}
2 changes: 2 additions & 0 deletions tests/ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ extern crate bitflags;
extern crate bitflags_derive;

mod debug;
mod display;
mod from_str;

0 comments on commit 046c3ee

Please sign in to comment.