tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

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:
Mcrates/tangle_runtime/src/base_relay.rs | 75+--------------------------------------------------------------------------
Mcrates/tangle_runtime/src/config.rs | 5++++-
Acrates/tangle_runtime/src/errors.rs | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/tangle_runtime/src/lib.rs | 4+++-
Mcrates/tangle_runtime/tests/base_relay_v2.rs | 2+-
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 {