struct_field_offsets/lib.rs
1//! This crate provides a `field_offsets` member over `struct` declaration
2//! that adorns the `FieldOffsets` macro.
3
4//! For example, this can be used in FFI scenarios where asserting the offset
5//! of each field among the various languages struct becomes a concern.
6
7//! ```rust
8//! use struct_field_offsets::FieldOffsets;
9//!
10//! // at declaration site
11//! #[derive(FieldOffsets)]
12//! #[repr(C)]
13//! struct Data {
14//! x: i32,
15//! y: i32,
16//! label: [u8;8]
17//! }
18//!
19//! // in the code
20//! let offsets = Data::field_offsets();
21//! for (name,offset) in offsets {
22//! println!("field {name} offset is {offset}.");
23//! }
24//! // prints:
25//! // > field x offset is 0.
26//! // > field y offset is 4.
27//! // > field label offset is 8.
28//! ```
29//!
30//! In your Cargo.toml:
31//! ```toml
32//! [dependencies]
33//! struct-field-offsets = "*"
34//! ```
35//!
36extern crate proc_macro;
37use proc_macro::TokenStream;
38use quote::quote;
39use syn::{parse_macro_input, DeriveInput, Data, Fields, Ident};
40
41/// ```rust
42/// use struct_field_offsets::FieldOffsets;
43///
44/// // at declaration site
45/// #[derive(FieldOffsets)]
46/// #[repr(C)]
47/// struct Data {
48/// x: i32,
49/// y: i32,
50/// label: [u8;8]
51/// }
52///
53/// // in the code
54/// let offsets = Data::field_offsets();
55/// for (name,offset) in offsets {
56/// println!("field {name} offset is {offset}.");
57/// }
58/// // prints:
59/// // > field x offset is 0.
60/// // > field y offset is 4.
61/// // > field label offset is 8.
62#[proc_macro_derive(FieldOffsets)]
63pub fn field_offsets_derive(input: TokenStream) -> TokenStream {
64 // Parse the input tokens into a syntax tree
65 let input = parse_macro_input!(input as DeriveInput);
66 let name = input.ident;
67 // Extract the fields from the struct
68 let fields: Vec<Ident> = match input.data {
69 Data::Struct(data) => match data.fields {
70 Fields::Named(fields_named) => {
71 fields_named.named.iter().map(|f| f.ident.clone().unwrap()).collect()
72 }
73 _ => panic!("FieldOffsets can only be used with structs with named fields"),
74 },
75 Data::Enum(_) => panic!("FieldOffsets can only be used with structs"),
76 Data::Union(_) => panic!("FieldOffsets can only be used with structs"),
77 };
78 let offsets = fields.iter().map(|field| {
79 quote! {
80 (stringify!(#field), std::mem::offset_of!(#name, #field))
81 }
82 });
83 let len = offsets.len();
84 let expanded = quote! {
85 impl #name {
86 pub fn field_offsets() -> [(&'static str, usize); #len] {
87 [
88 #(#offsets),*
89 ]
90 }
91 }
92 };
93 TokenStream::from(expanded)
94}