commit db4f3acda1eb67bc9a405b3c76962ca0f03d8dcb
parent cd05e1e724184056cc914d78c6faa9e5a1d3328d
Author: triesap <tyson@radroots.org>
Date: Fri, 19 Jun 2026 16:35:54 -0700
errors: type mobile runtime failures
- replace the string-only app error with stable mobile error categories.
- map identity, relay, runtime, initialization, and unsupported failures explicitly.
- turn retired listing message fetch into an unsupported operation.
- cover identity, relay, and unsupported error mappings in field core tests.
Diffstat:
10 files changed, 194 insertions(+), 95 deletions(-)
diff --git a/crates/field_core/src/error.rs b/crates/field_core/src/error.rs
@@ -2,6 +2,48 @@ use thiserror::Error;
#[derive(Debug, Error, uniffi::Error)]
pub enum RadrootsAppError {
- #[error("{0}")]
- Msg(String),
+ #[error("initialization: {0}")]
+ Initialization(String),
+ #[error("identity: {0}")]
+ Identity(String),
+ #[error("secure store: {0}")]
+ SecureStore(String),
+ #[error("relay: {0}")]
+ Relay(String),
+ #[error("runtime: {0}")]
+ Runtime(String),
+ #[error("unsupported: {0}")]
+ Unsupported(String),
+ #[error("internal: {0}")]
+ Internal(String),
+}
+
+impl RadrootsAppError {
+ pub fn initialization(message: impl Into<String>) -> Self {
+ Self::Initialization(message.into())
+ }
+
+ pub fn identity(message: impl Into<String>) -> Self {
+ Self::Identity(message.into())
+ }
+
+ pub fn secure_store(message: impl Into<String>) -> Self {
+ Self::SecureStore(message.into())
+ }
+
+ pub fn relay(message: impl Into<String>) -> Self {
+ Self::Relay(message.into())
+ }
+
+ pub fn runtime(message: impl Into<String>) -> Self {
+ Self::Runtime(message.into())
+ }
+
+ pub fn unsupported(message: impl Into<String>) -> Self {
+ Self::Unsupported(message.into())
+ }
+
+ pub fn internal(message: impl Into<String>) -> Self {
+ Self::Internal(message.into())
+ }
}
diff --git a/crates/field_core/src/logging.rs b/crates/field_core/src/logging.rs
@@ -15,7 +15,7 @@ pub fn init_logging(
};
match radroots_log::init_logging(opts) {
Ok(()) => Ok(()),
- Err(err) => Err(crate::RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => Err(crate::RadrootsAppError::initialization(format!("{err}"))),
}
}
@@ -23,7 +23,7 @@ pub fn init_logging(
pub fn init_logging_stdout() -> Result<(), crate::RadrootsAppError> {
match radroots_log::init_stdout() {
Ok(()) => Ok(()),
- Err(err) => Err(crate::RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => Err(crate::RadrootsAppError::initialization(format!("{err}"))),
}
}
diff --git a/crates/field_core/src/runtime/builder.rs b/crates/field_core/src/runtime/builder.rs
@@ -36,7 +36,9 @@ impl RuntimeBuilder {
.build()
{
Ok(handle) => Ok(handle),
- Err(err) => Err(RadrootsAppError::Msg(format!("net build failed: {err}"))),
+ Err(err) => Err(RadrootsAppError::initialization(format!(
+ "net build failed: {err}"
+ ))),
}
}
diff --git a/crates/field_core/src/runtime/key_management.rs b/crates/field_core/src/runtime/key_management.rs
@@ -35,14 +35,14 @@ fn account_record(
let selected_identity_id = net
.accounts
.default_account_id()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
let account = net
.accounts
.list_accounts()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?
.into_iter()
.find(|account| &account.account_id == account_id)
- .ok_or_else(|| RadrootsAppError::Msg(format!("identity not found: {account_id}")))?;
+ .ok_or_else(|| RadrootsAppError::identity(format!("identity not found: {account_id}")))?;
let is_selected = selected_identity_id
.as_ref()
.map(|selected| selected == &account.account_id)
@@ -66,7 +66,7 @@ fn invalidate_nostr_runtime(net: &mut radroots_net_core::Net) {
#[cfg(feature = "nostr-client")]
fn identity_from_secret(secret_key: &str) -> Result<RadrootsIdentity, RadrootsAppError> {
RadrootsIdentity::from_secret_key_str(secret_key)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))
}
#[cfg(feature = "nostr-client")]
@@ -92,7 +92,7 @@ fn restore_host_custody_identity(
let account_id = net
.accounts
.upsert_identity(identity, label, make_selected)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
invalidate_nostr_runtime(net);
account_record(net, &account_id)
}
@@ -148,16 +148,16 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let selected_identity_id = guard
.accounts
.default_account_id()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
let accounts = guard
.accounts
.list_accounts()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
return Ok(accounts
.into_iter()
.map(|account| {
@@ -177,7 +177,7 @@ impl RadrootsRuntime {
}
#[cfg(not(feature = "nostr-client"))]
{
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -194,16 +194,16 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let selected_identity_id = guard
.accounts
.default_account_id()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
let selected_npub = guard
.accounts
.default_public_identity()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?
.map(|identity| identity.public_key_npub);
let has_selected_signing_identity = guard
.accounts
@@ -214,7 +214,7 @@ impl RadrootsRuntime {
let identities = guard
.accounts
.list_accounts()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?
.into_iter()
.map(|account| {
let is_selected = selected_identity_id
@@ -239,7 +239,7 @@ impl RadrootsRuntime {
}
#[cfg(not(feature = "nostr-client"))]
{
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -255,7 +255,7 @@ impl RadrootsRuntime {
#[cfg(not(feature = "nostr-client"))]
{
let _ = secret_key;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -269,7 +269,7 @@ impl RadrootsRuntime {
{
let mut guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let (identity, _) = host_custody_identity_from_secret(secret_key.as_str())?;
return restore_host_custody_identity(&mut guard, &identity, label, make_selected);
@@ -277,7 +277,7 @@ impl RadrootsRuntime {
#[cfg(not(feature = "nostr-client"))]
{
let _ = (secret_key, label, make_selected);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -286,21 +286,21 @@ impl RadrootsRuntime {
{
let mut guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let account_id = RadrootsIdentityId::parse(identity_id.as_str())
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
guard
.accounts
.set_default_account(&account_id)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
invalidate_nostr_runtime(&mut guard);
Ok(())
}
#[cfg(not(feature = "nostr-client"))]
{
let _ = identity_id;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -309,21 +309,21 @@ impl RadrootsRuntime {
{
let mut guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let account_id = RadrootsIdentityId::parse(identity_id.as_str())
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
guard
.accounts
.remove_account(&account_id)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
invalidate_nostr_runtime(&mut guard);
Ok(())
}
#[cfg(not(feature = "nostr-client"))]
{
let _ = identity_id;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -332,22 +332,22 @@ impl RadrootsRuntime {
{
let mut guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let accounts = guard
.accounts
.list_accounts()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
for account in accounts {
guard
.accounts
.remove_account(&account.account_id)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
}
guard
.accounts
.clear_default_account()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))?;
+ .map_err(|e| RadrootsAppError::identity(format!("{e}")))?;
invalidate_nostr_runtime(&mut guard);
if let Ok(mut rx_guard) = self.post_events_rx.lock() {
*rx_guard = None;
@@ -356,7 +356,7 @@ impl RadrootsRuntime {
}
#[cfg(not(feature = "nostr-client"))]
{
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
diff --git a/crates/field_core/src/runtime/mod.rs b/crates/field_core/src/runtime/mod.rs
@@ -49,7 +49,11 @@ impl RadrootsRuntime {
#[cfg(feature = "rt")]
let handle = match NetBuilder::new().config(cfg).manage_runtime(true).build() {
Ok(handle) => handle,
- Err(err) => return Err(RadrootsAppError::Msg(format!("net build failed: {err}"))),
+ Err(err) => {
+ return Err(RadrootsAppError::initialization(format!(
+ "net build failed: {err}"
+ )));
+ }
};
#[cfg(not(feature = "rt"))]
let handle = NetBuilder::new()
diff --git a/crates/field_core/src/runtime/nostr.rs b/crates/field_core/src/runtime/nostr.rs
@@ -83,16 +83,16 @@ impl RadrootsRuntime {
{
let mut guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
guard
.nostr_set_default_relays(&relays)
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))
+ .map_err(|e| RadrootsAppError::relay(format!("{e}")))
}
#[cfg(not(feature = "nostr-client"))]
{
let _ = relays;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -101,15 +101,15 @@ impl RadrootsRuntime {
{
let mut guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
guard
.nostr_connect_if_key_present()
- .map_err(|e| RadrootsAppError::Msg(format!("{e}")))
+ .map_err(|e| RadrootsAppError::relay(format!("{e}")))
}
#[cfg(not(feature = "nostr-client"))]
{
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -194,19 +194,19 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
mgr.publish_profile_event_blocking(name, display_name, nip05, about)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
+ .map_err(|e| RadrootsAppError::relay(e.to_string()))
}
#[cfg(not(feature = "nostr-client"))]
{
let _ = (name, display_name, nip05, about);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -215,19 +215,19 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
mgr.publish_post_event_blocking(content)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
+ .map_err(|e| RadrootsAppError::relay(e.to_string()))
}
#[cfg(not(feature = "nostr-client"))]
{
let _ = content;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -240,21 +240,21 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
let items = mgr
.fetch_post_events_blocking(limit, since_unix)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))?;
+ .map_err(|e| RadrootsAppError::relay(e.to_string()))?;
Ok(items.into_iter().map(map_post_event_metadata).collect())
}
#[cfg(not(feature = "nostr-client"))]
{
let _ = (limit, since_unix);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -269,19 +269,19 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
mgr.publish_post_reply_event_blocking(
parent_event_id_hex,
parent_author_hex,
content,
root_event_id_hex,
)
- .map_err(|e| RadrootsAppError::Msg(e.to_string()))
+ .map_err(|e| RadrootsAppError::relay(e.to_string()))
}
#[cfg(not(feature = "nostr-client"))]
{
@@ -291,7 +291,7 @@ impl RadrootsRuntime {
content,
root_event_id_hex,
);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -303,12 +303,12 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
mgr.start_post_event_stream(since_unix);
if let Ok(mut rx_guard) = self.post_events_rx.lock() {
if rx_guard.is_none() {
@@ -320,7 +320,7 @@ impl RadrootsRuntime {
#[cfg(not(feature = "nostr-client"))]
{
let _ = since_unix;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -350,12 +350,12 @@ impl RadrootsRuntime {
{
let guard = match self.net.lock() {
Ok(guard) => guard,
- Err(err) => return Err(RadrootsAppError::Msg(format!("{err}"))),
+ Err(err) => return Err(RadrootsAppError::runtime(format!("{err}"))),
};
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
mgr.stop_post_event_stream();
if let Ok(mut rx_guard) = self.post_events_rx.lock() {
*rx_guard = None;
@@ -364,7 +364,7 @@ impl RadrootsRuntime {
}
#[cfg(not(feature = "nostr-client"))]
{
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
}
diff --git a/crates/field_core/src/runtime/trade_listing.rs b/crates/field_core/src/runtime/trade_listing.rs
@@ -115,10 +115,11 @@ impl RadrootsRuntime {
draft: TradeListingDraft,
) -> Result<TradeListingEventParts, RadrootsAppError> {
let listing = listing_from_draft(&draft)?;
- let parts = listing_to_wire_parts(&listing)
- .map_err(|error| RadrootsAppError::Msg(format!("listing encode failed: {error}")))?;
+ let parts = listing_to_wire_parts(&listing).map_err(|error| {
+ RadrootsAppError::runtime(format!("listing encode failed: {error}"))
+ })?;
let tags_json = serde_json::to_string(&parts.tags).map_err(|error| {
- RadrootsAppError::Msg(format!("listing tags encode failed: {error}"))
+ RadrootsAppError::runtime(format!("listing tags encode failed: {error}"))
})?;
Ok(TradeListingEventParts {
kind: parts.kind,
@@ -136,28 +137,28 @@ impl RadrootsRuntime {
let guard = self
.net
.lock()
- .map_err(|error| RadrootsAppError::Msg(format!("{error}")))?;
+ .map_err(|error| RadrootsAppError::runtime(format!("{error}")))?;
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
let listing = listing_from_draft(&draft)?;
let current_pubkey = current_pubkey_hex(self)?;
if listing.farm.pubkey != current_pubkey {
- return Err(RadrootsAppError::Msg(
- "farm_pubkey must match the default account public key".into(),
+ return Err(RadrootsAppError::runtime(
+ "farm_pubkey must match the default account public key",
));
}
let parts = listing_to_wire_parts(&listing).map_err(|error| {
- RadrootsAppError::Msg(format!("listing encode failed: {error}"))
+ RadrootsAppError::runtime(format!("listing encode failed: {error}"))
})?;
mgr.send_custom_event_blocking(parts.kind, parts.content, parts.tags)
- .map_err(|error| RadrootsAppError::Msg(error.to_string()))
+ .map_err(|error| RadrootsAppError::relay(error.to_string()))
}
#[cfg(not(feature = "nostr-client"))]
{
let _ = draft;
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -171,11 +172,11 @@ impl RadrootsRuntime {
let guard = self
.net
.lock()
- .map_err(|error| RadrootsAppError::Msg(format!("{error}")))?;
+ .map_err(|error| RadrootsAppError::runtime(format!("{error}")))?;
let mgr = guard
.nostr
.as_ref()
- .ok_or_else(|| RadrootsAppError::Msg("nostr not initialized".into()))?;
+ .ok_or_else(|| RadrootsAppError::relay("nostr not initialized"))?;
let mut filter =
RadrootsNostrFilter::new().kind(RadrootsNostrKind::Custom(KIND_LISTING as u16));
filter = filter.limit(limit.into());
@@ -185,7 +186,7 @@ impl RadrootsRuntime {
let events = mgr
.fetch_events_blocking(filter, core::time::Duration::from_secs(10))
- .map_err(|error| RadrootsAppError::Msg(error.to_string()))?;
+ .map_err(|error| RadrootsAppError::relay(error.to_string()))?;
let mut out = Vec::new();
for event in events {
let event = radroots_event_from_nostr(&event);
@@ -199,7 +200,7 @@ impl RadrootsRuntime {
#[cfg(not(feature = "nostr-client"))]
{
let _ = (limit, since_unix);
- Err(RadrootsAppError::Msg("nostr disabled".into()))
+ Err(RadrootsAppError::unsupported("nostr disabled"))
}
}
@@ -216,8 +217,8 @@ impl RadrootsRuntime {
listing_id,
recipient_pubkey,
);
- Err(RadrootsAppError::Msg(
- "legacy listing validation requests are retired".into(),
+ Err(RadrootsAppError::unsupported(
+ "legacy listing validation requests are retired",
))
}
@@ -226,8 +227,8 @@ impl RadrootsRuntime {
draft: TradeOrderDraft,
) -> Result<TradeOrderSendResult, RadrootsAppError> {
let _ = draft;
- Err(RadrootsAppError::Msg(
- "legacy listing order requests are retired; use active trade order APIs".into(),
+ Err(RadrootsAppError::unsupported(
+ "legacy listing order requests are retired; use active trade order APIs",
))
}
@@ -238,7 +239,9 @@ impl RadrootsRuntime {
since_unix: Option<u64>,
) -> Result<Vec<TradeListingMessageSummary>, RadrootsAppError> {
let _ = (listing_addr, limit, since_unix);
- Ok(Vec::new())
+ Err(RadrootsAppError::unsupported(
+ "legacy listing message fetch is retired",
+ ))
}
}
@@ -260,9 +263,9 @@ fn listing_from_draft(draft: &TradeListingDraft) -> Result<RadrootsListing, Radr
"bin_id",
)?;
let listing_id = RadrootsDTag::parse(listing_id)
- .map_err(|error| RadrootsAppError::Msg(format!("invalid listing_id: {error}")))?;
+ .map_err(|error| RadrootsAppError::runtime(format!("invalid listing_id: {error}")))?;
let bin_id = RadrootsInventoryBinId::parse(bin_id)
- .map_err(|error| RadrootsAppError::Msg(format!("invalid bin_id: {error}")))?;
+ .map_err(|error| RadrootsAppError::runtime(format!("invalid bin_id: {error}")))?;
let amount = parse_decimal(&draft.bin_display_amount, "bin_display_amount")?;
let unit = parse_unit(&draft.bin_display_unit)?;
let canonical_unit = unit.canonical_unit();
@@ -367,19 +370,19 @@ fn current_pubkey_hex(runtime: &RadrootsRuntime) -> Result<String, RadrootsAppEr
let guard = runtime
.net
.lock()
- .map_err(|error| RadrootsAppError::Msg(format!("{error}")))?;
+ .map_err(|error| RadrootsAppError::runtime(format!("{error}")))?;
let identity = guard
.accounts
.default_public_identity()
- .map_err(|error| RadrootsAppError::Msg(format!("{error}")))?
- .ok_or_else(|| RadrootsAppError::Msg("default account is not configured".into()))?;
+ .map_err(|error| RadrootsAppError::identity(format!("{error}")))?
+ .ok_or_else(|| RadrootsAppError::identity("default account is not configured"))?;
Ok(identity.public_key_hex)
}
fn non_empty(value: String, field: &str) -> Result<String, RadrootsAppError> {
let value = value.trim().to_string();
if value.is_empty() {
- return Err(RadrootsAppError::Msg(format!("{field} is required")));
+ return Err(RadrootsAppError::runtime(format!("{field} is required")));
}
Ok(value)
}
@@ -392,17 +395,17 @@ fn blank_to_none(value: Option<String>) -> Option<String> {
fn parse_decimal(value: &str, field: &str) -> Result<RadrootsCoreDecimal, RadrootsAppError> {
RadrootsCoreDecimal::from_str(value.trim())
- .map_err(|error| RadrootsAppError::Msg(format!("{field} is invalid: {error}")))
+ .map_err(|error| RadrootsAppError::runtime(format!("{field} is invalid: {error}")))
}
fn parse_currency(value: &str) -> Result<RadrootsCoreCurrency, RadrootsAppError> {
RadrootsCoreCurrency::from_str(value.trim())
- .map_err(|error| RadrootsAppError::Msg(format!("currency is invalid: {error}")))
+ .map_err(|error| RadrootsAppError::runtime(format!("currency is invalid: {error}")))
}
fn parse_unit(value: &str) -> Result<RadrootsCoreUnit, RadrootsAppError> {
RadrootsCoreUnit::from_str(value.trim())
- .map_err(|error| RadrootsAppError::Msg(format!("unit is invalid: {error}")))
+ .map_err(|error| RadrootsAppError::runtime(format!("unit is invalid: {error}")))
}
fn parse_delivery_method(value: &str) -> RadrootsListingDeliveryMethod {
diff --git a/crates/field_core/tests/error_mapping.rs b/crates/field_core/tests/error_mapping.rs
@@ -0,0 +1,48 @@
+#![cfg(feature = "nostr-client")]
+
+use radroots_field_core::{RadrootsAppError, RadrootsRuntime};
+
+#[test]
+fn invalid_host_custody_secret_maps_to_identity_error() {
+ let runtime = RadrootsRuntime::new().expect("runtime");
+
+ let err = runtime
+ .nostr_identity_validate_host_custody_secret("not-a-secret".to_string())
+ .expect_err("invalid secret should fail");
+
+ assert!(matches!(err, RadrootsAppError::Identity(_)));
+}
+
+#[test]
+fn uninitialized_nostr_publish_maps_to_relay_error() {
+ let runtime = RadrootsRuntime::new().expect("runtime");
+
+ let err = runtime
+ .nostr_post_text_note("hello".to_string())
+ .expect_err("uninitialized nostr should fail");
+
+ assert!(matches!(
+ err,
+ RadrootsAppError::Relay(message) if message == "nostr not initialized"
+ ));
+}
+
+#[test]
+fn retired_trade_operations_map_to_unsupported_error() {
+ let runtime = RadrootsRuntime::new().expect("runtime");
+
+ let validation_err = runtime
+ .trade_listing_send_validation_request(
+ "event".to_string(),
+ "seller".to_string(),
+ "listing".to_string(),
+ "recipient".to_string(),
+ )
+ .expect_err("retired validation request should fail");
+ let messages_err = runtime
+ .trade_listing_fetch_messages("listing".to_string(), 10, None)
+ .expect_err("retired message fetch should fail");
+
+ assert!(matches!(validation_err, RadrootsAppError::Unsupported(_)));
+ assert!(matches!(messages_err, RadrootsAppError::Unsupported(_)));
+}
diff --git a/crates/field_core/tests/logging_error.rs b/crates/field_core/tests/logging_error.rs
@@ -5,5 +5,5 @@ use radroots_field_core::logging;
fn init_logging_stdout_maps_global_subscriber_error() {
let _ = tracing_subscriber::fmt().try_init();
let err = logging::init_logging_stdout();
- assert!(matches!(err, Err(RadrootsAppError::Msg(_))));
+ assert!(matches!(err, Err(RadrootsAppError::Initialization(_))));
}
diff --git a/crates/field_core/tests/no_nostr_runtime.rs b/crates/field_core/tests/no_nostr_runtime.rs
@@ -11,7 +11,7 @@ use radroots_net_core::config::NetConfig;
fn expect_disabled<T>(result: Result<T, RadrootsAppError>) {
match result {
- Err(RadrootsAppError::Msg(message)) => assert_eq!(message, "nostr disabled"),
+ Err(RadrootsAppError::Unsupported(message)) => assert_eq!(message, "nostr disabled"),
_ => panic!("expected nostr disabled error"),
}
}
@@ -87,7 +87,7 @@ fn runtime_builder_and_logging_paths_are_exercised() {
drop(default_handle);
let err = logging::init_logging(Some("/dev/null/file.log".to_string()), None, Some(false));
- assert!(matches!(err, Err(RadrootsAppError::Msg(_))));
+ assert!(matches!(err, Err(RadrootsAppError::Initialization(_))));
let _ = logging::init_logging(None, None, None);
let _ = logging::init_logging(None, Some("app.log".to_string()), Some(false));
let _ = logging::init_logging_stdout();