vta_cli_common/commands/
mediator.rs1use vta_sdk::client::VtaClient;
9use vta_sdk::protocol::{DrainCancelRequest, MigrateMediatorRequest};
10
11pub async fn cmd_mediator_migrate(
14 client: &VtaClient,
15 new_mediator_did: String,
16 drain_ttl_secs: u64,
17 force: bool,
18 handshake_timeout_secs: Option<u64>,
19) -> Result<(), Box<dyn std::error::Error>> {
20 run_migrate(
21 client,
22 new_mediator_did,
23 drain_ttl_secs,
24 force,
25 handshake_timeout_secs,
26 false,
27 )
28 .await
29}
30
31pub async fn cmd_mediator_rollback(
39 client: &VtaClient,
40 target_mediator_did: String,
41 drain_ttl_secs: u64,
42 force: bool,
43 handshake_timeout_secs: Option<u64>,
44) -> Result<(), Box<dyn std::error::Error>> {
45 run_migrate(
46 client,
47 target_mediator_did,
48 drain_ttl_secs,
49 force,
50 handshake_timeout_secs,
51 true,
52 )
53 .await
54}
55
56async fn run_migrate(
57 client: &VtaClient,
58 new_mediator_did: String,
59 drain_ttl_secs: u64,
60 force: bool,
61 handshake_timeout_secs: Option<u64>,
62 rollback: bool,
63) -> Result<(), Box<dyn std::error::Error>> {
64 let mut req = MigrateMediatorRequest::new(&new_mediator_did, drain_ttl_secs);
65 req.force = force;
66 req.handshake_timeout_secs = handshake_timeout_secs;
67 req.rollback = rollback;
68
69 let resp = client
70 .migrate_mediator(req)
71 .await
72 .map_err(|e| format!("{e}"))?;
73
74 let verb = if rollback { "rolled back" } else { "migrated" };
75 println!("Mediator {verb}.");
76 println!(" Prior mediator: {}", resp.prior_mediator_did);
77 println!(" Active mediator: {}", resp.active_mediator_did);
78 if !resp.active_mediator_endpoint.is_empty() {
79 println!(" Active endpoint: {}", resp.active_mediator_endpoint);
80 }
81 println!(" New version ID: {}", resp.new_version_id);
82 println!(
83 " Drain deadline: {} (prior listener stays up until then)",
84 resp.drains_until
85 );
86 if force {
87 println!();
88 println!(" Note: --force was set; mediator handshake steps 2-5 were bypassed.");
89 }
90 Ok(())
91}
92
93pub async fn cmd_mediator_drain_cancel(
95 client: &VtaClient,
96 mediator_did: String,
97) -> Result<(), Box<dyn std::error::Error>> {
98 let req = DrainCancelRequest { mediator_did };
99 let resp = client.drain_cancel(req).await.map_err(|e| format!("{e}"))?;
100 println!("Drain cancelled for {}.", resp.mediator_did);
101 println!(" Listener was torn down immediately.");
102 Ok(())
103}
104
105pub async fn cmd_mediator_report(
108 client: &VtaClient,
109 since: Option<String>,
110 until: Option<String>,
111 format: ReportFormat,
112) -> Result<(), Box<dyn std::error::Error>> {
113 let report = client
114 .mediator_report(since.as_deref(), until.as_deref())
115 .await
116 .map_err(|e| format!("{e}"))?;
117
118 match format {
119 ReportFormat::Json => {
120 println!("{}", serde_json::to_string_pretty(&report)?);
121 }
122 ReportFormat::Table => {
123 println!("Mediator report");
124 if let Some(ref s) = report.since {
125 println!(" Window: {s} → {}", report.until);
126 } else {
127 println!(" Window: (all time) → {}", report.until);
128 }
129 println!();
130 if report.mediators.is_empty() {
131 println!(" No inbound DIDComm messages recorded.");
132 } else {
133 println!(" Per-mediator inbound counts (most recent first):");
134 let header_did = "MEDIATOR DID";
135 let header_count = "INBOUND";
136 println!(" {header_did:<60} {header_count:>10} LAST SEEN");
137 for m in &report.mediators {
138 println!(
139 " {:<60} {:>10} {}",
140 truncate(&m.mediator_did, 60),
141 m.inbound_count,
142 m.last_seen
143 );
144 }
145 }
146 if !report.senders.is_empty() {
147 println!();
148 println!(" Senders by last-seen mediator:");
149 for s in &report.senders {
150 println!(
151 " {} → {} (at {})",
152 truncate(&s.sender_did, 50),
153 truncate(&s.last_seen_mediator, 50),
154 s.last_seen_at
155 );
156 }
157 }
158 }
159 }
160 Ok(())
161}
162
163#[derive(Debug, Clone, Copy)]
164pub enum ReportFormat {
165 Json,
166 Table,
167}
168
169impl std::str::FromStr for ReportFormat {
170 type Err = String;
171 fn from_str(s: &str) -> Result<Self, Self::Err> {
172 match s {
173 "json" => Ok(Self::Json),
174 "table" => Ok(Self::Table),
175 other => Err(format!("unknown format `{other}` — use `json` or `table`")),
176 }
177 }
178}
179
180fn truncate(s: &str, max: usize) -> String {
181 if s.chars().count() <= max {
182 s.to_string()
183 } else {
184 let mut out: String = s.chars().take(max - 1).collect();
185 out.push('…');
186 out
187 }
188}