feat: container tests

This commit is contained in:
davidon-top 2023-12-29 23:07:56 +01:00
parent 6dc79f7e29
commit 0b1486aeee
Signed by: DavidOnTop
GPG key ID: FAB914DDC2F180EB
3 changed files with 339 additions and 316 deletions

View file

@ -25,169 +25,169 @@ use proc_macro2_diagnostics::Diagnostic;
/// you can also call functions from BitFlag trait beacause the new struct implements deref and deref_mut to the underlying type /// you can also call functions from BitFlag trait beacause the new struct implements deref and deref_mut to the underlying type
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn bitflag(_attr: TokenStream, input: TokenStream) -> TokenStream { pub fn bitflag(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut diagnostics = Vec::new(); let mut diagnostics = Vec::new();
let structdef: syn::ItemStruct = syn::parse_macro_input!(input as syn::ItemStruct); let structdef: syn::ItemStruct = syn::parse_macro_input!(input as syn::ItemStruct);
// check if the struct has correct shape // check if the struct has correct shape
check_struct(&mut diagnostics, &structdef); check_struct(&mut diagnostics, &structdef);
let structname = structdef.ident; let structname = structdef.ident;
let structfields = match &structdef.fields { let structfields = match &structdef.fields {
syn::Fields::Named(f) => f syn::Fields::Named(f) => f
.named .named
.iter() .iter()
.map(|f| f.ident.clone().unwrap()) .map(|f| f.ident.clone().unwrap())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
_ => { _ => {
diagnostics.push( diagnostics.push(
syn::Error::new_spanned( syn::Error::new_spanned(
&structdef.fields, &structdef.fields,
"struct has incorrect shape, only works on struct with named fields", "struct has incorrect shape, only works on struct with named fields",
) )
.into(), .into(),
); );
Vec::new() Vec::new()
} }
}; };
let vis = structdef.vis.clone(); let vis = structdef.vis.clone();
let attrs = structdef.attrs.clone(); let attrs = structdef.attrs.clone();
// a unsigned int type of a size larger then the number of fields in the struct // a unsigned int type of a size larger then the number of fields in the struct
let u_type = { let u_type = {
let fields_len = structfields.len(); let fields_len = structfields.len();
match fields_len { match fields_len {
0..=8 => quote::quote! { u8 }, 0..=8 => quote::quote! { u8 },
9..=16 => quote::quote! { u16 }, 9..=16 => quote::quote! { u16 },
17..=32 => quote::quote! { u32 }, 17..=32 => quote::quote! { u32 },
33..=64 => quote::quote! { u64 }, 33..=64 => quote::quote! { u64 },
65..=128 => quote::quote! { u128 }, 65..=128 => quote::quote! { u128 },
_ => { _ => {
diagnostics.push( diagnostics.push(
syn::Error::new_spanned(structdef.fields, "struct has too many fields").into(), syn::Error::new_spanned(structdef.fields, "struct has too many fields").into(),
); );
quote::quote! { u128 } quote::quote! { u128 }
} }
} }
}; };
let newstruct = quote::quote! { let newstruct = quote::quote! {
#(#attrs)* #(#attrs)*
#vis struct #structname { #vis struct #structname {
value: #u_type value: #u_type
} }
}; };
// make functions for each field in the struct // make functions for each field in the struct
let mut functions = Vec::new(); let mut functions = Vec::new();
for (i, field) in structfields.iter().enumerate() { for (i, field) in structfields.iter().enumerate() {
let field = field.clone(); let field = field.clone();
let i = i as u8; let i = i as u8;
let set_ident = syn::Ident::new(&format!("set_{}", field), field.span()); let set_ident = syn::Ident::new(&format!("set_{}", field), field.span());
functions.push(quote::quote! { functions.push(quote::quote! {
pub fn #field(&self) -> bool { pub fn #field(&self) -> bool {
self.value.get_flag(#i) self.value.get_flag(#i)
} }
pub fn #set_ident(&mut self, value: bool) { pub fn #set_ident(&mut self, value: bool) {
self.value.set_flag(#i, value); self.value.set_flag(#i, value);
} }
}); });
} }
let impls = quote::quote! { let impls = quote::quote! {
impl #structname { impl #structname {
#(#functions)* #(#functions)*
pub fn new(val: #u_type) -> Self { pub fn new(val: #u_type) -> Self {
Self { Self {
value: val value: val
} }
} }
pub fn value(&self) -> #u_type { pub fn value(&self) -> #u_type {
self.value self.value
} }
} }
}; };
let deref_impl = quote::quote! { let deref_impl = quote::quote! {
impl std::ops::Deref for #structname { impl std::ops::Deref for #structname {
type Target = #u_type; type Target = #u_type;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.value &self.value
} }
} }
}; };
let deref_mut_impl = quote::quote! { let deref_mut_impl = quote::quote! {
impl std::ops::DerefMut for #structname { impl std::ops::DerefMut for #structname {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value &mut self.value
} }
} }
}; };
let diagnostics = diagnostics.iter().map(|d| d.clone().emit_as_item_tokens()); let diagnostics = diagnostics.iter().map(|d| d.clone().emit_as_item_tokens());
quote::quote! { quote::quote! {
#(#diagnostics)* #(#diagnostics)*
#newstruct #newstruct
#impls #impls
#deref_impl #deref_impl
#deref_mut_impl #deref_mut_impl
} }
.into() .into()
} }
/// returns true if the struct has a correct shape /// returns true if the struct has a correct shape
fn check_struct(diagnostics: &mut Vec<Diagnostic>, input: &syn::ItemStruct) { fn check_struct(diagnostics: &mut Vec<Diagnostic>, input: &syn::ItemStruct) {
if input.generics.lt_token.is_some() { if input.generics.lt_token.is_some() {
diagnostics.push( diagnostics.push(
syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs") syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs")
.into(), .into(),
); );
} }
if input.generics.gt_token.is_some() { if input.generics.gt_token.is_some() {
diagnostics.push( diagnostics.push(
syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs") syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs")
.into(), .into(),
); );
} }
if input.generics.where_clause.is_some() { if input.generics.where_clause.is_some() {
diagnostics.push( diagnostics.push(
syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs") syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs")
.into(), .into(),
); );
} }
if !input.generics.params.is_empty() { if !input.generics.params.is_empty() {
diagnostics.push( diagnostics.push(
syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs") syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs")
.into(), .into(),
); );
} }
match &input.fields { match &input.fields {
syn::Fields::Unnamed(f) => diagnostics.push( syn::Fields::Unnamed(f) => diagnostics.push(
syn::Error::new_spanned(f, "struct has incorrect shape, found tuple struct").into(), syn::Error::new_spanned(f, "struct has incorrect shape, found tuple struct").into(),
), ),
syn::Fields::Unit => diagnostics.push( syn::Fields::Unit => diagnostics.push(
syn::Error::new_spanned(input, "struct has incorrect shape, found unit struct").into(), syn::Error::new_spanned(input, "struct has incorrect shape, found unit struct").into(),
), ),
syn::Fields::Named(f) => { syn::Fields::Named(f) => {
if f.named.len() > 128 { if f.named.len() > 128 {
diagnostics.push(syn::Error::new_spanned(f, "struct has too many fields").into()); diagnostics.push(syn::Error::new_spanned(f, "struct has too many fields").into());
} }
for field in f.named.iter() { for field in f.named.iter() {
match &field.ty { match &field.ty {
syn::Type::Path(path_type) if path_type.path.is_ident("bool") => {} syn::Type::Path(path_type) if path_type.path.is_ident("bool") => {}
_ => diagnostics.push( _ => diagnostics.push(
syn::Error::new_spanned( syn::Error::new_spanned(
field, field,
"struct has incorrect shape, found non bool field", "struct has incorrect shape, found non bool field",
) )
.into(), .into(),
), ),
} }
} }
} }
} }
} }

