bytecheck_derive-0.8.0/.cargo_vcs_info.json0000644000000001560000000000100143420ustar { "git": { "sha1": "1fb8574401d92c74f1f39d0bba73299bd7b6fefc" }, "path_in_vcs": "bytecheck_derive" }bytecheck_derive-0.8.0/Cargo.toml0000644000000025520000000000100123420ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.81" name = "bytecheck_derive" version = "0.8.0" authors = ["David Koloski "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Derive macro for bytecheck" documentation = "https://docs.rs/bytecheck_derive" readme = "README.md" keywords = [ "no_std", "validation", "serialization", ] categories = [ "encoding", "no-std", "no-std::no-alloc", ] license = "MIT" repository = "https://github.com/rkyv/bytecheck" [lib] name = "bytecheck_derive" path = "src/lib.rs" proc-macro = true [dependencies.proc-macro2] version = "1" default-features = false [dependencies.quote] version = "1" default-features = false [dependencies.syn] version = "2" features = [ "clone-impls", "derive", "full", "parsing", "printing", "proc-macro", ] default-features = false bytecheck_derive-0.8.0/Cargo.toml.orig000064400000000000000000000012331046102023000160160ustar 00000000000000[package] name = "bytecheck_derive" description = "Derive macro for bytecheck" version.workspace = true authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true readme.workspace = true repository.workspace = true keywords.workspace = true categories.workspace = true documentation = "https://docs.rs/bytecheck_derive" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] proc-macro = true [dependencies] proc-macro2.workspace = true syn = { workspace = true, features = ["clone-impls", "derive", "full", "parsing", "printing", "proc-macro"] } quote.workspace = true bytecheck_derive-0.8.0/LICENSE000064400000000000000000000020351046102023000141350ustar 00000000000000Copyright 2020 David Koloski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bytecheck_derive-0.8.0/README.md000064400000000000000000000050311046102023000144060ustar 00000000000000# `bytecheck` [![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] [crates.io badge]: https://img.shields.io/crates/v/bytecheck.svg [crates.io]: https://crates.io/crates/bytecheck [docs badge]: https://img.shields.io/docsrs/bytecheck [docs]: https://docs.rs/bytecheck [license badge]: https://img.shields.io/badge/license-MIT-blue.svg [license]: https://github.com/rkyv/bytecheck/blob/master/LICENSE bytecheck is a memory validation framework for Rust. ## Documentation - [bytecheck](https://docs.rs/bytecheck), a memory validation framework for Rust - [bytecheck_derive](https://docs.rs/bytecheck_derive), the derive macro for bytecheck ## Example ```rust use bytecheck::{CheckBytes, check_bytes, rancor::Failure}; #[derive(CheckBytes, Debug)] #[repr(C)] struct Test { a: u32, b: char, c: bool, } #[repr(C, align(4))] struct Aligned([u8; N]); macro_rules! bytes { ($($byte:literal,)*) => { (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() }; ($($byte:literal),*) => { bytes!($($byte,)*) }; } // In this example, the architecture is assumed to be little-endian #[cfg(target_endian = "little")] unsafe { // These are valid bytes for a `Test` check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); // Changing the bytes for the u32 is OK, any bytes are a valid u32 check_bytes::( bytes![ 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); // Characters outside the valid ranges are invalid check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); // 0 is a valid boolean value (false) but 2 is not check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); } ``` bytecheck_derive-0.8.0/src/attributes.rs000064400000000000000000000065621046102023000164640ustar 00000000000000use quote::ToTokens; use syn::{ meta::ParseNestedMeta, parenthesized, parse::Parse, parse_quote, punctuated::Punctuated, AttrStyle, DeriveInput, Error, Field, Path, Token, WherePredicate, }; fn try_set_attribute( attribute: &mut Option, value: T, name: &'static str, ) -> Result<(), Error> { if attribute.is_none() { *attribute = Some(value); Ok(()) } else { Err(Error::new_spanned( value, format!("{name} already specified"), )) } } #[derive(Default)] pub struct Attributes { pub bounds: Option>, crate_path: Option, pub verify: Option, } impl Attributes { fn parse_check_bytes_attributes( &mut self, meta: ParseNestedMeta<'_>, ) -> Result<(), Error> { if meta.path.is_ident("bounds") { let bounds; parenthesized!(bounds in meta.input); let bounds = bounds.parse_terminated(WherePredicate::parse, Token![,])?; try_set_attribute(&mut self.bounds, bounds, "bounds") } else if meta.path.is_ident("crate") { if meta.input.parse::().is_ok() { let path = meta.input.parse::()?; try_set_attribute(&mut self.crate_path, path, "crate") } else if meta.input.is_empty() || meta.input.peek(Token![,]) { try_set_attribute( &mut self.crate_path, parse_quote! { crate }, "crate", ) } else { Err(meta.error("expected `crate` or `crate = ...`")) } } else if meta.path.is_ident("verify") { if !meta.input.is_empty() && !meta.input.peek(Token![,]) { return Err(meta.error("verify argument must be a path")); } try_set_attribute(&mut self.verify, meta.path, "verify") } else { Err(meta.error("unrecognized bytecheck argument")) } } pub fn parse(input: &DeriveInput) -> Result { let mut result = Self::default(); for attr in input.attrs.iter() { if !matches!(attr.style, AttrStyle::Outer) { continue; } if attr.path().is_ident("bytecheck") { attr.parse_nested_meta(|nested| { result.parse_check_bytes_attributes(nested) })?; } } Ok(result) } pub fn crate_path(&self) -> Path { self.crate_path .clone() .unwrap_or_else(|| parse_quote! { ::bytecheck }) } } #[derive(Default)] pub struct FieldAttributes { pub omit_bounds: Option, } impl FieldAttributes { fn parse_meta(&mut self, meta: ParseNestedMeta<'_>) -> Result<(), Error> { if meta.path.is_ident("omit_bounds") { self.omit_bounds = Some(meta.path); Ok(()) } else { Err(meta.error("unrecognized bytecheck arguments")) } } pub fn parse(input: &Field) -> Result { let mut result = Self::default(); for attr in input.attrs.iter() { if attr.path().is_ident("bytecheck") { attr.parse_nested_meta(|meta| result.parse_meta(meta))?; } } Ok(result) } } bytecheck_derive-0.8.0/src/lib.rs000064400000000000000000000500361046102023000150370ustar 00000000000000//! Procedural macros for bytecheck. #![deny( rust_2018_compatibility, rust_2018_idioms, future_incompatible, nonstandard_style, unused, clippy::all )] mod attributes; mod repr; mod util; use proc_macro2::TokenStream; use quote::quote; use syn::{ parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Error, Field, Fields, Ident, Index, Path, }; use crate::{ attributes::{Attributes, FieldAttributes}, repr::Repr, util::{iter_fields, strip_raw}, }; /// Derives `CheckBytes` for the labeled type. /// /// This derive macro automatically adds a type bound `field: CheckBytes<__C>` /// for each field type. This can cause an overflow while evaluating trait /// bounds if the structure eventually references its own type, as the /// implementation of `CheckBytes` for a struct depends on each field type /// implementing it as well. Adding the attribute `#[check_bytes(omit_bounds)]` /// to a field will suppress this trait bound and allow recursive structures. /// This may be too coarse for some types, in which case additional type bounds /// may be required with `bounds(...)`. /// /// # Attributes /// /// Additional arguments can be specified using attributes. /// /// `#[bytecheck(...)]` accepts the following attributes: /// /// ## Types only /// /// - `bounds(...)`: Adds additional bounds to the `CheckBytes` implementation. /// This can be especially useful when dealing with recursive structures, /// where bounds may need to be omitted to prevent recursive type definitions. /// In the context of the added bounds, `__C` is the name of the context /// generic (e.g. `__C: MyContext`). /// - `crate = ...`: Chooses an alternative crate path to import bytecheck from. /// - `verify`: Adds an additional verification step after the validity of each /// field has been checked. See the `Verify` trait for more information. /// /// ## Fields only /// /// - `omit_bounds`: Omits trait bounds for the annotated field in the generated /// impl. #[proc_macro_derive(CheckBytes, attributes(bytecheck))] pub fn check_bytes_derive( input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { match derive_check_bytes(parse_macro_input!(input as DeriveInput)) { Ok(result) => result.into(), Err(e) => e.to_compile_error().into(), } } fn derive_check_bytes(mut input: DeriveInput) -> Result { let attributes = Attributes::parse(&input)?; let crate_path = attributes.crate_path(); let name = &input.ident; let mut trait_generics = input.generics.clone(); // Split type generics for use later input.generics.make_where_clause(); let (type_impl_generics, type_ty_generics, type_where_clause) = input.generics.split_for_impl(); let type_where_clause = type_where_clause.unwrap(); // Trait generics are created by modifying the type generics. // We add a context parameter __C for the CheckBytes type parameter. trait_generics.params.push(parse_quote! { __C: #crate_path::rancor::Fallible + ?::core::marker::Sized }); // We add context error bounds to the where clause for the trait impl. let trait_where_clause = trait_generics.make_where_clause(); trait_where_clause.predicates.push(match &input.data { // Structs and unions just propagate any errors from checking their // fields, so the error type of the context just needs to be `Trace`. Data::Struct(_) | Data::Union(_) => parse_quote! { < __C as #crate_path::rancor::Fallible >::Error: #crate_path::rancor::Trace }, // Enums may error while checking the discriminant, so the error type of // the context needs to implement `Source` so we can create a new error // from an `InvalidEnumDiscriminantError`. Data::Enum(_) => parse_quote! { < __C as #crate_path::rancor::Fallible >::Error: #crate_path::rancor::Source }, }); // If the user specified any aditional bounds, we add them to the where // clause. if let Some(ref bounds) = attributes.bounds { for clause in bounds { trait_where_clause.predicates.push(clause.clone()); } } // If the user specified `verify`, then we need to bound `Self: Verify<__C>` // so we can call `Verify::verify`. let verify = if attributes.verify.is_some() { trait_where_clause.predicates.push(parse_quote!( #name #type_ty_generics: #crate_path::Verify<__C> )); Some(quote! { <#name #type_ty_generics as #crate_path::Verify<__C>>::verify( unsafe { &*value }, context, )?; }) } else { None }; let mut check_where = trait_where_clause.clone(); for field in iter_fields(&input.data) { let field_attrs = FieldAttributes::parse(field)?; if field_attrs.omit_bounds.is_none() { let ty = &field.ty; check_where.predicates.push(parse_quote! { #ty: #crate_path::CheckBytes<__C> }); } } // Split trait generics for use later let (trait_impl_generics, _, trait_where_clause) = trait_generics.split_for_impl(); let trait_where_clause = trait_where_clause.unwrap(); // Build CheckBytes impl let check_bytes_impl = match input.data { Data::Struct(ref data) => match data.fields { Fields::Named(ref fields) => { let field_checks = fields.named.iter().map(|f| { let field = &f.ident; let ty = &f.ty; quote! { <#ty as #crate_path::CheckBytes<__C>>::check_bytes( ::core::ptr::addr_of!((*value).#field), context ).map_err(|e| { < < __C as #crate_path::rancor::Fallible >::Error as #crate_path::rancor::Trace >::trace( e, #crate_path::StructCheckContext { struct_name: ::core::stringify!(#name), field_name: ::core::stringify!(#field), }, ) })?; } }); quote! { #[automatically_derived] // SAFETY: `check_bytes` only returns `Ok` if all of the // fields of the struct are valid. If all of the fields are // valid, then the overall struct is also valid. unsafe impl #trait_impl_generics #crate_path::CheckBytes<__C> for #name #type_ty_generics #check_where { unsafe fn check_bytes( value: *const Self, context: &mut __C, ) -> ::core::result::Result< (), <__C as #crate_path::rancor::Fallible>::Error, > { #(#field_checks)* #verify ::core::result::Result::Ok(()) } } } } Fields::Unnamed(ref fields) => { let field_checks = fields.unnamed.iter().enumerate().map(|(i, f)| { let ty = &f.ty; let index = Index::from(i); quote! { < #ty as #crate_path::CheckBytes<__C> >::check_bytes( ::core::ptr::addr_of!((*value).#index), context ).map_err(|e| { < < __C as #crate_path::rancor::Fallible >::Error as #crate_path::rancor::Trace >::trace( e, #crate_path::TupleStructCheckContext { tuple_struct_name: ::core::stringify!( #name ), field_index: #i, }, ) })?; } }); quote! { #[automatically_derived] // SAFETY: `check_bytes` only returns `Ok` if all of the // fields of the struct are valid. If all of the fields are // valid, then the overall struct is also valid. unsafe impl #trait_impl_generics #crate_path::CheckBytes<__C> for #name #type_ty_generics #check_where { unsafe fn check_bytes( value: *const Self, context: &mut __C, ) -> ::core::result::Result< (), <__C as #crate_path::rancor::Fallible>::Error, > { #(#field_checks)* #verify ::core::result::Result::Ok(()) } } } } Fields::Unit => { quote! { #[automatically_derived] // SAFETY: Unit structs are always valid since they have a // size of 0 and no invalid bit patterns. unsafe impl #trait_impl_generics #crate_path::CheckBytes<__C> for #name #type_ty_generics #trait_where_clause { unsafe fn check_bytes( value: *const Self, context: &mut __C, ) -> ::core::result::Result< (), <__C as #crate_path::rancor::Fallible>::Error, > { #verify ::core::result::Result::Ok(()) } } } } }, Data::Enum(ref data) => { let repr = Repr::from_attrs(&input.attrs)?; let primitive = match repr { Repr::Transparent => { return Err(Error::new_spanned( name, "enums cannot be repr(transparent)", )) } Repr::Primitive(i) => i, Repr::C { .. } => { return Err(Error::new_spanned( name, "repr(C) enums are not currently supported", )) } Repr::Rust { .. } => { return Err(Error::new_spanned( name, "enums implementing CheckBytes must have an explicit \ repr", )) } }; let tag_variant_defs = data.variants.iter().map(|v| { let variant = &v.ident; if let Some((_, expr)) = &v.discriminant { quote! { #variant = #expr } } else { quote! { #variant } } }); let discriminant_const_defs = data.variants.iter().map(|v| { let variant = &v.ident; quote! { #[allow(non_upper_case_globals)] const #variant: #primitive = Tag::#variant as #primitive; } }); let tag_variant_values = data.variants.iter().map(|v| { let name = &v.ident; quote! { Discriminant::#name } }); let variant_structs = data.variants.iter().map(|v| { let variant = &v.ident; let variant_name = Ident::new( &format!("Variant{}", strip_raw(variant)), v.span(), ); match v.fields { Fields::Named(ref fields) => { let fields = fields.named.iter().map(|f| { let name = &f.ident; let ty = &f.ty; quote! { #name: #ty } }); quote! { #[repr(C)] struct #variant_name #type_impl_generics #type_where_clause { __tag: Tag, #(#fields,)* __phantom: ::core::marker::PhantomData< #name #type_ty_generics >, } } } Fields::Unnamed(ref fields) => { let fields = fields.unnamed.iter().map(|f| { let ty = &f.ty; quote! { #ty } }); quote! { #[repr(C)] struct #variant_name #type_impl_generics ( Tag, #(#fields,)* ::core::marker::PhantomData< #name #type_ty_generics > ) #type_where_clause; } } Fields::Unit => quote! {}, } }); let check_arms = data.variants.iter().map(|v| { let variant = &v.ident; let variant_name = Ident::new( &format!("Variant{}", strip_raw(variant)), v.span(), ); match v.fields { Fields::Named(ref fields) => { let checks = fields.named.iter().map(|f| { check_arm_named_field(f, &crate_path, name, variant) }); quote! { { let value = value.cast::<#variant_name #type_ty_generics>(); #(#checks)* } } } Fields::Unnamed(ref fields) => { let checks = fields.unnamed.iter().enumerate().map(|(i, f)| { check_arm_unnamed_field( i, f, &crate_path, name, variant, ) }); quote! { { let value = value.cast::<#variant_name #type_ty_generics>(); #(#checks)* } } } Fields::Unit => quote! { (), }, } }); let no_matching_tag_arm = quote! { return ::core::result::Result::Err( < < __C as #crate_path::rancor::Fallible >::Error as #crate_path::rancor::Source >::new( #crate_path::InvalidEnumDiscriminantError { enum_name: ::core::stringify!(#name), invalid_discriminant: tag, } ) ) }; quote! { const _: () = { #[repr(#primitive)] enum Tag { #(#tag_variant_defs,)* } struct Discriminant; #[automatically_derived] impl Discriminant { #(#discriminant_const_defs)* } #(#variant_structs)* #[automatically_derived] // SAFETY: `check_bytes` only returns `Ok` if: // - The discriminant is valid for some variant of the enum, // and // - Each field of the variant struct is valid. // If the discriminant is valid and the fields of the // indicated variant struct are valid, then the overall enum // is valid. unsafe impl #trait_impl_generics #crate_path::CheckBytes<__C> for #name #type_ty_generics #check_where { unsafe fn check_bytes( value: *const Self, context: &mut __C, ) -> ::core::result::Result< (), <__C as #crate_path::rancor::Fallible>::Error, > { let tag = *value.cast::<#primitive>(); match tag { #(#tag_variant_values => #check_arms)* _ => #no_matching_tag_arm, } #verify ::core::result::Result::Ok(()) } } }; } } Data::Union(_) => { return Err(Error::new( input.span(), "CheckBytes cannot be derived for unions", )); } }; Ok(check_bytes_impl) } fn check_arm_named_field( f: &Field, crate_path: &Path, name: &Ident, variant: &Ident, ) -> TokenStream { let field_name = &f.ident; let ty = &f.ty; quote! { <#ty as #crate_path::CheckBytes<__C>>::check_bytes( ::core::ptr::addr_of!((*value).#field_name), context ).map_err(|e| { < < __C as #crate_path::rancor::Fallible >::Error as #crate_path::rancor::Trace >::trace( e, #crate_path::NamedEnumVariantCheckContext { enum_name: ::core::stringify!(#name), variant_name: ::core::stringify!(#variant), field_name: ::core::stringify!(#field_name), }, ) })?; } } fn check_arm_unnamed_field( i: usize, f: &Field, crate_path: &Path, name: &Ident, variant: &Ident, ) -> TokenStream { let ty = &f.ty; let index = Index::from(i + 1); quote! { <#ty as #crate_path::CheckBytes<__C>>::check_bytes( ::core::ptr::addr_of!((*value).#index), context ).map_err(|e| { < < __C as #crate_path::rancor::Fallible >::Error as #crate_path::rancor::Trace >::trace( e, #crate_path::UnnamedEnumVariantCheckContext { enum_name: ::core::stringify!(#name), variant_name: ::core::stringify!(#variant), field_index: #index, }, ) })?; } } bytecheck_derive-0.8.0/src/repr.rs000064400000000000000000000072561046102023000152470ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{parenthesized, token, Attribute, Error, Ident, LitInt}; #[derive(Clone, Copy)] pub enum Primitive { I8, I16, I32, I64, Isize, U8, U16, U32, U64, Usize, } impl Primitive { const ALL: [Self; 10] = [ Self::I8, Self::I16, Self::I32, Self::I64, Self::Isize, Self::U8, Self::U16, Self::U32, Self::U64, Self::Usize, ]; pub const fn as_str(&self) -> &'static str { match self { Self::I8 => "i8", Self::I16 => "i16", Self::I32 => "i32", Self::I64 => "i64", Self::Isize => "isize", Self::U8 => "u8", Self::U16 => "u16", Self::U32 => "u32", Self::U64 => "u64", Self::Usize => "usize", } } } impl ToTokens for Primitive { fn to_tokens(&self, tokens: &mut TokenStream) { let ident = Ident::new(self.as_str(), Span::call_site()); tokens.extend(quote! { #ident }); } } pub enum Modifier { Packed(#[allow(dead_code)] usize), Align(#[allow(dead_code)] usize), } pub enum Repr { Transparent, Primitive(Primitive), C { #[allow(dead_code)] primitive: Option, #[allow(dead_code)] modifier: Option, }, Rust { #[allow(dead_code)] modifier: Option, }, } impl Repr { pub fn from_attrs(attrs: &[Attribute]) -> Result { let mut c = false; let mut transparent = false; let mut primitive = None; let mut modifier = None; for attr in attrs.iter().filter(|a| a.meta.path().is_ident("repr")) { attr.parse_nested_meta(|meta| { if meta.path.is_ident("C") { c = true; Ok(()) } else if meta.path.is_ident("transparent") { transparent = true; Ok(()) } else if let Some(&p) = Primitive::ALL .iter() .find(|p| meta.path.is_ident(p.as_str())) { primitive = Some(p); Ok(()) } else if meta.path.is_ident("align") { let content; parenthesized!(content in meta.input); let lit = content.parse::()?; let n = lit.base10_parse()?; modifier = Some(Modifier::Align(n)); Ok(()) } else if meta.path.is_ident("packed") { if meta.input.peek(token::Paren) { let content; parenthesized!(content in meta.input); let lit = content.parse::()?; let n = lit.base10_parse()?; modifier = Some(Modifier::Packed(n)); } else { modifier = Some(Modifier::Packed(1)); } Ok(()) } else { Err(Error::new_spanned( meta.path, "unrecognized repr argument", )) } })?; } if c { Ok(Repr::C { primitive, modifier, }) } else if transparent { Ok(Repr::Transparent) } else if let Some(primitive) = primitive { Ok(Repr::Primitive(primitive)) } else { Ok(Repr::Rust { modifier }) } } } bytecheck_derive-0.8.0/src/util.rs000064400000000000000000000023701046102023000152440ustar 00000000000000use core::iter::FlatMap; use syn::{ punctuated::Iter, Data, DataEnum, DataStruct, DataUnion, Field, Ident, Variant, }; type VariantFieldsFn = fn(&Variant) -> Iter<'_, Field>; fn variant_fields(variant: &Variant) -> Iter<'_, Field> { variant.fields.iter() } pub enum FieldsIter<'a> { Struct(Iter<'a, Field>), Enum(FlatMap, Iter<'a, Field>, VariantFieldsFn>), } impl<'a> Iterator for FieldsIter<'a> { type Item = &'a Field; fn next(&mut self) -> Option { match self { Self::Struct(iter) => iter.next(), Self::Enum(iter) => iter.next(), } } } pub fn iter_fields(data: &Data) -> FieldsIter<'_> { match data { Data::Struct(DataStruct { fields, .. }) => { FieldsIter::Struct(fields.iter()) } Data::Enum(DataEnum { variants, .. }) => { FieldsIter::Enum(variants.iter().flat_map(variant_fields)) } Data::Union(DataUnion { fields, .. }) => { FieldsIter::Struct(fields.named.iter()) } } } pub fn strip_raw(ident: &Ident) -> String { let as_string = ident.to_string(); as_string .strip_prefix("r#") .map(ToString::to_string) .unwrap_or(as_string) }