Skip to main content

nautilus_live/node/
state.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::sync::{
17    Arc,
18    atomic::{AtomicBool, AtomicU8, Ordering},
19};
20
21/// Lifecycle state of the `LiveNode` runner.
22#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
23#[repr(u8)]
24pub enum NodeState {
25    #[default]
26    Idle = 0,
27    Starting = 1,
28    Running = 2,
29    ShuttingDown = 3,
30    Stopped = 4,
31}
32
33impl NodeState {
34    /// Creates a `NodeState` from its `u8` representation.
35    ///
36    /// # Panics
37    ///
38    /// Panics if the value is not a valid `NodeState` discriminant (0-4).
39    #[must_use]
40    pub const fn from_u8(value: u8) -> Self {
41        match value {
42            0 => Self::Idle,
43            1 => Self::Starting,
44            2 => Self::Running,
45            3 => Self::ShuttingDown,
46            4 => Self::Stopped,
47            _ => panic!("Invalid NodeState value"),
48        }
49    }
50
51    /// Returns the `u8` representation of this state.
52    #[must_use]
53    pub const fn as_u8(self) -> u8 {
54        self as u8
55    }
56
57    /// Returns whether the state is `Running`.
58    #[must_use]
59    pub const fn is_running(&self) -> bool {
60        matches!(self, Self::Running)
61    }
62}
63
64/// A thread-safe handle to control a `LiveNode` from other threads.
65///
66/// This allows stopping and querying the node's state without requiring the
67/// node itself to be Send + Sync.
68#[derive(Clone, Debug)]
69pub struct LiveNodeHandle {
70    pub(crate) stop_flag: Arc<AtomicBool>,
71    pub(crate) state: Arc<AtomicU8>,
72}
73
74impl Default for LiveNodeHandle {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl LiveNodeHandle {
81    /// Creates a new handle with default (`Idle`) state.
82    #[must_use]
83    pub fn new() -> Self {
84        Self {
85            stop_flag: Arc::new(AtomicBool::new(false)),
86            state: Arc::new(AtomicU8::new(NodeState::Idle.as_u8())),
87        }
88    }
89
90    pub(crate) fn set_state(&self, state: NodeState) {
91        self.state.store(state.as_u8(), Ordering::Relaxed);
92        if state == NodeState::Running {
93            self.stop_flag.store(false, Ordering::Relaxed);
94        }
95    }
96
97    /// Returns the current node state.
98    #[must_use]
99    pub fn state(&self) -> NodeState {
100        NodeState::from_u8(self.state.load(Ordering::Relaxed))
101    }
102
103    /// Returns whether the node should stop.
104    #[must_use]
105    pub fn should_stop(&self) -> bool {
106        self.stop_flag.load(Ordering::Relaxed)
107    }
108
109    /// Returns whether the node is currently running.
110    #[must_use]
111    pub fn is_running(&self) -> bool {
112        self.state().is_running()
113    }
114
115    /// Signals the node to stop.
116    pub fn stop(&self) {
117        self.stop_flag.store(true, Ordering::Relaxed);
118    }
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq)]
122pub(super) enum EngineConnectionStatus {
123    Connected,
124    TimedOut,
125    StopRequested,
126    ShutdownRequested,
127}
128
129impl EngineConnectionStatus {
130    pub(super) const fn abort_reason(self) -> Option<&'static str> {
131        match self {
132            Self::Connected | Self::TimedOut => None,
133            Self::StopRequested => Some("Stop signal received during startup"),
134            Self::ShutdownRequested => Some("Shutdown signal received during startup"),
135        }
136    }
137}