From f2bc2d912d45ad6fd43c70a0d0f15a0f6c4b905c Mon Sep 17 00:00:00 2001 From: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:30:25 -0600 Subject: [PATCH] Store session state directly --- LEAF_MACRO_INTERFACE.md | 13 ++++++++----- src/protocol/leaf_template.rs | 4 +--- src/protocol/runtime.rs | 6 +++--- src/protocol/session.rs | 26 +++++++++----------------- 4 files changed, 21 insertions(+), 28 deletions(-) diff --git a/LEAF_MACRO_INTERFACE.md b/LEAF_MACRO_INTERFACE.md index 117aa21..869288d 100644 --- a/LEAF_MACRO_INTERFACE.md +++ b/LEAF_MACRO_INTERFACE.md @@ -41,7 +41,7 @@ unshell_leaf! { authors: unshell::alloc::vec!["ASTATIN3"], }, sessions { - pty: PtySession, + pty: PtySessionState, } procedures {} } @@ -59,10 +59,14 @@ The example above expands to the equivalent of: pub struct FakePtyLeaf { state: FakePtyState, outbox: LeafOutbox, - pty: SessionFamily<>::State>, + pty: SessionFamily, } ``` +Session types are the per-hook state values themselves. There is no separate +zero-sized handler struct; a type like `PtySessionState` implements `Session` and is +stored directly in the generated `SessionFamily`. + The wrapper implements: - `new(state)` @@ -149,13 +153,12 @@ Ratatui rendering is a plain feature-gated pass: leaf.render_ratatui(frame, area, &mut interface); ``` -Session rendering is an associated function because session families are type-level -contracts, not stored objects: +Session rendering is an associated function on the stored session state type: ```rust fn render_ratatui( leaf: &LeafState, - session: &Self::State, + session: &Self, view: &mut SessionView, frame: &mut ratatui::Frame<'_>, area: ratatui::layout::Rect, diff --git a/src/protocol/leaf_template.rs b/src/protocol/leaf_template.rs index 339065b..cdbe86c 100644 --- a/src/protocol/leaf_template.rs +++ b/src/protocol/leaf_template.rs @@ -17,9 +17,7 @@ macro_rules! unshell_leaf { state: $State, outbox: $crate::protocol::LeafOutbox, $( - $session_field: $crate::protocol::SessionFamily< - <$Session as $crate::protocol::Session<$State>>::State, - >, + $session_field: $crate::protocol::SessionFamily<$Session>, )* } diff --git a/src/protocol/runtime.rs b/src/protocol/runtime.rs index c16f5d6..78a319e 100644 --- a/src/protocol/runtime.rs +++ b/src/protocol/runtime.rs @@ -91,7 +91,7 @@ pub fn dispatch_session( endpoint: &Endpoint, leaf_id: u32, leaf: &mut L, - family: &mut SessionFamily, + family: &mut SessionFamily, packet: Packet, outbox: &mut LeafOutbox, interface: &mut Option<&mut InterfaceStore>, @@ -215,7 +215,7 @@ pub fn dispatch_session( pub fn update_session_family( leaf_id: u32, leaf: &mut L, - family: &mut SessionFamily, + family: &mut SessionFamily, interface: &mut Option<&mut InterfaceStore>, ) where S: Session, @@ -338,7 +338,7 @@ pub fn flush_leaf_outbox( pub fn flush_session_family( endpoint: &mut Endpoint, leaf_id: u32, - family: &mut SessionFamily, + family: &mut SessionFamily, interface: &mut Option<&mut InterfaceStore>, ) where S: Session, diff --git a/src/protocol/session.rs b/src/protocol/session.rs index 0889226..e0e9f22 100644 --- a/src/protocol/session.rs +++ b/src/protocol/session.rs @@ -9,26 +9,25 @@ use crate::interface::SessionView; /// /// A session family maps one outer `procedure_id` to many live hook instances. The /// generated leaf owns packet grouping, retry-safe output flushing, and final cleanup; -/// the session implementation owns only application behavior. +/// the session value owns one hook's application behavior and mutable state. /// /// # Example /// /// ```rust,ignore -/// impl Session for MySession { +/// impl Session for MySessionState { /// const PROCEDURE_ID: u32 = 7; -/// type State = MySessionState; /// /// fn init( /// leaf: &mut MyLeafState, /// packet: Packet, /// ctx: &mut SessionInit, -/// ) -> Result { +/// ) -> Result { /// Ok(MySessionState::from_open(leaf, packet, ctx)) /// } /// /// fn update( /// leaf: &mut MyLeafState, -/// session: &mut Self::State, +/// session: &mut Self, /// incoming: &mut PacketQueue, /// ctx: &mut SessionCtx<'_>, /// ) -> SessionStatus { @@ -39,23 +38,16 @@ use crate::interface::SessionView; /// } /// } /// ``` -pub trait Session { +pub trait Session: Sized { /// Outer packet procedure id used by every packet in this session family. const PROCEDURE_ID: u32; - /// Application state stored for one live hook. - type State; - - /// Creates one session state from a packet whose hook has no active session. + /// Creates one session value from a packet whose hook has no active session. /// /// The generated runtime derives all response routing from hook state. Session /// initialization therefore returns only application state or a protocol-level /// rejection; it never stores or receives a caller reply path. - fn init( - leaf: &mut L, - packet: Packet, - ctx: &mut SessionInit, - ) -> Result; + fn init(leaf: &mut L, packet: Packet, ctx: &mut SessionInit) -> Result; /// Advances one active hook session. /// @@ -65,7 +57,7 @@ pub trait Session { /// generated retry rules. fn update( leaf: &mut L, - session: &mut Self::State, + session: &mut Self, incoming: &mut PacketQueue, ctx: &mut SessionCtx<'_>, ) -> SessionStatus; @@ -73,7 +65,7 @@ pub trait Session { #[cfg(feature = "interface_ratatui")] fn render_ratatui( _: &L, - _: &Self::State, + _: &Self, _: &mut SessionView, _: &mut ratatui::Frame<'_>, _: ratatui::layout::Rect,