View file

@ -3,185 +3,187 @@
This crate aims to make working with binary/bit flags easier. It also provides a macro to add similar functionality to zig's packed structs with boolean fields. This crate aims to make working with binary/bit flags easier. It also provides a macro to add similar functionality to zig's packed structs with boolean fields.
*/ */
use std::ops::{Deref, DerefMut};
pub use binf_macros::*; pub use binf_macros::*;
/// A trait for types that can be used as bit flags. /// A trait for types that can be used as bit flags.
pub trait BitFlag { pub trait BitFlag {
type T; type T;
/// Sets the flag at the given position to the given value. /// Sets the flag at the given position to the given value.
/// I don't know what happens if the position is larger than the number of bits in the type. /// I don't know what happens if the position is larger than the number of bits in the type.
fn set_flag(&mut self, position: u8, value: bool); fn set_flag(&mut self, position: u8, value: bool);
/// Returns the value of the flag at the given position. /// Returns the value of the flag at the given position.
/// I don't know what happens if the position is larger than the number of bits in the type. /// I don't know what happens if the position is larger than the number of bits in the type.
fn get_flag(&self, position: u8) -> bool; fn get_flag(&self, position: u8) -> bool;
/// Returns an array of bools representing the flags. /// Returns an array of bools representing the flags.
/// The first element in the array is the flag at position 0. /// The first element in the array is the flag at position 0.
fn flags(&self) -> Self::T; fn flags(&self) -> Self::T;
/// Sets the flags to the given values. /// Sets the flags to the given values.
/// flags can be any size, but if it is larger than the number of bits in the type only the first bits will be used. /// flags can be any size, but if it is larger than the number of bits in the type only the first bits will be used.
/// So if this is u8 flags should be 8 or less any more are ignored. /// So if this is u8 flags should be 8 or less any more are ignored.
fn set_flags(&mut self, flags: &[bool]); fn set_flags(&mut self, flags: &[bool]);
} }
/// implementation for u8. u8 means it can store 8 flags 1 flag per bit. /// implementation for u8. u8 means it can store 8 flags 1 flag per bit.
impl BitFlag for u8 { impl BitFlag for u8 {
type T = [bool; 8]; type T = [bool; 8];
fn set_flag(&mut self, position: u8, value: bool) { fn set_flag(&mut self, position: u8, value: bool) {
if value { if value {
*self |= 1 << position; *self |= 1 << position;
} else { } else {
*self &= !(1 << position); *self &= !(1 << position);
} }
} }
fn get_flag(&self, position: u8) -> bool { fn get_flag(&self, position: u8) -> bool {
(*self & (1 << position)) != 0 (*self & (1 << position)) != 0
} }
fn flags(&self) -> Self::T { fn flags(&self) -> Self::T {
let mut flags = [false; 8]; let mut flags = [false; 8];
for (i, flag) in flags.iter_mut().enumerate() { for (i, flag) in flags.iter_mut().enumerate() {
*flag = self.get_flag(i as u8); *flag = self.get_flag(i as u8);
} }
flags flags
} }
fn set_flags(&mut self, flags: &[bool]) { fn set_flags(&mut self, flags: &[bool]) {
for (i, v) in flags.iter().enumerate() { for (i, v) in flags.iter().enumerate() {
if i >= 8 { if i >= 8 {
break; break;
} }
self.set_flag(i as u8, *v); self.set_flag(i as u8, *v);
} }
} }
} }
impl BitFlag for u16 { impl BitFlag for u16 {
type T = [bool; 16]; type T = [bool; 16];
fn set_flag(&mut self, position: u8, value: bool) { fn set_flag(&mut self, position: u8, value: bool) {
if value { if value {
*self |= 1 << position; *self |= 1 << position;
} else { } else {
*self &= !(1 << position); *self &= !(1 << position);
} }
} }
fn get_flag(&self, position: u8) -> bool { fn get_flag(&self, position: u8) -> bool {
(*self & (1 << position)) != 0 (*self & (1 << position)) != 0
} }
fn flags(&self) -> Self::T { fn flags(&self) -> Self::T {
let mut flags = [false; 16]; let mut flags = [false; 16];
for (i, flag) in flags.iter_mut().enumerate() { for (i, flag) in flags.iter_mut().enumerate() {
*flag = self.get_flag(i as u8); *flag = self.get_flag(i as u8);
} }
flags flags
} }
fn set_flags(&mut self, flags: &[bool]) { fn set_flags(&mut self, flags: &[bool]) {
for (i, v) in flags.iter().enumerate() { for (i, v) in flags.iter().enumerate() {
if i >= 16 { if i >= 16 {
break; break;
} }
self.set_flag(i as u8, *v); self.set_flag(i as u8, *v);
} }
} }
} }
impl BitFlag for u32 { impl BitFlag for u32 {
type T = [bool; 32]; type T = [bool; 32];
fn set_flag(&mut self, position: u8, value: bool) { fn set_flag(&mut self, position: u8, value: bool) {
if value { if value {
*self |= 1 << position; *self |= 1 << position;
} else { } else {
*self &= !(1 << position); *self &= !(1 << position);
} }
} }
fn get_flag(&self, position: u8) -> bool { fn get_flag(&self, position: u8) -> bool {
(*self & (1 << position)) != 0 (*self & (1 << position)) != 0
} }
fn flags(&self) -> Self::T { fn flags(&self) -> Self::T {
let mut flags = [false; 32]; let mut flags = [false; 32];
for (i, flag) in flags.iter_mut().enumerate() { for (i, flag) in flags.iter_mut().enumerate() {
*flag = self.get_flag(i as u8); *flag = self.get_flag(i as u8);
} }
flags flags
} }
fn set_flags(&mut self, flags: &[bool]) { fn set_flags(&mut self, flags: &[bool]) {
for (i, v) in flags.iter().enumerate() { for (i, v) in flags.iter().enumerate() {
if i >= 32 { if i >= 32 {
break; break;
} }
self.set_flag(i as u8, *v); self.set_flag(i as u8, *v);
} }
} }
} }
impl BitFlag for u64 { impl BitFlag for u64 {
type T = [bool; 64]; type T = [bool; 64];
fn set_flag(&mut self, position: u8, value: bool) { fn set_flag(&mut self, position: u8, value: bool) {
if value { if value {
*self |= 1 << position; *self |= 1 << position;
} else { } else {
*self &= !(1 << position); *self &= !(1 << position);
} }
} }
fn get_flag(&self, position: u8) -> bool { fn get_flag(&self, position: u8) -> bool {
(*self & (1 << position)) != 0 (*self & (1 << position)) != 0
} }
fn flags(&self) -> Self::T { fn flags(&self) -> Self::T {
let mut flags = [false; 64]; let mut flags = [false; 64];
for (i, flag) in flags.iter_mut().enumerate() { for (i, flag) in flags.iter_mut().enumerate() {
*flag = self.get_flag(i as u8); *flag = self.get_flag(i as u8);
} }
flags flags
} }
fn set_flags(&mut self, flags: &[bool]) { fn set_flags(&mut self, flags: &[bool]) {
for (i, v) in flags.iter().enumerate() { for (i, v) in flags.iter().enumerate() {
if i >= 64 { if i >= 64 {
break; break;
} }
self.set_flag(i as u8, *v); self.set_flag(i as u8, *v);
} }
} }
} }
impl BitFlag for u128 { impl BitFlag for u128 {
type T = [bool; 128]; type T = [bool; 128];
fn set_flag(&mut self, position: u8, value: bool) { fn set_flag(&mut self, position: u8, value: bool) {
if value { if value {
*self |= 1 << position; *self |= 1 << position;
} else { } else {
*self &= !(1 << position); *self &= !(1 << position);
} }
} }
fn get_flag(&self, position: u8) -> bool { fn get_flag(&self, position: u8) -> bool {
(*self & (1 << position)) != 0 (*self & (1 << position)) != 0
} }
fn flags(&self) -> Self::T { fn flags(&self) -> Self::T {
let mut flags = [false; 128]; let mut flags = [false; 128];
for (i, flag) in flags.iter_mut().enumerate() { for (i, flag) in flags.iter_mut().enumerate() {
*flag = self.get_flag(i as u8); *flag = self.get_flag(i as u8);
} }
flags flags
} }
fn set_flags(&mut self, flags: &[bool]) { fn set_flags(&mut self, flags: &[bool]) {
for (i, v) in flags.iter().enumerate() { for (i, v) in flags.iter().enumerate() {
if i >= 128 { if i >= 128 {
break; break;
} }
self.set_flag(i as u8, *v); self.set_flag(i as u8, *v);
} }
} }
} }
/// A wrapper around a type that implements BitFlag. In case you don't want to import the trait and /// A wrapper around a type that implements BitFlag. In case you don't want to import the trait and
@ -189,32 +191,46 @@ impl BitFlag for u128 {
pub struct BitFlags<T>(T); pub struct BitFlags<T>(T);
impl<T: BitFlag> BitFlags<T> { impl<T: BitFlag> BitFlags<T> {
pub fn new(value: T) -> Self { pub fn new(value: T) -> Self {
Self(value) Self(value)
} }
/// Sets the flag at the given position to the given value. /// Sets the flag at the given position to the given value.
/// I don't know what happens if the position is larger than the number of bits in the type. /// I don't know what happens if the position is larger than the number of bits in the type.
pub fn set_flag(&mut self, position: u8, value: bool) { pub fn set_flag(&mut self, position: u8, value: bool) {
self.0.set_flag(position, value); self.0.set_flag(position, value);
} }
/// Returns the value of the flag at the given position. /// Returns the value of the flag at the given position.
/// I don't know what happens if the position is larger than the number of bits in the type. /// I don't know what happens if the position is larger than the number of bits in the type.
pub fn get_flag(&self, position: u8) -> bool { pub fn get_flag(&self, position: u8) -> bool {
self.0.get_flag(position) self.0.get_flag(position)
} }
/// Returns an array of bools representing the flags. /// Returns an array of bools representing the flags.
/// The first element in the array is the flag at position 0. /// The first element in the array is the flag at position 0.
pub fn flags(&self) -> T::T { pub fn flags(&self) -> T::T {
self.0.flags() self.0.flags()
} }
/// Sets the flags to the given values. /// Sets the flags to the given values.
/// flags can be any size, but if it is larger than the number of bits in the type only the first bits will be used. /// flags can be any size, but if it is larger than the number of bits in the type only the first bits will be used.
/// So if this is u8 flags should be 8 or less any more are ignored. /// So if this is u8 flags should be 8 or less any more are ignored.
pub fn set_flags(&mut self, flags: &[bool]) { pub fn set_flags(&mut self, flags: &[bool]) {
self.0.set_flags(flags); self.0.set_flags(flags);
} }
}
impl<T: BitFlag> Deref for BitFlags<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: BitFlag> DerefMut for BitFlags<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
} }

7
tests/container.rs Normal file
View file

@ -0,0 +1,7 @@
use binf::BitFlags;
#[test]
fn standalone() {
let flags = BitFlags::<u8>::new(0b10101010);
assert_eq!(*flags, 0b10101010);
}