apimock_server/middleware.rs
1//! Compiled Rhai middlewares.
2//!
3//! # Why this lives here and not in `apimock-config`
4//!
5//! A `MiddlewareHandler` owns a compiled Rhai `AST` and returns
6//! `hyper::Response` values when it handles a request. Both of those
7//! are runtime concerns that must not leak into the config crate (the
8//! config crate must remain serde/TOML-centric so stage-2 GUI editing
9//! can reason about it without linking against a scripting engine).
10
11use std::path::Path;
12
13use crate::error::{ServerError, ServerResult};
14
15pub mod middleware_handler;
16mod middleware_response;
17
18pub use middleware_handler::MiddlewareHandler;
19
20/// An ordered list of compiled middleware handlers.
21///
22/// Compilation happens once at server startup (see
23/// [`compile`](LoadedMiddlewares::compile)) so the per-request path is
24/// free of filesystem reads and Rhai parsing cost.
25#[derive(Clone, Default)]
26pub struct LoadedMiddlewares {
27 handlers: Vec<MiddlewareHandler>,
28}
29
30impl LoadedMiddlewares {
31 /// Compile every Rhai source file listed in `middleware_file_paths`.
32 ///
33 /// Paths are interpreted relative to `relative_dir_path` — the same
34 /// convention used by `Config::new` for rule-set paths.
35 ///
36 /// `max_operations` (RFC 068 S-03) is applied identically to every
37 /// middleware here — see `MiddlewareHandler::new`'s doc comment.
38 pub fn compile(
39 middleware_file_paths: &[String],
40 relative_dir_path: &str,
41 max_operations: u64,
42 ) -> ServerResult<Self> {
43 let mut handlers = Vec::with_capacity(middleware_file_paths.len());
44 for (idx, relative_path) in middleware_file_paths.iter().enumerate() {
45 let joined = Path::new(relative_dir_path).join(relative_path);
46 let path_str = joined.to_str().ok_or_else(|| {
47 ServerError::Io(std::io::Error::new(
48 std::io::ErrorKind::InvalidData,
49 format!(
50 "middleware #{} path contains non-UTF-8 bytes: {}",
51 idx + 1,
52 joined.to_string_lossy(),
53 ),
54 ))
55 })?;
56 handlers.push(MiddlewareHandler::new(path_str, max_operations)?);
57 }
58 Ok(Self { handlers })
59 }
60
61 pub fn len(&self) -> usize {
62 self.handlers.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
66 self.handlers.is_empty()
67 }
68
69 pub fn iter(&self) -> std::slice::Iter<'_, MiddlewareHandler> {
70 self.handlers.iter()
71 }
72}