use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheView {
pub cap_bytes: u64,
pub used_bytes: u64,
pub dir: String,
pub shared: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SyncAvailability {
pub available: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusResult {
pub running: bool,
pub service: String,
pub version: String,
pub commit: String,
pub protocol: String,
pub uptime_secs: u64,
pub addr: String,
pub upstream: String,
pub cache: CacheView,
pub hosted_store_count: u64,
pub cached_capsule_count: u64,
pub pinned_store_count: u64,
pub sync: SyncAvailability,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigResult {
pub addr: String,
pub port: String,
pub upstream: String,
pub upstream_override: Option<String>,
pub cache_dir: String,
pub cache_shared: bool,
pub config_path: String,
pub sync_available: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SetUpstreamResult {
pub upstream: String,
pub requires_restart: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SetLevelResult {
pub filter: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SetCapResult {
pub cap_bytes: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheClearResult {
pub cleared: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CapsuleEntry {
pub capsule: String,
pub root: String,
pub size_bytes: u64,
pub last_used_unix_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HostedStore {
pub store_id: String,
pub pinned: bool,
pub capsule_count: u64,
pub total_bytes: u64,
pub capsules: Vec<CapsuleEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HostedStoresListResult {
pub stores: Vec<HostedStore>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PinResult {
pub store_id: String,
pub root: Option<String>,
pub pinned: bool,
pub fetch: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnpinResult {
pub store_id: String,
pub unpinned: bool,
pub evicted_capsules: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HostedStoreStatusResult {
pub store_id: String,
pub pinned: bool,
pub capsule_count: u64,
pub total_bytes: u64,
pub capsules: Vec<CapsuleEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SyncStatusResult {
pub available: bool,
pub method: String,
pub pinned_total: u64,
pub pinned_synced: u64,
pub whole_store_trigger_supported: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SyncTriggerResult {
pub store_id: String,
pub root: String,
pub status: String,
pub size_bytes: u64,
pub served_root: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PairingApproveResult {
pub approved: bool,
pub client_name: String,
pub token_id: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PairingRevokeResult {
pub revoked: bool,
pub token_id: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PeersConnectResult {
pub connected: bool,
pub peer_id: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PeersDisconnectResult {
pub disconnected: bool,
pub peer_id: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SubscribeResult {
pub subscribed: bool,
pub added: bool,
pub store_id: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnsubscribeResult {
pub subscribed: bool,
pub removed: bool,
pub store_id: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListSubscriptionsResult {
pub subscriptions: Vec<String>,
pub count: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PairingRequestResult {
pub pairing_id: String,
pub pairing_code: String,
pub expires_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PairingPollResult {
pub status: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub token: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn status_result_round_trips_the_node_shape() {
let v = json!({
"running": true, "service": "dig-node", "version": "0.30.0", "commit": "abc",
"protocol": "21", "uptime_secs": 5, "addr": "127.0.0.1:9256", "upstream": "https://rpc.dig.net",
"cache": {"cap_bytes": 1024, "used_bytes": 10, "dir": "/c", "shared": false},
"hosted_store_count": 2, "cached_capsule_count": 3, "pinned_store_count": 1,
"sync": {"available": true}
});
let parsed: StatusResult = serde_json::from_value(v.clone()).unwrap();
assert_eq!(serde_json::to_value(&parsed).unwrap(), v);
}
#[test]
fn config_result_keeps_upstream_override_null_when_unset() {
let parsed = ConfigResult {
addr: "127.0.0.1:9256".into(),
port: "9256".into(),
upstream: "https://rpc.dig.net".into(),
upstream_override: None,
cache_dir: "/c".into(),
cache_shared: false,
config_path: "/c/config.json".into(),
sync_available: true,
};
let v = serde_json::to_value(&parsed).unwrap();
assert_eq!(v["upstream_override"], json!(null));
assert!(v.as_object().unwrap().contains_key("upstream_override"));
}
#[test]
fn pairing_poll_omits_token_until_approved() {
let pending = PairingPollResult {
status: "pending".into(),
token: None,
};
let v = serde_json::to_value(&pending).unwrap();
assert_eq!(v, json!({"status": "pending"}));
let approved = PairingPollResult {
status: "approved".into(),
token: Some("deadbeef".into()),
};
assert_eq!(
serde_json::to_value(&approved).unwrap(),
json!({"status": "approved", "token": "deadbeef"})
);
}
}