File: define.rs

package info (click to toggle)
rust-serde-json-path-macros-internal 0.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 144 kB
  • sloc: makefile: 4
file content (123 lines) | stat: -rw-r--r-- 4,093 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{ItemFn, LitStr};

use crate::common::extract::FnArgument;

use super::extract::{extract_components, Components};

pub(crate) struct Expanded {
    pub(crate) name_str: LitStr,
    pub(crate) validator: TokenStream,
    pub(crate) validator_name: Ident,
    pub(crate) evaluator: TokenStream,
    pub(crate) evaluator_name: Ident,
    pub(crate) result: TokenStream,
    pub(crate) core: TokenStream,
}

/// Expand the macro input to produce the common elements used in the `#[function]` and
/// `#[register]` macros.
pub(crate) fn expand(input: ItemFn, name_str: Option<LitStr>) -> Result<Expanded, TokenStream> {
    let ItemFn {
        attrs: _,
        vis: _,
        sig,
        block,
    } = input;

    let Components {
        name,
        generics,
        validator_name,
        evaluator_name,
        result,
        args,
        ret,
        inputs,
    } = match extract_components(sig) {
        Ok(fd) => fd,
        Err(e) => return Err(e.into_compile_error()),
    };
    // Stringified name of the function:
    let name_str = name_str.unwrap_or_else(|| LitStr::new(name.to_string().as_str(), name.span()));
    // The number of arguments the function accepts:
    let args_len = args.len();
    // Generate token streams for some needed types:
    let lazy = quote! {
        std::sync::LazyLock
    };
    let core = quote! {
        ::serde_json_path_macros::serde_json_path_core::spec::functions
    };
    let res = quote! {
        std::result::Result
    };
    // Generate code for checking each individual argument in a query at parse time:
    let arg_checks = args.iter().enumerate().map(|(idx, arg)| {
        let FnArgument { ident: _, ty } = arg;
        quote! {
            match a[#idx].as_type_kind() {
                #res::Ok(tk) => {
                    if !tk.converts_to(#ty::json_path_type()) {
                        return #res::Err(#core::FunctionValidationError::MismatchTypeKind {
                            name: String::from(#name_str),
                            expected: #ty::json_path_type(),
                            received: tk,
                            position: #idx,
                        });
                    }
                },
                #res::Err(err) => return #res::Err(err)
            }
        }
    });
    // Generate the validator function used at parse time to validate a function declaration:
    let validator = quote! {
        static #validator_name: #core::Validator = #lazy::new(|| {
            std::boxed::Box::new(|a: &[#core::FunctionExprArg]| {
                if a.len() != #args_len {
                    return #res::Err(#core::FunctionValidationError::NumberOfArgsMismatch {
                        expected: a.len(),
                        received: #args_len,
                    });
                }
                #(#arg_checks)*
                Ok(())
            })
        });
    };
    // Generate the code to declare each individual argument for evaluation, at query time:
    let arg_declarations = args.iter().map(|arg| {
        let FnArgument { ident, ty } = arg;
        // validation should ensure unwrap is okay here:
        quote! {
            let #ident = #ty::try_from(v.pop_front().unwrap()).unwrap();
        }
    });
    // Produce the argument name identifiers:
    let arg_names = args.iter().map(|arg| {
        let FnArgument { ident, ty: _ } = arg;
        ident
    });
    // Generate the evaluator function used to evaluate a function at query time:
    let evaluator = quote! {
        fn #name #generics (#inputs) #ret #block
        static #evaluator_name: #core::Evaluator = #lazy::new(|| {
            std::boxed::Box::new(|mut v: std::collections::VecDeque<#core::JsonPathValue>| {
                #(#arg_declarations)*
                return #name(#(#arg_names,)*).into()
            })
        });
    };

    Ok(Expanded {
        name_str,
        validator,
        validator_name,
        evaluator,
        evaluator_name,
        result,
        core,
    })
}