use vta_sdk::client::VtaClient;
use vta_sdk::protocol::{DrainCancelRequest, MigrateMediatorRequest};
pub async fn cmd_mediator_migrate(
client: &VtaClient,
new_mediator_did: String,
drain_ttl_secs: u64,
force: bool,
handshake_timeout_secs: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
run_migrate(
client,
new_mediator_did,
drain_ttl_secs,
force,
handshake_timeout_secs,
false,
)
.await
}
pub async fn cmd_mediator_rollback(
client: &VtaClient,
target_mediator_did: String,
drain_ttl_secs: u64,
force: bool,
handshake_timeout_secs: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
run_migrate(
client,
target_mediator_did,
drain_ttl_secs,
force,
handshake_timeout_secs,
true,
)
.await
}
async fn run_migrate(
client: &VtaClient,
new_mediator_did: String,
drain_ttl_secs: u64,
force: bool,
handshake_timeout_secs: Option<u64>,
rollback: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let mut req = MigrateMediatorRequest::new(&new_mediator_did, drain_ttl_secs);
req.force = force;
req.handshake_timeout_secs = handshake_timeout_secs;
req.rollback = rollback;
let resp = client
.migrate_mediator(req)
.await
.map_err(|e| format!("{e}"))?;
let verb = if rollback { "rolled back" } else { "migrated" };
println!("Mediator {verb}.");
println!(" Prior mediator: {}", resp.prior_mediator_did);
println!(" Active mediator: {}", resp.active_mediator_did);
if !resp.active_mediator_endpoint.is_empty() {
println!(" Active endpoint: {}", resp.active_mediator_endpoint);
}
println!(" New version ID: {}", resp.new_version_id);
println!(
" Drain deadline: {} (prior listener stays up until then)",
resp.drains_until
);
if force {
println!();
println!(" Note: --force was set; mediator handshake steps 2-5 were bypassed.");
}
Ok(())
}
pub async fn cmd_mediator_drain_cancel(
client: &VtaClient,
mediator_did: String,
) -> Result<(), Box<dyn std::error::Error>> {
let req = DrainCancelRequest { mediator_did };
let resp = client.drain_cancel(req).await.map_err(|e| format!("{e}"))?;
println!("Drain cancelled for {}.", resp.mediator_did);
println!(" Listener was torn down immediately.");
Ok(())
}
pub async fn cmd_mediator_report(
client: &VtaClient,
since: Option<String>,
until: Option<String>,
format: ReportFormat,
) -> Result<(), Box<dyn std::error::Error>> {
let report = client
.mediator_report(since.as_deref(), until.as_deref())
.await
.map_err(|e| format!("{e}"))?;
match format {
ReportFormat::Json => {
println!("{}", serde_json::to_string_pretty(&report)?);
}
ReportFormat::Table => {
println!("Mediator report");
if let Some(ref s) = report.since {
println!(" Window: {s} → {}", report.until);
} else {
println!(" Window: (all time) → {}", report.until);
}
println!();
if report.mediators.is_empty() {
println!(" No inbound DIDComm messages recorded.");
} else {
println!(" Per-mediator inbound counts (most recent first):");
let header_did = "MEDIATOR DID";
let header_count = "INBOUND";
println!(" {header_did:<60} {header_count:>10} LAST SEEN");
for m in &report.mediators {
println!(
" {:<60} {:>10} {}",
truncate(&m.mediator_did, 60),
m.inbound_count,
m.last_seen
);
}
}
if !report.senders.is_empty() {
println!();
println!(" Senders by last-seen mediator:");
for s in &report.senders {
println!(
" {} → {} (at {})",
truncate(&s.sender_did, 50),
truncate(&s.last_seen_mediator, 50),
s.last_seen_at
);
}
}
}
}
Ok(())
}
#[derive(Debug, Clone, Copy)]
pub enum ReportFormat {
Json,
Table,
}
impl std::str::FromStr for ReportFormat {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"json" => Ok(Self::Json),
"table" => Ok(Self::Table),
other => Err(format!("unknown format `{other}` — use `json` or `table`")),
}
}
}
fn truncate(s: &str, max: usize) -> String {
if s.chars().count() <= max {
s.to_string()
} else {
let mut out: String = s.chars().take(max - 1).collect();
out.push('…');
out
}
}