mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-08 22:38:01 -06:00
Support module-inferred leaf hosts
This commit is contained in:
@@ -6,12 +6,12 @@ description = "Application-layer UnShell leaves and client surfaces"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
leaf_endpoint = ["dep:portable-pty"]
|
||||
leaf_endpoint = []
|
||||
leaf_tui = []
|
||||
|
||||
[dependencies]
|
||||
rkyv = { workspace = true }
|
||||
portable-pty = { workspace = true, optional = true }
|
||||
portable-pty = { workspace = true }
|
||||
unshell-macros = { workspace = true }
|
||||
unshell-protocol = { workspace = true }
|
||||
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user