machine_check/
builder.rs

1use crate::{ExecArgs, FullMachine};
2use machine_check_common::{ExecResult, PropertyMacroFn, PropertyMacros};
3use std::collections::HashMap;
4
5type CreatorFn<M, D, E, A> = dyn Fn(A) -> Result<(M, D), E>;
6
7/// A builder for **machine-check** execution.
8///
9/// Using a builder allows richer configuration of system execution.
10/// Notably, it is possible to define user macros that can be used
11/// in properties.
12pub struct ExecBuilder<M: FullMachine, D: Send + 'static, E: 'static, A> {
13    instantiation_fn: Box<CreatorFn<M, D, E, A>>,
14    property_macros: HashMap<String, PropertyMacroFn<D>>,
15}
16
17impl<M: FullMachine, D: Send + 'static, E: 'static, A: clap::Args + 'static>
18    ExecBuilder<M, D, E, A>
19{
20    /// Creates the builder.
21    ///
22    /// The function `instantiation_fn` should take supplied system arguments
23    /// and return a tuple of the instantiated system and custom system data
24    /// that can be used within custom property macros, or an error
25    /// on a failure to instantiate the system.
26    ///
27    /// Logging is initialised before `instantiation_fn` is called,
28    /// so it can be used in `instantiation_fn` normally.
29    pub fn new(instantiation_fn: fn(A) -> Result<(M, D), E>) -> Self {
30        Self {
31            instantiation_fn: Box::new(instantiation_fn),
32            property_macros: HashMap::new(),
33        }
34    }
35}
36
37impl<M: FullMachine, D: Send + 'static, E: 'static, A> ExecBuilder<M, D, E, A> {
38    /// Adds a custom macro that can be used within properties.
39    ///
40    /// The macro is called as a function-like macro using the given name.
41    /// The macro function `macro_fn` is called when processing a property
42    /// containing this macro with a reference to custom system data
43    /// and a [`proc_macro2::TokenStream`]. On success, it should return
44    /// [`proc_macro2::TokenStream`] with the tokens it will be replaced with.
45    ///
46    /// For example, a custom property macro with name `example` can be called
47    /// within properties as `example!(example_data)`.
48    ///
49    ///
50    pub fn property_macro(mut self, name: String, macro_fn: PropertyMacroFn<D>) -> Self {
51        if self
52            .property_macros
53            .insert(name.clone(), macro_fn)
54            .is_some()
55        {
56            panic!("Multiple property macros with name {:?}", name);
57        }
58
59        self
60    }
61
62    /// Executes **machine-check** with the given execution and system arguments.
63    ///
64    /// The inputs `exec_args` and `system_args` are typically to be given
65    /// from the result of [`crate::parse_args`].
66    ///
67    /// Consumes the builder and returns an execution result or an error
68    /// from the system creator function.
69    pub fn execute(self, exec_args: ExecArgs, system_args: A) -> Result<ExecResult, E> {
70        super::setup_logging(&exec_args);
71        match (self.instantiation_fn)(system_args) {
72            Ok((system, data)) => Ok(super::execute_inner(
73                system,
74                exec_args,
75                PropertyMacros {
76                    macros: self.property_macros,
77                    data,
78                },
79            )),
80            Err(err) => Err(err),
81        }
82    }
83}