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}