summaryrefslogtreecommitdiff
path: root/subplotlib/src/prelude.rs
blob: e0ac6278be168785adbc16e3c02ab543d4ffa37b (plain)
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! The subplotlib prelude.
//!
//! This prelude is automatically imported into all generated subplot test suites
//! using the `rust` template.  Effectively they get
//!
//! ```rust
//! use subplotlib::prelude::*;
//! ```
//!
//! inserted into them at the top of the file.
//!
//! You should familiarise yourself with the context-related types if you
//! are writing your own contexts, or writing step functions which use
//! the contexts provided by the [step library][crate::steplibrary] itself.
//!
//! The primary thing you will need to learn, as a step function author, is
//! the [`#[step]`][macro@step] attribute macro, and it interacts with contexts.

// Re-export fehler's macros so that step functions can use them
pub use fehler::throw;
/// Indicate what type a function throws
///
/// This attribute macro comes from the [`fehler`] crate and is used
/// to indicate that a function "throws" a particular kind of error.
///
/// ```rust
/// # use std::io;
/// # use fehler::throws;
/// #[throws(io::Error)]
/// fn create_thingy() {
///     // something which might cause an io::Error
/// }
/// # fn main() {}
/// ```
///
/// It transforms a function such that the above function would be compiled
/// effectively as:
///
/// ```rust
/// # use std::io;
/// fn create_thingy() -> Result<(), io::Error> {
///     // something which might cause an io::Error
///     Ok(())
/// }
/// ```
///
/// Return statements and the final expression of the function automatically
/// get wrappered with [`Ok`][std::result::Result::Ok].  You can use the
/// [`throw`][macro@throw] macro inside such a function to automatically return
/// an error.
///
// When https://github.com/rust-lang/rust/issues/83976 is resolved, the below
// can be added to this doc string.
// You can see more documentation on this in the [fehler crate docs][fehler::throws].
// Alternatively we can get rid of this entirely if and when
// https://github.com/rust-lang/rust/issues/81893 is fixed.
pub use fehler::throws;

// Re-export the lazy_static macro
#[doc(hidden)]
pub use lazy_static::lazy_static;

// Re-export subplotlib-derive's macros so that #[step] works
// We have to document it here because we're referring to things
// inside subplotlib.
/// Mark a function as a Subplot step function.
///
/// This attribute macro is used to indicate that a given function is
/// a step which can be bound to Subplot scenario statements.
///
/// In its simplest form, you use this thusly:
///
/// ```rust
/// # use subplotlib::prelude::*;
/// # #[derive(Debug, Default)] struct SomeContextType;
/// # impl ContextElement for SomeContextType {}
/// #[step]
/// fn my_step_function(context: &mut SomeContextType, arg1: &str, arg2: &str)
/// {
///     // Do something appropriate here.
/// }
/// # fn main() {}
/// ```
///
/// Step functions must be pretty basic.  For now at least they cannot be async,
/// and they cannot be generic, take variadic arguments, be unsafe, etc.
///
/// The first argument to the step function is considered the step's context.
/// Anything which implements
/// [`ContextElement`] can be used here. You
/// can take either a `&ContextType` or `&mut ContextType` as your context. It's
/// likely best to take the shared borrow if you're not altering the context at all.
///
/// If you require access to a number of context objects as part of your step
/// function implementation, then there is the high level container
/// [`ScenarioContext`].  You should take
/// this high level container as a shared borrow, and then within your step function
/// you can access other contexts as follows:
///
/// ```rust
/// # use subplotlib::prelude::*;
/// # #[derive(Debug, Default)] struct ContextA;
/// # #[derive(Debug, Default)] struct ContextB;
/// # impl ContextElement for ContextA {}
/// # impl ContextElement for ContextB {}
/// # impl ContextA { fn get_thingy(&self) -> Result<usize, StepError> { Ok(0) } }
/// # impl ContextB { fn do_mut_thing(&mut self, _n: usize, _arg: &str) -> Result<(), StepError> { Ok(()) } }
/// #[step]
/// #[context(ContextA)]
/// #[context(ContextB)]
/// fn my_step(context: &ScenarioContext, arg: &str) {
///     let thingy = context.with(|ctx: &ContextA| ctx.get_thingy(), false )?;
///     context.with_mut(|ctx: &mut ContextB| ctx.do_mut_thing(thingy, arg), false)?;
/// }
/// # fn main() {}
/// ```
///
/// Importantly here there is the `#[context(SomeContext)]` attribute to tell
/// the system that you use that context in your step function (without this,
/// the relevant context may not be initialised for the scenario).  The mechanism
/// to then access contexts from the step function is the
/// [`ScenarioContext::with`] and
/// [`ScenarioContext::with_mut`]
/// functions which allow shared, or mutable, access to scenario contexts respectively.
///
/// These functions take two arguments, the first is a closure which will be run
/// with access to the given context and whose return value will be ultimately
/// returned by the call to the function.  The second is whether or not to defuse
/// the poison on the context mutex.  In normal steps this should be `false` since
/// you want a step to fail if the context has been poisoned.  However, in cleanup
/// related step functions you probably want to defuse the poison and be careful in
/// how you then use the contexts so that you can clean up effectively.
pub use subplotlib_derive::step;

// Re-export the step result types
#[doc(inline)]
pub use crate::types::StepError;
#[doc(inline)]
pub use crate::types::StepResult;

// And the step itself
#[doc(inline)]
pub use crate::step::ScenarioStep;

// Data files
#[doc(inline)]
pub use crate::file::SubplotDataFile;

// Finally a scenario itself
#[doc(inline)]
pub use crate::scenario::ContextElement;
#[doc(inline)]
pub use crate::scenario::Scenario;
#[doc(inline)]
pub use crate::scenario::ScenarioContext;

// Utility functions
#[doc(inline)]
pub use crate::utils::base64_decode;