commit b452c0cf6703b6d0f3a4f333fff125a9c343f41d
parent f1167c031bf5aee848d90ddb73bb4605423d839c
Author: triesap <triesap@radroots.dev>
Date: Sat, 3 Jan 2026 18:28:28 +0000
jsonrpc: replace relay responses with typed structs
- Add relays module with Serialize response types
- Return RelayAdded/Removed/Connect response structs instead of json! values
- Switch relays.list to return Vec<String> directly
- Default relays.status params and emit typed RelayStatusRow with optional nip11
Diffstat:
8 files changed, 82 insertions(+), 35 deletions(-)
diff --git a/src/api/jsonrpc/methods/relays/add.rs b/src/api/jsonrpc/methods/relays/add.rs
@@ -2,8 +2,8 @@ use anyhow::Result;
use jsonrpsee::server::RpcModule;
use radroots_nostr::prelude::radroots_nostr_add_relay;
use serde::Deserialize;
-use serde_json::{Value as JsonValue, json};
+use crate::api::jsonrpc::relays::RelayAddedResponse;
use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError};
#[derive(Debug, Deserialize)]
@@ -22,7 +22,7 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
.await
.map_err(|e| RpcError::AddRelay(url.clone(), e.to_string()))?;
- Ok::<JsonValue, RpcError>(json!({ "added": url }))
+ Ok::<RelayAddedResponse, RpcError>(RelayAddedResponse { added: url })
})?;
Ok(())
}
diff --git a/src/api/jsonrpc/methods/relays/connect.rs b/src/api/jsonrpc/methods/relays/connect.rs
@@ -1,7 +1,7 @@
use anyhow::Result;
use jsonrpsee::server::RpcModule;
-use serde_json::{Value as JsonValue, json};
+use crate::api::jsonrpc::relays::RelayConnectResponse;
use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError};
use radroots_nostr::prelude::{radroots_nostr_connect, RadrootsNostrRelayStatus};
@@ -32,12 +32,12 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
tokio::spawn(async move { radroots_nostr_connect(&client).await });
}
- Ok::<JsonValue, RpcError>(json!({
- "connected": connected,
- "connecting": connecting,
- "disconnected": disconnected,
- "spawned_connect": need_connect
- }))
+ Ok::<RelayConnectResponse, RpcError>(RelayConnectResponse {
+ connected,
+ connecting,
+ disconnected,
+ spawned_connect: need_connect,
+ })
})?;
Ok(())
}
diff --git a/src/api/jsonrpc/methods/relays/list.rs b/src/api/jsonrpc/methods/relays/list.rs
@@ -1,6 +1,5 @@
use anyhow::Result;
use jsonrpsee::server::RpcModule;
-use serde_json::{Value as JsonValue, json};
use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError};
@@ -8,9 +7,8 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
registry.track("relays.list");
m.register_async_method("relays.list", |_p, ctx, _| async move {
let relays = ctx.state.client.relays().await;
- Ok::<JsonValue, RpcError>(json!(
- relays.keys().map(|u| u.to_string()).collect::<Vec<_>>()
- ))
+ let out = relays.keys().map(|u| u.to_string()).collect::<Vec<_>>();
+ Ok::<Vec<String>, RpcError>(out)
})?;
Ok(())
}
diff --git a/src/api/jsonrpc/methods/relays/remove.rs b/src/api/jsonrpc/methods/relays/remove.rs
@@ -2,8 +2,8 @@ use anyhow::Result;
use jsonrpsee::server::RpcModule;
use radroots_nostr::prelude::radroots_nostr_remove_relay;
use serde::Deserialize;
-use serde_json::{Value as JsonValue, json};
+use crate::api::jsonrpc::relays::RelayRemovedResponse;
use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError};
#[derive(Debug, Deserialize)]
@@ -22,7 +22,7 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
.await
.map_err(|e| RpcError::Other(format!("failed to remove relay {url}: {e}")))?;
- Ok::<JsonValue, RpcError>(json!({ "removed": url }))
+ Ok::<RelayRemovedResponse, RpcError>(RelayRemovedResponse { removed: url })
})?;
Ok(())
}
diff --git a/src/api/jsonrpc/methods/relays/status.rs b/src/api/jsonrpc/methods/relays/status.rs
@@ -1,12 +1,12 @@
use anyhow::Result;
use jsonrpsee::server::RpcModule;
use serde::Deserialize;
-use serde_json::{Map as JsonMap, Value as JsonValue, json};
+use crate::api::jsonrpc::relays::RelayStatusRow;
use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError};
use radroots_nostr::prelude::fetch_nip11;
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Default, Deserialize)]
struct StatusParams {
#[serde(default)]
include_nip11: bool,
@@ -16,8 +16,9 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
registry.track("relays.status");
m.register_async_method("relays.status", |params, ctx, _| async move {
let StatusParams { include_nip11 } = params
- .parse()
- .map_err(|e| RpcError::InvalidParams(e.to_string()))?;
+ .parse::<Option<StatusParams>>()
+ .map_err(|e| RpcError::InvalidParams(e.to_string()))?
+ .unwrap_or_default();
let relays = ctx.state.client.relays().await;
let mut out = Vec::with_capacity(relays.len());
@@ -27,31 +28,37 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
let status_str = format!("{}", relay.status());
let parsed = reqwest::Url::parse(&url_str).ok();
- let mut row = JsonMap::new();
- row.insert("url".into(), json!(url_str));
- row.insert("status".into(), json!(status_str));
+ let mut row = RelayStatusRow {
+ url: url_str.clone(),
+ status: status_str,
+ scheme: None,
+ host: None,
+ onion: None,
+ port: None,
+ nip11: None,
+ };
if let Some(u) = &parsed {
- row.insert("scheme".into(), json!(u.scheme()));
+ row.scheme = Some(u.scheme().to_string());
if let Some(h) = u.host_str() {
- row.insert("host".into(), json!(h));
- row.insert("onion".into(), json!(h.ends_with(".onion")));
+ row.host = Some(h.to_string());
+ row.onion = Some(h.ends_with(".onion"));
}
if let Some(p) = u.port() {
- row.insert("port".into(), json!(p));
+ row.port = Some(p);
}
}
if include_nip11 {
- if let Some(doc) = fetch_nip11(row["url"].as_str().unwrap()).await {
- row.insert("nip11".into(), json!(doc));
+ if let Some(doc) = fetch_nip11(&row.url).await {
+ row.nip11 = Some(doc);
}
}
- out.push(JsonValue::Object(row));
+ out.push(row);
}
- Ok::<JsonValue, RpcError>(json!(out))
+ Ok::<Vec<RelayStatusRow>, RpcError>(out)
})?;
Ok(())
}
diff --git a/src/api/jsonrpc/methods/system.rs b/src/api/jsonrpc/methods/system.rs
@@ -1,9 +1,16 @@
use anyhow::Result;
use jsonrpsee::server::RpcModule;
-use serde_json::json;
+use serde::Serialize;
use crate::api::jsonrpc::{MethodRegistry, RpcContext};
+#[derive(Clone, Debug, Serialize)]
+struct SystemInfoResponse {
+ version: Option<serde_json::Value>,
+ build: Option<serde_json::Value>,
+ uptime_secs: u64,
+}
+
pub fn module(ctx: RpcContext, registry: MethodRegistry) -> Result<RpcModule<RpcContext>> {
let mut m = RpcModule::new(ctx);
@@ -13,10 +20,10 @@ pub fn module(ctx: RpcContext, registry: MethodRegistry) -> Result<RpcModule<Rpc
registry.track("system.get_info");
m.register_method("system.get_info", |_p, ctx, _| {
let uptime = ctx.state.started.elapsed().as_secs();
- json!({
- "version": ctx.state.info.get("version"),
- "build": ctx.state.info.get("build"),
- "uptime_secs": uptime,
+ Ok::<SystemInfoResponse, crate::api::jsonrpc::RpcError>(SystemInfoResponse {
+ version: ctx.state.info.get("version").cloned(),
+ build: ctx.state.info.get("build").cloned(),
+ uptime_secs: uptime,
})
})?;
diff --git a/src/api/jsonrpc/mod.rs b/src/api/jsonrpc/mod.rs
@@ -12,6 +12,7 @@ mod context;
mod error;
mod nostr;
mod params;
+mod relays;
mod registry;
mod server;
diff --git a/src/api/jsonrpc/relays.rs b/src/api/jsonrpc/relays.rs
@@ -0,0 +1,34 @@
+#![forbid(unsafe_code)]
+
+use serde::Serialize;
+
+use radroots_events::relay_document::RadrootsRelayDocument;
+
+#[derive(Clone, Debug, Serialize)]
+pub(crate) struct RelayAddedResponse {
+ pub added: String,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub(crate) struct RelayRemovedResponse {
+ pub removed: String,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub(crate) struct RelayConnectResponse {
+ pub connected: usize,
+ pub connecting: usize,
+ pub disconnected: usize,
+ pub spawned_connect: bool,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub(crate) struct RelayStatusRow {
+ pub url: String,
+ pub status: String,
+ pub scheme: Option<String>,
+ pub host: Option<String>,
+ pub onion: Option<bool>,
+ pub port: Option<u16>,
+ pub nip11: Option<RadrootsRelayDocument>,
+}