commit 35db648b3cc9009a2194710f4b61d2443185e8cd
parent 46e2600522f1a02262735ed7d00ccc46afb0befb
Author: triesap <tyson@radroots.org>
Date: Sun, 14 Jun 2026 01:45:57 -0700
runtime: extract relay error types
- move BaseRelayError into the runtime errors module
- move OK reply constructors with the relay error surface
- update direct callers to use the new errors boundary
- verify formatting and focused tangle_runtime checks stay green
Diffstat:
5 files changed, 108 insertions(+), 77 deletions(-)
diff --git a/crates/tangle_runtime/src/base_relay.rs b/crates/tangle_runtime/src/base_relay.rs
@@ -1,10 +1,10 @@
+use crate::errors::{BaseRelayError, ok_accepted, ok_rejected};
use axum::{
Json, Router,
extract::State,
response::{IntoResponse, Response},
routing::get,
};
-use core::fmt;
use http::{HeaderMap, HeaderValue, StatusCode, header};
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, collections::BTreeSet, str};
@@ -1355,63 +1355,6 @@ pub enum CloseResult {
NotFound,
}
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct BaseRelayError {
- prefix: &'static str,
- message: String,
-}
-
-impl BaseRelayError {
- pub fn invalid(message: impl Into<String>) -> Self {
- Self {
- prefix: "invalid",
- message: message.into(),
- }
- }
-
- pub fn auth_required(message: impl Into<String>) -> Self {
- Self {
- prefix: "auth-required",
- message: message.into(),
- }
- }
-
- pub fn error(message: impl Into<String>) -> Self {
- Self {
- prefix: "error",
- message: message.into(),
- }
- }
-
- pub fn prefixed_message(&self) -> String {
- format!("{}: {}", self.prefix, self.message)
- }
-
- pub fn message(&self) -> &str {
- &self.message
- }
-}
-
-impl fmt::Display for BaseRelayError {
- fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- formatter.write_str(&self.prefixed_message())
- }
-}
-
-impl std::error::Error for BaseRelayError {}
-
-impl From<tangle_store_pocket::PocketStoreError> for BaseRelayError {
- fn from(error: tangle_store_pocket::PocketStoreError) -> Self {
- Self::error(error.to_string())
- }
-}
-
-impl From<GroupError> for BaseRelayError {
- fn from(error: GroupError) -> Self {
- Self::error(error.prefixed_message())
- }
-}
-
fn relay_self_from_groups(
groups: &GroupRuntimeConfig,
) -> Result<Option<PublicKeyHex>, BaseRelayError> {
@@ -1434,22 +1377,6 @@ fn accepts_nostr_json(value: Option<&HeaderValue>) -> bool {
})
}
-fn ok_accepted(event_id: EventId, message: String) -> RelayMessage {
- RelayMessage::Ok {
- event_id,
- accepted: true,
- message,
- }
-}
-
-fn ok_rejected(event_id: EventId, message: String) -> RelayMessage {
- RelayMessage::Ok {
- event_id,
- accepted: false,
- message,
- }
-}
-
fn tangle_event_to_pocket(event: &Event) -> Result<PocketOwnedEvent, BaseRelayError> {
let raw = event_to_value(event).to_string();
parse_pocket_event_json(raw.as_bytes()).map_err(BaseRelayError::from)
diff --git a/crates/tangle_runtime/src/config.rs b/crates/tangle_runtime/src/config.rs
@@ -1,6 +1,9 @@
#![forbid(unsafe_code)]
-use crate::base_relay::{BaseAuthState, BaseRelay, BaseRelayError};
+use crate::{
+ base_relay::{BaseAuthState, BaseRelay},
+ errors::BaseRelayError,
+};
use serde::Deserialize;
use std::{net::SocketAddr, path::PathBuf};
use tangle_groups::GroupRuntimeConfig;
diff --git a/crates/tangle_runtime/src/errors.rs b/crates/tangle_runtime/src/errors.rs
@@ -0,0 +1,99 @@
+#![forbid(unsafe_code)]
+
+use core::fmt;
+use tangle_groups::GroupError;
+use tangle_protocol::{EventId, RelayMessage};
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct BaseRelayError {
+ prefix: &'static str,
+ message: String,
+}
+
+impl BaseRelayError {
+ pub fn invalid(message: impl Into<String>) -> Self {
+ Self {
+ prefix: "invalid",
+ message: message.into(),
+ }
+ }
+
+ pub fn auth_required(message: impl Into<String>) -> Self {
+ Self {
+ prefix: "auth-required",
+ message: message.into(),
+ }
+ }
+
+ pub fn error(message: impl Into<String>) -> Self {
+ Self {
+ prefix: "error",
+ message: message.into(),
+ }
+ }
+
+ pub fn prefixed_message(&self) -> String {
+ format!("{}: {}", self.prefix, self.message)
+ }
+
+ pub fn message(&self) -> &str {
+ &self.message
+ }
+}
+
+impl fmt::Display for BaseRelayError {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str(&self.prefixed_message())
+ }
+}
+
+impl std::error::Error for BaseRelayError {}
+
+impl From<tangle_store_pocket::PocketStoreError> for BaseRelayError {
+ fn from(error: tangle_store_pocket::PocketStoreError) -> Self {
+ Self::error(error.to_string())
+ }
+}
+
+impl From<GroupError> for BaseRelayError {
+ fn from(error: GroupError) -> Self {
+ Self::error(error.prefixed_message())
+ }
+}
+
+pub(crate) fn ok_accepted(event_id: EventId, message: String) -> RelayMessage {
+ RelayMessage::Ok {
+ event_id,
+ accepted: true,
+ message,
+ }
+}
+
+pub(crate) fn ok_rejected(event_id: EventId, message: String) -> RelayMessage {
+ RelayMessage::Ok {
+ event_id,
+ accepted: false,
+ message,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::BaseRelayError;
+
+ #[test]
+ fn relay_error_prefixes_are_stable() {
+ assert_eq!(
+ BaseRelayError::invalid("bad event").prefixed_message(),
+ "invalid: bad event"
+ );
+ assert_eq!(
+ BaseRelayError::auth_required("login").prefixed_message(),
+ "auth-required: login"
+ );
+ assert_eq!(
+ BaseRelayError::error("store").prefixed_message(),
+ "error: store"
+ );
+ }
+}
diff --git a/crates/tangle_runtime/src/lib.rs b/crates/tangle_runtime/src/lib.rs
@@ -3,11 +3,13 @@
pub mod base_relay;
pub mod chorus_pocket;
pub mod config;
+pub mod errors;
use std::{fmt, fs, path::Path, path::PathBuf};
-use base_relay::{BaseRelayError, BaseRelayReadinessState};
+use base_relay::BaseRelayReadinessState;
use config::{BaseRelayRuntimeConfig, parse_base_relay_runtime_config_json};
+use errors::BaseRelayError;
pub const TANGLE_SUPPORTED_NIPS: [u16; 6] = [1, 11, 29, 42, 45, 70];
pub const TANGLE_RELAY_SOFTWARE: &str = "https://github.com/radrootslabs/tangle";
diff --git a/crates/tangle_runtime/tests/base_relay_v2.rs b/crates/tangle_runtime/tests/base_relay_v2.rs
@@ -735,7 +735,7 @@ fn assert_event_query(
}
fn assert_count(
- message: Result<RelayMessage, tangle_runtime::base_relay::BaseRelayError>,
+ message: Result<RelayMessage, tangle_runtime::errors::BaseRelayError>,
expected: u64,
) {
let RelayMessage::Count { count, .. } = message.expect("count") else {