pub struct ModeState { /* private fields */ }Expand description
Manages session mode state with support for runtime transitions
This type encapsulates the current session mode and routing mode configuration, providing thread-safe mode transitions for hybrid routing.
§Design
- Current Mode:
AtomicU8for lock-free concurrent reads/writes - Routing Mode: Immutable configuration (Stateful,
PerCommand, or Hybrid) - Mode transitions are only allowed in Hybrid mode
§One-Way Transition Invariant
CRITICAL: In Hybrid mode, the transition from PerCommand → Stateful is
permanent and irreversible for the lifetime of the connection:
PerCommand ──stateful command──> Stateful
↑ │
└───────── NO WAY BACK ─────────┘Once switch_to_stateful() is called:
- Connection acquires a dedicated backend
- All subsequent commands use that backend
- Connection stays stateful until client disconnects
- New client connection starts fresh in
PerCommandmode (if Hybrid)
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
// Stateful mode (no transitions allowed)
let state = ModeState::new(SessionMode::Stateful, RoutingMode::Stateful);
assert!(state.is_stateful());
assert!(!state.can_switch_mode());
// Hybrid mode (starts per-command, can switch)
let state = ModeState::new(SessionMode::PerCommand, RoutingMode::Hybrid);
assert!(state.is_per_command());
assert!(state.can_switch_mode());
state.switch_to_stateful();
assert!(state.is_stateful());
// Now permanently stateful for this connectionImplementations§
Source§impl ModeState
impl ModeState
Sourcepub const fn new(initial_mode: SessionMode, routing_mode: RoutingMode) -> Self
pub const fn new(initial_mode: SessionMode, routing_mode: RoutingMode) -> Self
Create a new mode state
§Arguments
initial_mode- Initial session moderouting_mode- Routing mode configuration
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let state = ModeState::new(SessionMode::Stateful, RoutingMode::Stateful);
assert!(state.is_stateful());Sourcepub fn mode(&self) -> SessionMode
pub fn mode(&self) -> SessionMode
Get the current session mode
This is a cheap atomic load operation.
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let state = ModeState::new(SessionMode::PerCommand, RoutingMode::PerCommand);
assert_eq!(state.mode(), SessionMode::PerCommand);Sourcepub const fn routing_mode(&self) -> RoutingMode
pub const fn routing_mode(&self) -> RoutingMode
Get the routing mode configuration
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let state = ModeState::new(SessionMode::Stateful, RoutingMode::Stateful);
assert_eq!(state.routing_mode(), RoutingMode::Stateful);Sourcepub fn is_per_command(&self) -> bool
pub fn is_per_command(&self) -> bool
Check if currently in per-command mode
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let state = ModeState::new(SessionMode::PerCommand, RoutingMode::PerCommand);
assert!(state.is_per_command());Sourcepub fn is_stateful(&self) -> bool
pub fn is_stateful(&self) -> bool
Check if currently in stateful mode
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let state = ModeState::new(SessionMode::Stateful, RoutingMode::Stateful);
assert!(state.is_stateful());Sourcepub const fn can_switch_mode(&self) -> bool
pub const fn can_switch_mode(&self) -> bool
Check if mode switching is allowed
Mode switching is only allowed in Hybrid routing mode.
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let hybrid = ModeState::new(SessionMode::PerCommand, RoutingMode::Hybrid);
assert!(hybrid.can_switch_mode());
let stateful = ModeState::new(SessionMode::Stateful, RoutingMode::Stateful);
assert!(!stateful.can_switch_mode());Sourcepub fn switch_to_stateful(&self)
pub fn switch_to_stateful(&self)
Switch to stateful mode (one-way transition)
IMPORTANT: This is a permanent, one-way transition for this connection. Once switched from per-command to stateful mode, the connection remains stateful for its entire lifetime and never switches back.
This transition happens in Hybrid mode when:
- Client issues a stateful command (GROUP, NEXT, LAST, XOVER, etc.)
- Client needs server-side state maintained across commands
- Connection acquires a dedicated backend and keeps it until disconnect
Only allowed in Hybrid routing mode. No-op if already stateful or if routing mode doesn’t allow switching.
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let state = ModeState::new(SessionMode::PerCommand, RoutingMode::Hybrid);
assert!(state.is_per_command());
// Client sends "GROUP alt.binaries.test"
state.switch_to_stateful();
assert!(state.is_stateful());
// Connection stays stateful until client disconnects
// (no way to switch back to per-command)Sourcepub const fn is_per_command_routing(&self) -> bool
pub const fn is_per_command_routing(&self) -> bool
Check if this session is using per-command routing
Returns true if routing_mode is PerCommand or Hybrid.
§Examples
use nntp_proxy::session::{ModeState, SessionMode};
use nntp_proxy::config::RoutingMode;
let per_cmd = ModeState::new(SessionMode::PerCommand, RoutingMode::PerCommand);
assert!(per_cmd.is_per_command_routing());
let hybrid = ModeState::new(SessionMode::PerCommand, RoutingMode::Hybrid);
assert!(hybrid.is_per_command_routing());
let stateful = ModeState::new(SessionMode::Stateful, RoutingMode::Stateful);
assert!(!stateful.is_per_command_routing());Trait Implementations§
Auto Trait Implementations§
impl !Freeze for ModeState
impl RefUnwindSafe for ModeState
impl Send for ModeState
impl Sync for ModeState
impl Unpin for ModeState
impl UnsafeUnpin for ModeState
impl UnwindSafe for ModeState
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more