feat: add peer pod handshake command

This commit is contained in:
2026-06-02 19:43:20 +09:00
parent b51c32401c
commit 093a84fc83
12 changed files with 663 additions and 14 deletions
+16
View File
@@ -1204,6 +1204,22 @@ impl App {
});
}
Event::PodsListed { .. } | Event::PodRestored { .. } => {}
Event::PeerRegistered { result } => {
let source = result
.get("source")
.and_then(serde_json::Value::as_str)
.unwrap_or("this Pod");
let peer = result
.get("peer")
.and_then(serde_json::Value::as_str)
.unwrap_or("peer Pod");
self.flash_actionbar_notice(
format!("Peer handshake registered: `{source}` ↔ `{peer}`"),
ActionbarNoticeLevel::Info,
ActionbarNoticeSource::Tui,
Duration::from_secs(4),
);
}
Event::Shutdown => {
self.mark_orphan_compacts_incomplete();
self.quit = true;
+82
View File
@@ -156,6 +156,15 @@ impl CommandRegistry {
can_execute: rewind_available,
executor: rewind_command,
});
registry.register(CommandSpec {
name: "peer",
aliases: &[],
usage: "peer <pod-name>",
description: "Make another existing Pod visible as a reciprocal peer.",
argument_parser: peer_args,
can_execute: peer_available,
executor: peer_command,
});
registry
}
@@ -302,6 +311,17 @@ fn rewind_args(raw: &str) -> Result<CommandArgs, CommandDiagnostic> {
}
}
fn peer_args(raw: &str) -> Result<CommandArgs, CommandDiagnostic> {
let args = CommandArgs::parse_whitespace(raw);
if args.argv().len() == 1 {
Ok(args)
} else {
Err(CommandDiagnostic::new(
"Invalid arguments. Usage: peer <pod-name>",
))
}
}
fn compact_available(environment: &CommandEnvironment) -> Result<(), CommandDiagnostic> {
if !environment.connected {
return Err(CommandDiagnostic::new(
@@ -335,6 +355,20 @@ fn rewind_available(environment: &CommandEnvironment) -> Result<(), CommandDiagn
Ok(())
}
fn peer_available(environment: &CommandEnvironment) -> Result<(), CommandDiagnostic> {
if !environment.connected {
return Err(CommandDiagnostic::new(
"Cannot register a peer before the Pod is connected.",
));
}
if environment.running {
return Err(CommandDiagnostic::new(
"Cannot register a peer while the Pod is running.",
));
}
Ok(())
}
fn help_command(invocation: CommandInvocation<'_>) -> CommandExecution {
if let Some(name) = invocation.args.argv().first() {
let Some(command) = invocation.registry.find(name) else {
@@ -394,6 +428,20 @@ fn rewind_command(invocation: CommandInvocation<'_>) -> CommandExecution {
}
}
fn peer_command(invocation: CommandInvocation<'_>) -> CommandExecution {
let _ = invocation.command;
let _ = invocation.environment;
let name = invocation.args.argv()[0].clone();
CommandExecution {
method: Some(Method::RegisterPeer { name: name.clone() }),
diagnostics: vec![CommandDiagnostic::new(format!(
"peer handshake requested with `{name}`"
))],
exit_command_mode: true,
clear_input: true,
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -501,4 +549,38 @@ mod tests {
let result = registry.dispatch("rewind", &paused);
assert!(matches!(result.method, Some(Method::ListRewindTargets)));
}
#[test]
fn peer_command_returns_register_peer_method() {
let registry = CommandRegistry::builtins();
let result = registry.dispatch("peer reviewer", &env());
assert!(matches!(
result.method,
Some(Method::RegisterPeer { ref name }) if name == "reviewer"
));
assert!(result.exit_command_mode);
assert!(result.clear_input);
assert!(result.diagnostics[0].message.contains("peer handshake"));
}
#[test]
fn peer_invalid_arguments_are_local_diagnostic() {
let registry = CommandRegistry::builtins();
for command in ["peer", "peer one two"] {
let result = registry.dispatch(command, &env());
assert!(result.method.is_none());
assert!(!result.exit_command_mode);
assert!(result.diagnostics[0].message.contains("Invalid arguments"));
}
}
#[test]
fn peer_rejects_running() {
let registry = CommandRegistry::builtins();
let mut running = env();
running.running = true;
let result = registry.dispatch("peer reviewer", &running);
assert!(result.method.is_none());
assert!(result.diagnostics[0].message.contains("running"));
}
}