Skip to main content

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 [`compile`]) so the
23/// per-request path is free of filesystem reads and Rhai parsing cost.
24#[derive(Clone, Default)]
25pub struct LoadedMiddlewares {
26    handlers: Vec<MiddlewareHandler>,
27}
28
29impl LoadedMiddlewares {
30    /// Compile every Rhai source file listed in `middleware_file_paths`.
31    ///
32    /// Paths are interpreted relative to `relative_dir_path` — the same
33    /// convention used by `Config::new` for rule-set paths.
34    // clippy: ServerError is a public error type (RFC 030 §6 escalation
35    // trigger); boxing its large variant would change that type's shape.
36    // See ESCALATION-002 in the RFC 030 review-request package.
37    #[allow(clippy::result_large_err)]
38    pub fn compile(
39        middleware_file_paths: &[String],
40        relative_dir_path: &str,
41    ) -> ServerResult<Self> {
42        let mut handlers = Vec::with_capacity(middleware_file_paths.len());
43        for (idx, relative_path) in middleware_file_paths.iter().enumerate() {
44            let joined = Path::new(relative_dir_path).join(relative_path);
45            let path_str = joined.to_str().ok_or_else(|| {
46                ServerError::Io(std::io::Error::new(
47                    std::io::ErrorKind::InvalidData,
48                    format!(
49                        "middleware #{} path contains non-UTF-8 bytes: {}",
50                        idx + 1,
51                        joined.to_string_lossy(),
52                    ),
53                ))
54            })?;
55            handlers.push(MiddlewareHandler::new(path_str)?);
56        }
57        Ok(Self { handlers })
58    }
59
60    pub fn len(&self) -> usize {
61        self.handlers.len()
62    }
63
64    pub fn is_empty(&self) -> bool {
65        self.handlers.is_empty()
66    }
67
68    pub fn iter(&self) -> std::slice::Iter<'_, MiddlewareHandler> {
69        self.handlers.iter()
70    }
71}