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