Skip to main content

apimock_server/
control.rs

1//! Stage-1 control/introspection API for the server.
2//!
3//! # Scope (5.0.0)
4//!
5//! Per the brief (§4.3, §5.3, §7), the server crate exposes:
6//!
7//! - A minimal handle the embedder can hold.
8//! - A "state" enum the embedder can poll.
9//! - A reload-hint type.
10//!
11//! Critically, **the server does not restart itself**. When a config
12//! edit would need the listener rebuilt, the server-side code only
13//! emits a hint; an external control layer (GUI / supervisor) decides
14//! whether to restart. That's the brief's §7 rule and the shape here
15//! reflects it.
16//!
17//! Implementation of actual shutdown / reload wiring is stage-2 work.
18//! In 5.0.0 these types exist with placeholder methods so downstream
19//! code can start coding against them.
20
21use serde::Serialize;
22
23/// Handle an embedder holds to interact with a running server.
24///
25/// # Why it doesn't expose a `.restart()`
26///
27/// The brief is specific: "restart は server crate の内部責務にしない"
28/// (restart is not a server-crate responsibility). A `ServerHandle`
29/// carries read-only introspection and a shutdown signal — nothing
30/// more. If a change requires the listener to rebind a new port, the
31/// embedder tears the server down and constructs a fresh one.
32#[derive(Clone, Debug)]
33#[non_exhaustive]
34pub struct ServerHandle {
35    /// Address the HTTP listener is bound to, if any.
36    pub http_addr: Option<std::net::SocketAddr>,
37    /// Address the HTTPS listener is bound to, if any.
38    pub https_addr: Option<std::net::SocketAddr>,
39}
40
41/// Small control surface for the embedder.
42///
43/// 5.0.0 ships this as a placeholder; stage-2 adds the actual
44/// shutdown-signal channel + reload trigger implementation.
45#[derive(Clone, Debug, Default)]
46#[non_exhaustive]
47pub struct ServerControl {}
48
49impl ServerControl {
50    pub fn new() -> Self {
51        Self {}
52    }
53}
54
55/// What the server is doing right now.
56#[derive(Clone, Copy, Debug, Serialize)]
57pub enum ServerState {
58    /// The listener is being brought up.
59    Starting,
60    /// Requests are being served normally.
61    Running,
62    /// Shutdown has been requested; drains are in flight.
63    ShuttingDown,
64    /// The listener is no longer accepting connections.
65    Stopped,
66}
67
68/// How much of the server needs to restart after a config change.
69///
70/// # Why this type also lives in `apimock-config`
71///
72/// The config crate defines the *same* `ReloadHint` enum as its
73/// `view::ReloadHint` because it's what an `ApplyResult` carries — the
74/// GUI consumes the hint via the config-layer API without pulling the
75/// server crate. The server's copy exists so that runtime code can
76/// also produce hints without depending on the config crate's
77/// GUI-shaped module. Both types convert into each other trivially.
78#[derive(Clone, Copy, Debug, Serialize)]
79pub enum ReloadHint {
80    /// No reload required.
81    None,
82    /// Rule sets / middlewares need to reload.
83    Reload,
84    /// Listener configuration changed; need a full restart.
85    Restart,
86}
87
88impl From<apimock_config::ReloadHint> for ReloadHint {
89    fn from(value: apimock_config::ReloadHint) -> Self {
90        match value {
91            apimock_config::ReloadHint::None => ReloadHint::None,
92            apimock_config::ReloadHint::Reload => ReloadHint::Reload,
93            apimock_config::ReloadHint::Restart => ReloadHint::Restart,
94        }
95    }
96}
97
98impl From<ReloadHint> for apimock_config::ReloadHint {
99    fn from(value: ReloadHint) -> Self {
100        match value {
101            ReloadHint::None => apimock_config::ReloadHint::None,
102            ReloadHint::Reload => apimock_config::ReloadHint::Reload,
103            ReloadHint::Restart => apimock_config::ReloadHint::Restart,
104        }
105    }
106}