use super::InfraContext;
use anyhow::{bail, Context, Result};
use std::process::Command;
#[derive(Debug, Clone)]
pub struct SnapshotSummary {
pub version: u8,
pub chain_hash: String,
pub timestamp: u32,
pub timestamp_str: String,
pub node_count: usize,
pub channel_count: usize,
pub update_count: usize,
}
pub fn fetch_snapshot_summary(
ctx: &InfraContext,
snapshot_path: Option<&str>,
) -> Result<SnapshotSummary> {
let container = super::ops::rgs_container_name(ctx);
let path = snapshot_path.unwrap_or("/res/symlinks/0.bin");
let out = Command::new("docker")
.args(["exec", &container, "cat", path])
.output()
.context("docker exec cat snapshot")?;
if !out.status.success() {
let stderr = String::from_utf8_lossy(&out.stderr);
bail!("docker exec failed: {}", stderr.trim());
}
let data = &out.stdout;
parse_snapshot_header(data)
}
fn parse_snapshot_header(data: &[u8]) -> Result<SnapshotSummary> {
if data.len() < 48 {
bail!("snapshot too short ({} bytes)", data.len());
}
if &data[0..4] != b"RLGS" {
bail!(
"invalid snapshot magic: {:?}",
&data[..4.min(data.len())]
);
}
let version = data[4];
let chain_hash = hex_encode(&data[5..37]);
let timestamp = u32::from_be_bytes([data[37], data[38], data[39], data[40]]);
let timestamp_str = format_unix_timestamp(timestamp);
let node_count = if data.len() >= 44 {
((data[41] as usize) << 16) | ((data[42] as usize) << 8) | (data[43] as usize)
} else {
0
};
let channel_count = if data.len() >= 48 {
u32::from_be_bytes([data[44], data[45], data[46], data[47]]) as usize
} else {
0
};
let update_count = data.len().saturating_sub(48) / 136;
Ok(SnapshotSummary {
version,
chain_hash,
timestamp,
timestamp_str,
node_count,
channel_count,
update_count,
})
}
fn hex_encode(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
fn format_unix_timestamp(ts: u32) -> String {
format!("unix:{}", ts)
}