1use anyhow::{Result, anyhow};
2use serde::{Deserialize, Serialize};
3use std::path::Path;
4use tokio::io::{AsyncReadExt, AsyncWriteExt};
5use tokio::net::UnixStream;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(tag = "command", rename_all = "snake_case")]
9pub enum ControlRequest {
10 RuntimeStatus,
11 StageProtocol {
12 #[serde(default, skip_serializing_if = "Option::is_none")]
13 asset: Option<String>,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 custom_asset: Option<String>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 asset_name: Option<String>,
18 protocol_version: String,
19 activation_point: u64,
20 #[serde(default, skip_serializing_if = "Vec::is_empty")]
21 chainspec_overrides: Vec<String>,
22 restart_sidecars: bool,
23 rust_log: Option<String>,
24 },
25 AddNodes {
26 count: u32,
27 },
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(tag = "status", rename_all = "snake_case")]
32pub enum ControlResponse {
33 Ok { result: ControlResult },
34 Error { error: String },
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(tag = "command", rename_all = "snake_case")]
39pub enum ControlResult {
40 RuntimeStatus {
41 running_node_ids: Vec<u32>,
42 last_block_height: Option<u64>,
43 },
44 StageProtocol {
45 live_mode: bool,
46 staged_nodes: u32,
47 restarted_sidecars: Vec<u32>,
48 },
49 AddNodes {
50 added_node_ids: Vec<u32>,
51 total_nodes: u32,
52 started_processes: u32,
53 },
54}
55
56pub async fn send_request(socket_path: &Path, request: &ControlRequest) -> Result<ControlResponse> {
57 let mut stream = UnixStream::connect(socket_path).await?;
58 let request_bytes = serde_json::to_vec(request)?;
59 stream.write_all(&request_bytes).await?;
60 stream.shutdown().await?;
61
62 let mut response_bytes = Vec::new();
63 stream.read_to_end(&mut response_bytes).await?;
64 if response_bytes.is_empty() {
65 return Err(anyhow!(
66 "empty control response from {}",
67 socket_path.display()
68 ));
69 }
70
71 let response = serde_json::from_slice::<ControlResponse>(&response_bytes)?;
72 Ok(response)
73}