| 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 |