返回 CodeWhale
session_group.rs
根目录 / crates / tui / src / commands / session_group.rs
1 //! Host registration/action adapter for the portable session group. Replaces
2 //! groups/session/mod.rs's concrete-App dispatcher; the existing central
3 //! dispatcher still constructs the declared envelope and consumes the result.
4
5 use super::CommandResult;
6 use super::groups::session::*;
7 use super::traits::{Command, CommandGroup, ContextualCommand};
8 use crate::tui::app::AppAction;
9 use codewhale_command_contract::handler::CommandHandler;
10 use codewhale_command_contract::metadata::{CommandInfo, RegisterCommand};
11 use codewhale_command_contract::outcome::{SessionAction, SessionCommandResult};
12 use std::marker::PhantomData;
13
14 pub(super) struct SessionCommands;
15
16 struct HostRegistration<C>(PhantomData<C>);
17 impl<C: RegisterCommand<SessionCommandResult>> RegisterCommand<CommandResult>
18 for HostRegistration<C>
19 {
20 fn info() -> &'static CommandInfo {
21 C::info()
22 }
23 fn handler() -> CommandHandler<CommandResult> {
24 match C::handler() {
25 CommandHandler::Pure(_) => CommandHandler::Pure(|args| match C::handler() {
26 CommandHandler::Pure(run) => host_result(run(args)),
27 _ => CommandResult::error("command handler shape changed"),
28 }),
29 CommandHandler::Contextual { capabilities, .. } => CommandHandler::Contextual {
30 capabilities,
31 handler: |contexts, args| match C::handler() {
32 CommandHandler::Contextual { handler, .. } => {
33 host_result(handler(contexts, args))
34 }
35 _ => CommandResult::error("command handler shape changed"),
36 },
37 },
38 }
39 }
40 }
41
42 impl CommandGroup for SessionCommands {
43 fn commands(&self) -> &'static [Box<dyn Command>] {
44 static COMMANDS: std::sync::OnceLock<Vec<Box<dyn Command>>> = std::sync::OnceLock::new();
45 COMMANDS.get_or_init(|| {
46 let commands: Vec<Box<dyn Command>> = vec![
47 Box::new(ContextualCommand::from_contract::<HostRegistration<rename::RenameCmd>>().expect("session registration")),
48 Box::new(ContextualCommand::from_contract::<HostRegistration<title::TitleCmd>>().expect("session registration")),
49 Box::new(ContextualCommand::from_contract::<HostRegistration<save::SaveCmd>>().expect("session registration")),
50 Box::new(ContextualCommand::from_contract::<HostRegistration<fork::ForkCmd>>().expect("session registration")),
51 Box::new(ContextualCommand::from_contract::<HostRegistration<new::NewCmd>>().expect("session registration")),
52 Box::new(ContextualCommand::from_contract::<HostRegistration<sessions::SessionsCmd>>().expect("session registration")),
53 Box::new(ContextualCommand::from_contract::<HostRegistration<load::LoadCmd>>().expect("session registration")),
54 Box::new(ContextualCommand::from_contract::<HostRegistration<resume::ResumeCmd>>().expect("session registration")),
55 Box::new(ContextualCommand::from_contract::<HostRegistration<tree::TreeCmd>>().expect("session registration")),
56 Box::new(ContextualCommand::from_contract::<HostRegistration<branch::BranchCmd>>().expect("session registration")),
57 Box::new(ContextualCommand::from_contract::<HostRegistration<compact::CompactCmd>>().expect("session registration")),
58 Box::new(ContextualCommand::from_contract::<HostRegistration<purge::PurgeCmd>>().expect("session registration")),
59 Box::new(ContextualCommand::from_contract::<HostRegistration<relay::RelayCmd>>().expect("session registration")),
60 Box::new(ContextualCommand::from_contract::<HostRegistration<remote_control::RemoteControlCmd>>().expect("session registration")),
61 Box::new(ContextualCommand::from_contract::<HostRegistration<remote_env::RemoteEnvCmd>>().expect("session registration")),
62 Box::new(ContextualCommand::from_contract::<HostRegistration<export::ExportCmd>>().expect("session registration")),
63 Box::new(ContextualCommand::from_contract::<HostRegistration<StructcopyRegistration>>().expect("structcopy registration")),
64 ];
65 assert_eq!(commands.iter().map(|command| command.info().name).collect::<Vec<_>>(),
66 portable_handlers().iter().map(|(info, _)| info.name).collect::<Vec<_>>(),
67 "host registry must cover the complete portable session inventory");
68 commands
69 }).as_slice()
70 }
71 }
72
73 pub(in crate::commands) fn host_result(result: SessionCommandResult) -> CommandResult {
74 let action = result.action.map(|action| match action {
75 SessionAction::CompactContext { focus } => AppAction::CompactContext { focus },
76 SessionAction::PurgeContext => AppAction::PurgeContext,
77 SessionAction::LoadSession(path) => AppAction::LoadSession(path),
78 SessionAction::SendMessage(message) => AppAction::SendMessage(message),
79 SessionAction::OpenExternalUrl { url, label } => AppAction::OpenExternalUrl { url, label },
80 SessionAction::RemoteControl(action) => AppAction::RemoteControl(match action {
81 codewhale_command_contract::outcome::SessionRemoteControlAction::Start => {
82 crate::remote_control::RemoteControlAction::Start
83 }
84 codewhale_command_contract::outcome::SessionRemoteControlAction::Stop => {
85 crate::remote_control::RemoteControlAction::Stop
86 }
87 }),
88 SessionAction::SyncSession(sync) => AppAction::SyncSession {
89 session_id: sync.session_id,
90 messages: sync.messages,
91 system_prompt: sync.system_prompt,
92 model: sync.model,
93 workspace: sync.workspace,
94 mode: super::contract::from_command_mode(sync.mode),
95 },
96 });
97 CommandResult {
98 message: result.message,
99 action,
100 is_error: result.is_error,
101 }
102 }
103
103 lines RUST