parsely_impl/
lib.rs

1mod code_gen;
2pub mod error;
3mod model_types;
4pub mod parsely_read;
5pub mod parsely_write;
6mod syn_helpers;
7
8pub use bits_io::{
9    buf::bit_buf::BitBuf,
10    buf::bit_buf_exts::BitBufExts,
11    buf::bit_buf_mut::BitBufMut,
12    buf::bit_buf_mut_exts::BitBufMutExts,
13    buf::bits::Bits,
14    buf::bits_mut::BitsMut,
15    buf::byte_order::{BigEndian, ByteOrder, LittleEndian, NetworkOrder},
16    io::{bit_cursor::BitCursor, bit_read::BitRead, bit_write::BitWrite},
17};
18
19pub mod nsw_types {
20    pub use bits_io::nsw_types::*;
21}
22
23pub mod anyhow {
24    pub use anyhow::*;
25}
26
27use code_gen::{gen_read::generate_parsely_read_impl, gen_write::generate_parsely_write_impl};
28use darling::{ast, FromDeriveInput, FromField, FromMeta};
29use model_types::{Assertion, Context, ExprOrFunc, MapExpr, TypedFnArgList};
30use proc_macro2::TokenStream;
31use syn::DeriveInput;
32use syn_helpers::TypeExts;
33
34#[doc(hidden)]
35pub fn derive_parsely_read(item: TokenStream) -> std::result::Result<TokenStream, syn::Error> {
36    let ast: DeriveInput = syn::parse2(item)?;
37    let data = ParselyReadData::from_derive_input(&ast)?;
38    // eprintln!("parsely_read data = {data:#?}");
39    // eprintln!("HELLO, WORLD, item = {ast:#?}");
40
41    Ok(generate_parsely_read_impl(data))
42}
43
44#[doc(hidden)]
45pub fn derive_parsely_write(item: TokenStream) -> std::result::Result<TokenStream, syn::Error> {
46    let ast: DeriveInput = syn::parse2(item)?;
47    let data = ParselyWriteData::from_derive_input(&ast)?;
48    // eprintln!("parsely_write data = {data:#?}");
49    // eprintln!("HELLO, WORLD, item = {ast:#?}");
50
51    Ok(generate_parsely_write_impl(data))
52}
53
54#[derive(Debug, FromField, FromMeta)]
55pub struct ParselyCommonFieldData {
56    // Note: 'magic' fields (ident, ty, etc.) don't work with 'flatten' so can't be held here.
57    // See https://github.com/TedDriggs/darling/issues/330
58
59    // generics: Option<syn::Ident>,
60    assertion: Option<Assertion>,
61
62    /// Values that need to be passed as context to this fields read or write method
63    context: Option<Context>,
64
65    /// An optional mapping that will be applied to the read value
66    map: Option<MapExpr>,
67
68    /// An optional indicator that this field is or needs to be aligned to the given byte alignment
69    /// via padding.
70    alignment: Option<usize>,
71}
72
73#[derive(Debug, FromField)]
74#[darling(attributes(parsely, parsely_read))]
75pub struct ParselyReadFieldData {
76    ident: Option<syn::Ident>,
77
78    ty: syn::Type,
79
80    #[darling(flatten)]
81    common: ParselyCommonFieldData,
82    /// 'count' is required when the field is a collection
83    count: Option<syn::Expr>,
84    /// 'while' is an alternate option to 'count' to use with a collection field
85    // #[darling(rename = "while")]
86    // TODO: hopefully can get this to work as 'while'
87    while_pred: Option<syn::Expr>,
88
89    /// Instead of reading the value of this field from the buffer, assign it from the given
90    /// [`syn::Ident`]
91    assign_from: Option<syn::Expr>,
92
93    /// 'when' is required when there's an optional field
94    when: Option<syn::Expr>,
95}
96
97impl ParselyReadFieldData {
98    /// Get the 'buffer type' of this field (the type that will be used when reading from or
99    /// writing to the buffer): for wrapper types (like [`Option`] or [`Vec`]), this will be the
100    /// inner type.
101    pub(crate) fn buffer_type(&self) -> &syn::Type {
102        if self.ty.is_option() || self.ty.is_collection() {
103            self.ty
104                .inner_type()
105                .expect("Option or collection has an inner type")
106        } else {
107            &self.ty
108        }
109    }
110
111    /// Get the context values that need to be passed to the read or write call for this field
112    pub(crate) fn context_values(&self) -> Vec<syn::Expr> {
113        let field_name = self
114            .ident
115            .as_ref()
116            .expect("Field must have a name")
117            .to_string();
118        if let Some(ref field_context) = self.common.context {
119            field_context.expressions(&format!("Read context for field '{field_name}'"))
120        } else {
121            vec![]
122        }
123    }
124}
125
126#[derive(Debug, FromField)]
127#[darling(attributes(parsely, parsely_write))]
128pub struct ParselyWriteFieldData {
129    ident: Option<syn::Ident>,
130
131    ty: syn::Type,
132
133    #[darling(flatten)]
134    common: ParselyCommonFieldData,
135
136    /// An expression or function call that will be used to update this field in the generated
137    /// `StateSync` implementation for its parent type.
138    sync_expr: Option<ExprOrFunc>,
139
140    /// An list of expressions that should be passed as context to this field's sync method.  The
141    /// sync method provides an opportunity to synchronize "linked" fields, where one field's value
142    /// depends on the value of another.
143    #[darling(default)]
144    sync_with: Context,
145}
146
147impl ParselyWriteFieldData {
148    /// Get the 'buffer type' of this field (the type that will be used when reading from or
149    /// writing to the buffer): for wrapper types (like [`Option`] or [`Vec`]), this will be the
150    /// inner type.
151    pub(crate) fn buffer_type(&self) -> &syn::Type {
152        if self.ty.is_option() || self.ty.is_collection() {
153            self.ty
154                .inner_type()
155                .expect("Option or collection has an inner type")
156        } else {
157            &self.ty
158        }
159    }
160
161    /// Get the context values that need to be passed to the read or write call for this field
162    pub(crate) fn context_values(&self) -> Vec<syn::Expr> {
163        let field_name = self
164            .ident
165            .as_ref()
166            .expect("Field must have a name")
167            .to_string();
168        if let Some(ref field_context) = self.common.context {
169            field_context.expressions(&format!("Write context for field '{field_name}'"))
170        } else {
171            vec![]
172        }
173    }
174
175    /// Get the context values that need to be passed to the read or write call for this field
176    pub(crate) fn sync_with_expressions(&self) -> Vec<syn::Expr> {
177        let field_name = self
178            .ident
179            .as_ref()
180            .expect("Field must have a name")
181            .to_string();
182        self.sync_with
183            .expressions(&format!("Sync context for field '{field_name}'"))
184    }
185}
186
187#[derive(Debug, FromDeriveInput)]
188#[darling(attributes(parsely, parsely_read), supports(struct_any, enum_any))]
189pub struct ParselyReadData {
190    ident: syn::Ident,
191    required_context: Option<TypedFnArgList>,
192    alignment: Option<usize>,
193    data: ast::Data<(), ParselyReadFieldData>,
194}
195
196#[derive(Debug, FromDeriveInput)]
197#[darling(attributes(parsely, parsely_write), supports(struct_any, enum_any))]
198pub struct ParselyWriteData {
199    ident: syn::Ident,
200    required_context: Option<TypedFnArgList>,
201    sync_args: Option<TypedFnArgList>,
202    alignment: Option<usize>,
203    data: ast::Data<(), ParselyWriteFieldData>,
204}
205
206pub(crate) fn get_crate_name() -> syn::Ident {
207    let found_crate =
208        proc_macro_crate::crate_name("parsely-rs").expect("parsely-rs is present in Cargo.toml");
209
210    let crate_name = match found_crate {
211        proc_macro_crate::FoundCrate::Itself => "parsely-rs".to_string(),
212        proc_macro_crate::FoundCrate::Name(name) => name,
213    };
214
215    syn::Ident::new(&crate_name, proc_macro2::Span::call_site())
216}