Support module-inferred leaf hosts

This commit is contained in:
Michael Mikovsky
2026-04-26 15:19:33 -06:00
parent 54c44b407e
commit f16be8d64a
15 changed files with 275 additions and 267 deletions
+8 -11
View File
@@ -20,27 +20,27 @@ use super::OpenRequest;
/// caller-owned hook identity. That makes ownership and cleanup of hook-backed
/// shell processes easy to inspect during debugging.
#[derive(Default)]
pub struct RemoteShellEndpoint {
pub struct RemoteShell {
sessions: BTreeMap<HookKey, Open>,
}
impl ProcedureStore<Open> for RemoteShellEndpoint {
impl ProcedureStore<Open> for RemoteShell {
fn procedure_sessions(&mut self) -> &mut BTreeMap<HookKey, Open> {
&mut self.sessions
}
}
impl Procedure<RemoteShellEndpoint> for Open {
impl Procedure<RemoteShell> for Open {
type Error = ShellLeafError;
type Input = OpenRequest;
fn open(_leaf: &mut RemoteShellEndpoint, call: Call<Self::Input>) -> Result<Self, Self::Error> {
fn open(_leaf: &mut RemoteShell, call: Call<Self::Input>) -> Result<Self, Self::Error> {
let hook_key = call.response_hook.ok_or(ShellLeafError::MissingHook)?;
Open::spawn(hook_key.return_path, hook_key.hook_id, call.procedure_id)
}
fn on_data(
_leaf: &mut RemoteShellEndpoint,
_leaf: &mut RemoteShell,
session: &mut Self,
data: unshell::protocol::tree::IncomingData,
) -> Result<ProcedureEffect, Self::Error> {
@@ -48,21 +48,18 @@ impl Procedure<RemoteShellEndpoint> for Open {
}
fn on_fault(
_leaf: &mut RemoteShellEndpoint,
_leaf: &mut RemoteShell,
_session: &mut Self,
_fault: unshell::protocol::tree::IncomingFault,
) -> Result<(), Self::Error> {
Ok(())
}
fn poll(
_leaf: &mut RemoteShellEndpoint,
session: &mut Self,
) -> Result<ProcedureEffect, Self::Error> {
fn poll(_leaf: &mut RemoteShell, session: &mut Self) -> Result<ProcedureEffect, Self::Error> {
session.poll()
}
fn close(_leaf: &mut RemoteShellEndpoint, mut session: Self) -> Result<(), Self::Error> {
fn close(_leaf: &mut RemoteShell, mut session: Self) -> Result<(), Self::Error> {
session.terminate()
}
}
@@ -14,7 +14,7 @@ use portable_pty::{CommandBuilder, ExitStatus, PtySize, native_pty_system};
use unshell::Procedure;
use unshell::protocol::tree::{IncomingData, OutgoingData, ProcedureEffect};
use super::RemoteShellEndpoint;
use super::RemoteShell;
use super::errors::ShellLeafError;
/// Per-hook shell session created by the `open` procedure.
@@ -22,7 +22,7 @@ use super::errors::ShellLeafError;
/// The procedure type is also the stored session type so the mapping between
/// one opening procedure and one live hook remains direct and visible.
#[derive(Procedure)]
#[procedure(leaf = RemoteShellEndpoint, name = "open")]
#[procedure(leaf = RemoteShell, name = "open")]
pub struct Open {
/// Spawned PTY child process.
pub(super) child: Box<dyn portable_pty::Child + Send>,
+11 -67
View File
@@ -1,48 +1,14 @@
//! Remote shell leaf and its user-facing surfaces.
//!
//! The module always exports the protocol contract for the leaf. Role-specific
//! implementations live behind crate-wide features:
//! - `leaf_endpoint` builds the PTY-backed runtime leaf
//! - `leaf_tui` builds a placeholder client-side TUI surface
//! The module always exports the protocol contract for the leaf together with the
//! endpoint and TUI host implementations.
use rkyv::{Archive, Deserialize, Serialize};
#[cfg(not(feature = "leaf_endpoint"))]
use std::string::String;
use unshell_macros::leaf;
#[cfg(feature = "leaf_endpoint")]
pub mod endpoint;
#[cfg(feature = "leaf_tui")]
pub mod tui;
#[cfg(feature = "leaf_endpoint")]
pub use endpoint::Open;
#[cfg(feature = "leaf_endpoint")]
pub use endpoint::RemoteShellEndpoint;
#[cfg(feature = "leaf_tui")]
pub use tui::RemoteShellTui;
#[cfg(not(feature = "leaf_endpoint"))]
/// Compile-time procedure symbol kept available even when the endpoint runtime is
/// not built, so the leaf declaration still validates its declared inventory.
pub struct Open;
#[cfg(not(feature = "leaf_endpoint"))]
#[doc(hidden)]
pub struct RemoteShellDeclarationPlaceholder;
#[cfg(not(feature = "leaf_endpoint"))]
impl crate::protocol::tree::ProtocolLeaf for RemoteShellDeclarationPlaceholder {
fn leaf_name() -> String {
String::from("remote_shell")
}
}
#[cfg(not(feature = "leaf_endpoint"))]
impl crate::protocol::tree::ProcedureMetadata for Open {
type Leaf = RemoteShellDeclarationPlaceholder;
const PROCEDURE_SUFFIX: &'static str = "open";
}
/// Open-request payload for the remote shell leaf.
///
/// The shell currently needs no structured arguments, but a named payload type is
@@ -50,33 +16,11 @@ impl crate::protocol::tree::ProcedureMetadata for Open {
#[derive(Archive, Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq)]
pub struct OpenRequest;
#[cfg(any(feature = "leaf_endpoint", feature = "leaf_tui"))]
macro_rules! declare_remote_shell_leaf {
($($role_args:tt)*) => {
crate::leaf! {
name = "remote_shell",
procedures = [Open],
$($role_args)*
}
};
}
#[cfg(all(feature = "leaf_endpoint", not(feature = "leaf_tui")))]
declare_remote_shell_leaf!(endpoint_struct = RemoteShellEndpoint,);
#[cfg(all(not(feature = "leaf_endpoint"), feature = "leaf_tui"))]
declare_remote_shell_leaf!(tui_struct = RemoteShellTui,);
#[cfg(all(feature = "leaf_endpoint", feature = "leaf_tui"))]
declare_remote_shell_leaf!(
endpoint_struct = RemoteShellEndpoint,
tui_struct = RemoteShellTui,
);
crate::role_leaf! {
/// Feature-selected remote shell surface.
pub type RemoteShell {
endpoint => endpoint::RemoteShellEndpoint,
tui => tui::RemoteShellTui,
}
}
#[leaf(
name = "remote_shell",
procedures = [Open],
endpoint = endpoint,
tui = tui,
)]
/// Shared compile-time declaration for the `remote_shell` leaf surface.
pub struct RemoteShell;
+10 -3
View File
@@ -8,23 +8,24 @@ use std::string::String;
use std::vec::Vec;
use unshell::protocol::DataMessage;
use unshell_macros::Procedure;
use crate::{LeafTui, TuiError};
/// Stub TUI surface for the remote shell leaf.
#[derive(Default)]
pub struct RemoteShellTui {
pub struct RemoteShell {
transcript: Vec<u8>,
}
impl RemoteShellTui {
impl RemoteShell {
/// Returns a short explanation of the current stub status.
pub fn status_line(&self) -> &'static str {
"remote shell TUI stub: rendering is placeholder-only for now"
}
}
impl LeafTui for RemoteShellTui {
impl LeafTui for RemoteShell {
fn leaf_name(&self) -> String {
Self::protocol_leaf_name()
}
@@ -39,3 +40,9 @@ impl LeafTui for RemoteShellTui {
format!("{}\n\n{}", self.status_line(), body)
}
}
/// TUI-side placeholder procedure symbol for the shared `remote_shell` leaf
/// declaration.
#[derive(Procedure)]
#[procedure(leaf = RemoteShell, name = "open")]
pub struct Open {}