combine crates to workspace
This commit is contained in:
		
							
								
								
									
										14
									
								
								btrfs_parse_derive/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								btrfs_parse_derive/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "btrfs_parse_derive"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
proc-macro2 = "1.0.66"
 | 
			
		||||
quote = "1.0.32"
 | 
			
		||||
syn = "2.0.27"
 | 
			
		||||
 | 
			
		||||
[lib]
 | 
			
		||||
proc-macro = true
 | 
			
		||||
							
								
								
									
										184
									
								
								btrfs_parse_derive/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								btrfs_parse_derive/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,184 @@
 | 
			
		||||
extern crate proc_macro;
 | 
			
		||||
extern crate syn;
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate quote;
 | 
			
		||||
extern crate proc_macro2;
 | 
			
		||||
 | 
			
		||||
use proc_macro2::Span;
 | 
			
		||||
use proc_macro::TokenStream;
 | 
			
		||||
use syn::{DeriveInput, Data::Enum, parse_macro_input};
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(AllVariants)]
 | 
			
		||||
pub fn derive_all_variants(input: TokenStream) -> TokenStream {
 | 
			
		||||
	let syn_item: DeriveInput = parse_macro_input!(input);
 | 
			
		||||
 | 
			
		||||
	let variants = match syn_item.data {
 | 
			
		||||
		Enum(enum_item) => {
 | 
			
		||||
			enum_item.variants.into_iter().map(|v|v.ident)
 | 
			
		||||
		},
 | 
			
		||||
		_ => panic!("AllVariants only works on enums!"),
 | 
			
		||||
	};
 | 
			
		||||
	let enum_name = syn_item.ident;
 | 
			
		||||
 | 
			
		||||
	let expanded = quote! {
 | 
			
		||||
		impl #enum_name {
 | 
			
		||||
			fn all_variants() -> &'static[#enum_name] {
 | 
			
		||||
				&[ #(#enum_name::#variants),* ]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	expanded.into()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(ParseBin, attributes(skip_bytes, len))]
 | 
			
		||||
pub fn derive_parse_bin(input: TokenStream) -> TokenStream {
 | 
			
		||||
	let syn_item: DeriveInput = parse_macro_input!(input);
 | 
			
		||||
	let name = syn_item.ident;
 | 
			
		||||
 | 
			
		||||
	match syn_item.data {
 | 
			
		||||
		syn::Data::Struct(struct_item) => {
 | 
			
		||||
			match struct_item.fields {
 | 
			
		||||
				syn::Fields::Named(fields_named) => {
 | 
			
		||||
					return derive_parse_bin_struct(&name, &fields_named.named);
 | 
			
		||||
				},
 | 
			
		||||
				syn::Fields::Unnamed(fields_unnamed) => {
 | 
			
		||||
					if fields_unnamed.unnamed.len() != 1 {
 | 
			
		||||
						panic!("ParseBin does not support tuple structs!");
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					let inner_type = fields_unnamed.unnamed.into_iter().next().unwrap().ty;
 | 
			
		||||
					return derive_parse_bin_alias(name, inner_type);
 | 
			
		||||
				},
 | 
			
		||||
				_ => panic!("ParseBin on unit structs makes no sense!"),
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		_ => panic!("ParseBin only works on structs so far!"),
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn derive_parse_bin_alias(name: syn::Ident, ty: syn::Type) -> TokenStream {
 | 
			
		||||
	quote! {
 | 
			
		||||
		impl ParseBin for #name {
 | 
			
		||||
			fn parse_len(bytes: &[u8]) -> Result<(Self, usize), ParseError> {
 | 
			
		||||
				let (result, size) = <#ty>::parse_len(bytes)?;
 | 
			
		||||
				Ok((#name(result), size))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}.into()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn derive_parse_bin_struct<'a, T>(name: &syn::Ident, fields: T) -> TokenStream
 | 
			
		||||
	where T: IntoIterator<Item = &'a syn::Field>
 | 
			
		||||
{
 | 
			
		||||
	let mut parsing_statements = Vec::new();
 | 
			
		||||
	let mut combining_expressions = Vec::new();
 | 
			
		||||
 | 
			
		||||
	for field in fields {
 | 
			
		||||
		let field_name = field.ident.as_ref().unwrap();
 | 
			
		||||
		let field_type = &field.ty;
 | 
			
		||||
		let mut skip: Option<usize> = None;
 | 
			
		||||
		let mut veclen: Option<String> = None;
 | 
			
		||||
 | 
			
		||||
		// look for attributes
 | 
			
		||||
		for at in &field.attrs {
 | 
			
		||||
			if let syn::Meta::NameValue(nv) = &at.meta {
 | 
			
		||||
				if nv.path.segments.len() == 1 {
 | 
			
		||||
					let attr_name = nv.path.segments[0].ident.to_string();
 | 
			
		||||
					if attr_name == "skip_bytes" {
 | 
			
		||||
						if let syn::Expr::Lit(expr) = &nv.value {
 | 
			
		||||
							if let syn::Lit::Int(nbytes) = &expr.lit {
 | 
			
		||||
//								println!("reserved = {}", nbytes);
 | 
			
		||||
								skip = nbytes.base10_parse::<usize>().ok()
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					} else if attr_name == "len" {
 | 
			
		||||
						if let syn::Expr::Lit(expr) = &nv.value {
 | 
			
		||||
							if let syn::Lit::Str(litstr) = &expr.lit {
 | 
			
		||||
//								println!("len = {}", litstr.value());
 | 
			
		||||
								veclen = Some(litstr.value());
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if let Some(offset) = skip {
 | 
			
		||||
			parsing_statements.push(quote!{
 | 
			
		||||
				__parse_bin_derive_size += #offset;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if let Some(varname) = veclen {
 | 
			
		||||
			let field_name_item = format_ident!("{}_item", field_name);
 | 
			
		||||
 | 
			
		||||
			let field_type_item: &syn::Type = match &field_type {
 | 
			
		||||
				syn::Type::Path(tp) => {
 | 
			
		||||
					match tp.path.segments.iter().next() {
 | 
			
		||||
						Some(single) => {
 | 
			
		||||
							if &single.ident.to_string() == "Vec" {
 | 
			
		||||
								match &single.arguments {
 | 
			
		||||
									syn::PathArguments::AngleBracketed(args) => {
 | 
			
		||||
										match args.args.iter().next() {
 | 
			
		||||
											Some(firstarg) => {
 | 
			
		||||
												match firstarg {
 | 
			
		||||
													syn::GenericArgument::Type(ty) => {
 | 
			
		||||
														Some(ty)
 | 
			
		||||
													},
 | 
			
		||||
													_ => None,
 | 
			
		||||
												}
 | 
			
		||||
											},
 | 
			
		||||
											None => None,
 | 
			
		||||
										}
 | 
			
		||||
									},
 | 
			
		||||
									_ => None,
 | 
			
		||||
								}
 | 
			
		||||
							} else {
 | 
			
		||||
								None
 | 
			
		||||
							}
 | 
			
		||||
						},
 | 
			
		||||
						None => None,
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				_ => None,
 | 
			
		||||
			}.expect("The len attribute is only allowed on type Vec<_>");
 | 
			
		||||
 | 
			
		||||
			let varname_ident = syn::Ident::new(&varname, Span::call_site());
 | 
			
		||||
 | 
			
		||||
			parsing_statements.push(quote!{
 | 
			
		||||
				let mut #field_name = Vec::new();
 | 
			
		||||
				for i in 0 .. #varname_ident.0 as usize {
 | 
			
		||||
					let #field_name_item = <#field_type_item>::parse_len(&bytes[__parse_bin_derive_size..])?;
 | 
			
		||||
					__parse_bin_derive_size += #field_name_item.1;
 | 
			
		||||
					#field_name.push(#field_name_item.0);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			combining_expressions.push(quote!(#field_name: #field_name));
 | 
			
		||||
		} else {
 | 
			
		||||
			parsing_statements.push(quote!{
 | 
			
		||||
				let #field_name = <#field_type>::parse_len(&bytes[__parse_bin_derive_size..])?;
 | 
			
		||||
				__parse_bin_derive_size += #field_name.1;
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			combining_expressions.push(quote!(#field_name: #field_name.0));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	quote! {
 | 
			
		||||
		impl ParseBin for #name {
 | 
			
		||||
			fn parse_len(bytes: &[u8]) -> Result<(Self, usize), ParseError> {
 | 
			
		||||
				let mut __parse_bin_derive_size: usize = 0;
 | 
			
		||||
 | 
			
		||||
				#(#parsing_statements)*
 | 
			
		||||
 | 
			
		||||
				let result = #name {
 | 
			
		||||
					#(#combining_expressions),*
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				Ok((result, __parse_bin_derive_size))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}.into()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user