parsely_impl/
model_types.rs

1use darling::{ast, FromMeta};
2use proc_macro2::TokenStream;
3use quote::{format_ident, quote, ToTokens};
4use syn::parse::Parse;
5
6use crate::get_crate_name;
7
8pub(crate) enum CollectionLimit {
9    Count(syn::Expr),
10    While(syn::Expr),
11}
12
13#[derive(Debug)]
14pub(crate) struct TypedFnArgList(pub(crate) Vec<TypedFnArg>);
15
16impl TypedFnArgList {
17    /// Get the types from this [`TypedFnArgList`] as a vec
18    pub(crate) fn types(&self) -> Vec<&syn::Type> {
19        self.0.iter().map(|t| t.ty()).collect()
20    }
21
22    pub(crate) fn names(&self) -> Vec<&syn::Ident> {
23        self.0.iter().map(|t| t.name()).collect()
24    }
25
26    // TODO: this is context-specific, but now this type is more generic.  move it?
27    pub(crate) fn assignments(&self) -> Vec<Local> {
28        self.0
29            .iter()
30            .enumerate()
31            .map(|(idx, fn_arg)| {
32                let idx: syn::Index = idx.into();
33                syn::parse2::<Local>(quote! {
34                    let #fn_arg = ctx.#idx;
35                })
36                .unwrap()
37            })
38            .collect::<Vec<_>>()
39    }
40}
41
42impl FromMeta for TypedFnArgList {
43    fn from_none() -> Option<Self> {
44        None
45    }
46
47    fn from_list(items: &[ast::NestedMeta]) -> darling::Result<Self> {
48        let required_context: Vec<TypedFnArg> = items
49            .iter()
50            .map(|item| {
51                match item {
52                    // TODO: better error message here
53                    ast::NestedMeta::Meta(_) => Err(darling::Error::unsupported_format(
54                        "FnArg literals required",
55                    )),
56                    ast::NestedMeta::Lit(lit) => match lit {
57                        syn::Lit::Str(s) => s.parse().map_err(|e| e.into()),
58                        l => Err(darling::Error::unexpected_lit_type(l)),
59                    },
60                }
61            })
62            .collect::<Result<Vec<_>, _>>()?;
63
64        Ok(Self(required_context))
65    }
66}
67
68#[derive(Debug, Default)]
69pub(crate) struct Context(Vec<syn::Expr>);
70
71impl Context {
72    /// Get the context arguments as a vector of expressions, with an erorr context including the
73    /// given `context` value.
74    pub(crate) fn expressions(&self, context: &str) -> Vec<syn::Expr> {
75        // We support Context expressions that return a ParselyResult or a raw value.  So now wrap
76        // all the expressions with code that will normalize all of the results into
77        // ParselyResults.
78        self.0
79            .iter()
80            .cloned()
81            .enumerate()
82            .map(|(idx, e)| {
83                syn::parse2(quote! {
84                    (#e).into_parsely_result().with_context(|| format!("{}: expression {}", #context, #idx))?
85                })
86                .unwrap()
87            })
88            .collect()
89    }
90
91    pub(crate) fn is_empty(&self) -> bool {
92        self.0.is_empty()
93    }
94}
95
96impl FromMeta for Context {
97    fn from_none() -> Option<Self> {
98        None
99    }
100    fn from_list(items: &[ast::NestedMeta]) -> darling::Result<Self> {
101        let expressions: Vec<syn::Expr> = items
102            .iter()
103            .map(|item| {
104                match item {
105                    // TODO: better error message here
106                    ast::NestedMeta::Meta(_) => Err(darling::Error::unsupported_format(
107                        "FnArg literals required",
108                    )),
109                    ast::NestedMeta::Lit(lit) => match lit {
110                        syn::Lit::Str(s) => s.parse().map_err(|e| e.into()),
111                        l => Err(darling::Error::unexpected_lit_type(l)),
112                    },
113                }
114            })
115            .collect::<Result<Vec<_>, _>>()?;
116
117        Ok(Self(expressions))
118    }
119}
120
121/// Same as [`syn::FnArg`] but only allows the [`syn::FnArg::Typed`] variant.
122#[derive(Debug)]
123pub(crate) struct TypedFnArg(syn::FnArg);
124
125impl Parse for TypedFnArg {
126    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
127        match syn::FnArg::parse(input) {
128            Ok(syn::FnArg::Typed(t)) => Ok(Self(syn::FnArg::Typed(t))),
129            Ok(syn::FnArg::Receiver(_)) => todo!("figure out error to return here"),
130            Err(e) => Err(e),
131        }
132    }
133}
134
135impl TypedFnArg {
136    pub(crate) fn ty(&self) -> &syn::Type {
137        match self.0 {
138            syn::FnArg::Typed(ref t) => &t.ty,
139            _ => unreachable!("TypedFnArg should always be typed"),
140        }
141    }
142
143    pub(crate) fn name(&self) -> &syn::Ident {
144        match self.0 {
145            syn::FnArg::Typed(ref pat_type) => match *pat_type.pat {
146                syn::Pat::Ident(ref pat_ident) => &pat_ident.ident,
147                _ => unreachable!("TypedFnArg should always have an ident"),
148            },
149            _ => unreachable!("TypedFnArg should always be typed"),
150        }
151    }
152}
153
154impl ToTokens for TypedFnArg {
155    fn to_tokens(&self, tokens: &mut TokenStream) {
156        self.0.to_tokens(tokens)
157    }
158}
159
160/// [`syn::Local`] exists but doesn't have its own parse method, it get parsed as part of
161/// [`syn::Stmt`]
162pub(crate) struct Local(pub(crate) syn::Local);
163
164impl Parse for Local {
165    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
166        match syn::Stmt::parse(input) {
167            Ok(syn::Stmt::Local(l)) => Ok(Self(l)),
168            _ => Err(input.error("Failed to parse Local, expected a local declaration")),
169        }
170    }
171}
172
173impl ToTokens for Local {
174    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
175        self.0.to_tokens(tokens)
176    }
177}
178
179#[derive(Debug)]
180pub(crate) enum ExprOrFunc {
181    Expr(syn::Expr),
182    Func(syn::Ident),
183}
184
185impl Parse for ExprOrFunc {
186    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
187        if let Ok(expr) = syn::Expr::parse(input) {
188            Ok(ExprOrFunc::Expr(expr))
189        } else if let Ok(id) = syn::Ident::parse(input) {
190            Ok(ExprOrFunc::Func(id))
191        } else {
192            Err(input.error("Failed to parse ExporOrFunc: expected ExprClosure or Ident"))
193        }
194    }
195}
196
197impl ToTokens for ExprOrFunc {
198    fn to_tokens(&self, tokens: &mut TokenStream) {
199        match self {
200            ExprOrFunc::Expr(e) => e.to_tokens(tokens),
201            ExprOrFunc::Func(f) => f.to_tokens(tokens),
202        }
203    }
204}
205
206impl FromMeta for ExprOrFunc {
207    fn from_string(value: &str) -> darling::Result<Self> {
208        syn::parse_str::<ExprOrFunc>(value).map_err(darling::Error::custom)
209    }
210}
211
212#[derive(Debug)]
213pub(crate) enum FuncOrClosure {
214    Func(syn::Ident),
215    Closure(syn::ExprClosure),
216}
217
218impl Parse for FuncOrClosure {
219    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
220        if let Ok(expr_closure) = syn::ExprClosure::parse(input) {
221            Ok(FuncOrClosure::Closure(expr_closure))
222        } else if let Ok(id) = syn::Ident::parse(input) {
223            Ok(FuncOrClosure::Func(id))
224        } else {
225            Err(input.error("Failed to parse FuncOrClosure: expected ExprClosure or Ident"))
226        }
227    }
228}
229
230impl ToTokens for FuncOrClosure {
231    fn to_tokens(&self, tokens: &mut TokenStream) {
232        match self {
233            FuncOrClosure::Func(m) => m.to_tokens(tokens),
234            FuncOrClosure::Closure(c) => c.to_tokens(tokens),
235        }
236    }
237}
238
239impl FromMeta for FuncOrClosure {
240    fn from_string(value: &str) -> darling::Result<Self> {
241        syn::parse_str::<FuncOrClosure>(value).map_err(darling::Error::custom)
242    }
243}
244
245/// A map expression that can be applied to a value after reading or before writing
246#[derive(Debug)]
247pub(crate) struct MapExpr(FuncOrClosure);
248
249impl FromMeta for MapExpr {
250    fn from_string(value: &str) -> darling::Result<Self> {
251        Ok(Self(FuncOrClosure::from_string(value)?))
252    }
253}
254
255impl MapExpr {
256    pub(crate) fn to_read_map_tokens(&self, field_name: &syn::Ident, tokens: &mut TokenStream) {
257        let crate_name = get_crate_name();
258        let field_name_string = field_name.to_string();
259        let map_expr = &self.0;
260        // TODO: is there a case where context might be required for reading the 'buffer_type'
261        // value?
262        tokens.extend(quote! {
263            {
264                let original_value = ::#crate_name::ParselyRead::read::<T>(buf, ())
265                    .with_context(|| format!("Reading raw value for field '{}'", #field_name_string))?;
266                (#map_expr)(original_value).into_parsely_result()
267                    .with_context(|| format!("Mapping raw value for field '{}'", #field_name_string))
268            }
269        })
270    }
271
272    pub(crate) fn to_write_map_tokens(&self, field_name: &syn::Ident, tokens: &mut TokenStream) {
273        let crate_name = get_crate_name();
274        let field_name_string = field_name.to_string();
275        let map_expr = &self.0;
276        tokens.extend(quote! {
277            {
278                let mapped_value = (#map_expr)(&self.#field_name);
279                // Coerce the result of the mapping function into a ParselyResult<T> where we know
280                // T is writable to the buffer.  We need to use this syntax because otherwise the
281                // compiler gets caught up on trying to infer the buffer type.
282                let result = <_ as IntoWritableParselyResult<_, B>>::into_writable_parsely_result(mapped_value)
283                    .with_context(|| format!("Mapping raw value for field '{}'", #field_name_string))?;
284                ::#crate_name::ParselyWrite::write::<T>(&result, buf, ())
285                    .with_context(|| format!("Writing mapped value for field '{}'", #field_name_string))?;
286            }
287        })
288    }
289}
290
291/// An assertion that can be used after reading a value or before writing one
292#[derive(Debug)]
293pub(crate) struct Assertion(FuncOrClosure);
294
295impl Parse for Assertion {
296    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
297        Ok(Self(FuncOrClosure::parse(input)?))
298    }
299}
300
301impl FromMeta for Assertion {
302    fn from_string(value: &str) -> darling::Result<Self> {
303        Ok(Self(FuncOrClosure::from_string(value)?))
304    }
305}
306
307impl Assertion {
308    pub(crate) fn to_read_assertion_tokens(&self, field_name: &str, tokens: &mut TokenStream) {
309        let assertion = &self.0;
310        let assertion_string = quote! { #assertion }.to_string();
311        tokens.extend(quote! {
312            .and_then(|read_value| {
313                let assertion_func = #assertion;
314                if !assertion_func(&read_value) {
315                    bail!("Assertion failed: value of field '{}' ('{:?}') didn't pass assertion: '{}'", #field_name, read_value, #assertion_string)
316                }
317                Ok(read_value)
318            })
319        });
320    }
321
322    pub(crate) fn to_write_assertion_tokens(&self, field_name: &str, tokens: &mut TokenStream) {
323        let assertion = &self.0;
324        let assertion_string = quote! { #assertion }.to_string();
325        let assertion_func_ident = format_ident!("__{}_assertion_func", field_name);
326        let field_name_ident = format_ident!("{field_name}");
327        tokens.extend(quote! {
328            let #assertion_func_ident = #assertion;
329            if !#assertion_func_ident(&self.#field_name_ident) {
330                bail!("Assertion failed: value of field '{}' ('{:?}') didn't pass assertion: '{}'", #field_name, self.#field_name_ident, #assertion_string)
331            }
332        })
333    }
334}
335
336pub(crate) fn wrap_read_with_padding_handling(
337    element_ident: &syn::Ident,
338    alignment: usize,
339    inner: TokenStream,
340) -> TokenStream {
341    let bytes_read_before_ident = format_ident!("__bytes_read_before_{element_ident}_read");
342    let bytes_read_after_ident = format_ident!("__bytes_read_after_{element_ident}_read");
343    let amount_read_ident = format_ident!("__bytes_read_for_{element_ident}");
344
345    quote! {
346        let #bytes_read_before_ident = buf.remaining_bytes();
347
348        #inner
349
350        let #bytes_read_after_ident = buf.remaining_bytes();
351        let mut #amount_read_ident = #bytes_read_before_ident - #bytes_read_after_ident;
352        while #amount_read_ident % #alignment != 0 {
353            let _ = buf.get_u8().context("padding")?;
354            #amount_read_ident += 1;
355        }
356    }
357}
358
359pub(crate) fn wrap_write_with_padding_handling(
360    element_ident: &syn::Ident,
361    alignment: usize,
362    inner: TokenStream,
363) -> TokenStream {
364    let bytes_written_before_ident = format_ident!("__bytes_written_before_{element_ident}_write");
365    let bytes_written_after_ident = format_ident!("__bytes_written_after_{element_ident}_write");
366    let amount_written_ident = format_ident!("__bytes_written_for_{element_ident}");
367
368    quote! {
369        let #bytes_written_before_ident = buf.remaining_bytes();
370
371        #inner
372
373        let #bytes_written_after_ident = buf.remaining_bytes();
374        let mut #amount_written_ident = #bytes_written_after_ident - #bytes_written_before_ident;
375        while #amount_written_ident % #alignment != 0 {
376            buf.put_u8(0).context("padding")?;
377            #amount_written_ident += 1;
378        }
379    }
380}