|
| 1 | +//! This crate provides the `nasl_function` proc macro, which is |
| 2 | +//! designed to make implementing new NASL builtin functions as |
| 3 | +//! convenient as possible. |
| 4 | +//! |
| 5 | +//! Design: There are two main purposes that the `nasl_function` macro |
| 6 | +//! serves. |
| 7 | +//! |
| 8 | +//! Purpose 1: Unify argument handling. |
| 9 | +//! |
| 10 | +//! The `nasl_function!` macro provides a structured approach to argument handling |
| 11 | +//! within NASL builtin functions. The macro takes as input a function |
| 12 | +//! taking any number of arguments, along with instructions on whether |
| 13 | +//! those arguments are named, positional, optional, etc. It then |
| 14 | +//! produces a function that automatically handles conversion of the |
| 15 | +//! arguments into the correct types and produces consistent error |
| 16 | +//! messages if the function has been called with an invalid set of |
| 17 | +//! arguments. |
| 18 | +//! |
| 19 | +//! To do so, the macro transforms the annotated function into a function |
| 20 | +//! taking `&Context` and `&Register` as arguments (plus self arguments |
| 21 | +//! if needed) and then calls the original function from within the transformed |
| 22 | +//! function, deriving each argument from the `FromNaslValue` implementation |
| 23 | +//! of its type and handling optional and named arguments appropriately. |
| 24 | +//! |
| 25 | +//! The macro renames the inner function into a proper, first class |
| 26 | +//! function instead of a closure in order to provide support for |
| 27 | +//! async functions (without relying on the unstable async |
| 28 | +//! closures). |
| 29 | +//! |
| 30 | +//! Purpose 2: Provide a uniform way to add builtin functions to function sets. |
| 31 | +//! |
| 32 | +//! NASL builtin functions come in one of several types, depending on |
| 33 | +//! their asyncness and whether they are stateless or stateful (and |
| 34 | +//! whether they require mutable access to their state, if they |
| 35 | +//! are). The `NaslFunction` type defined in the executor code is a |
| 36 | +//! singular type which can represent all the various variants of |
| 37 | +//! builtin functions. The executor also provides the |
| 38 | +//! `StoredFunctionSet`, which represents a set of `NaslFunction`s |
| 39 | +//! together with their state. This state struct is used both as the |
| 40 | +//! actual state that these functions require, as well as an |
| 41 | +//! identifying name. Together, the `NaslFunction` and |
| 42 | +//! `StoredFunctionSet` types provide the ability to store NASL |
| 43 | +//! functions in a type-erased way, so that the interpreter can run |
| 44 | +//! them independently of their properties. |
| 45 | +//! |
| 46 | +//! In order to provide a unified interface for adding NASL functions |
| 47 | +//! to `StoredFunctionSet`s, there needs to be a way to convert any of |
| 48 | +//! the 6 variants which builtin functions come in (sync_stateless, |
| 49 | +//! async_stateless, sync_stateful, ... ) into their corresponding |
| 50 | +//! variant of `NaslFunction`. On the surface, this problem sounds |
| 51 | +//! simple: Simply implement `Into<NaslFunction>` for `Fn(&Context, |
| 52 | +//! &Register) -> NaslResult` as well as for `Fn(&Context, &Register) |
| 53 | +//! -> Future<NaslResult>`, as well as for the other 4 variants. Then |
| 54 | +//! provide a `add_function` method on `StoredFunctionSet` that takes |
| 55 | +//! any `impl Into<NaslFunction>` as argument. The problem with this |
| 56 | +//! approach is that the Rust compiler cannot determine that these 6 |
| 57 | +//! implementations are coherent, i.e. it believes that there might be |
| 58 | +//! a type `T` that implements multiple of these `Fn` traits |
| 59 | +//! simultaneously, which would result in overlapping trait impls. |
| 60 | +//! |
| 61 | +//! In order to solve this problem, the `nasl_function!` macro |
| 62 | +//! transforms the annotated function into a special function that |
| 63 | +//! takes a `StoredFunctionSet` and adds the correct variant of |
| 64 | +//! `NaslFunction` to the set. This is a very indirect approach, but |
| 65 | +//! it works because the `nasl_function!` macro knows exactly what the |
| 66 | +//! signature of the annotated function is and can therefore derive |
| 67 | +//! which of the 6 variants of `NaslFunction` it should become, |
| 68 | +//! without requiring type-erasure via an intermediate trait. |
| 69 | +
|
1 | 70 | mod codegen;
|
2 | 71 | mod error;
|
3 | 72 | mod parse;
|
|
0 commit comments