Skip to content
This repository was archived by the owner on May 13, 2026. It is now read-only.

Commit bf6932c

Browse files
shiba4lifeclaude
andauthored
refactor(handlers): add handler_err_msg trait, dedupe 12 verbatim-message sites (#968)
Adds IntoHandlerErrorMsg sibling to IntoHandlerError. Unlike .handler_err() ("Failed to {verb}: {e}"), .handler_err_msg() uses the supplied message verbatim — for sites whose existing prefix has different capitalization, embeds a runtime ID, or otherwise doesn't fit the fixed shape. Migrates the 12 verbatim-message HandlerError::Internal(format!(...)) sites in src/handlers/fingerprints/ that PR #967 explicitly left out of scope: - ingestion_errors.rs (1 site) - import_identity_card.rs (1 site) - reissue_identity_card.rs (1 site) - suggestions.rs (1 site) - personas.rs (8 sites) Error-message text is preserved byte-for-byte — no API contract change. Continues the handler-error refactor from #964 / #966 / #967. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2245709 commit bf6932c

7 files changed

Lines changed: 61 additions & 57 deletions

File tree

src/handlers/fingerprints/import_identity_card.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ use crate::fold_node::FoldNode;
7575
use crate::handlers::fingerprints::personas::{
7676
apply_persona_patch, PersonaDetailResponse, PersonaPatch,
7777
};
78-
use crate::handlers::response::{ApiResponse, HandlerError, HandlerResult, IntoHandlerError};
78+
use crate::handlers::response::{
79+
ApiResponse, HandlerError, HandlerResult, IntoHandlerError, IntoHandlerErrorMsg,
80+
};
7981

8082
/// Incoming card body. Shape mirrors `MyIdentityCardResponse` so a
8183
/// node can paste the JSON from another node's `/my-identity-card`
@@ -150,12 +152,9 @@ pub async fn import_identity_card(
150152
&now,
151153
));
152154
}
153-
write_records(node.clone(), &records).await.map_err(|e| {
154-
HandlerError::Internal(format!(
155-
"import_identity_card: failed to persist Identity/IdentityReceipt: {}",
156-
e
157-
))
158-
})?;
155+
write_records(node.clone(), &records)
156+
.await
157+
.handler_err_msg("import_identity_card: failed to persist Identity/IdentityReceipt")?;
159158
tracing::info!(
160159
"fingerprints.handler: imported Identity Card for pub_key='{}' (display_name='{}', face={})",
161160
req.card.pub_key,

src/handlers/fingerprints/ingestion_errors.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
2323
use crate::fingerprints::schemas::INGESTION_ERROR;
2424
use crate::fold_node::FoldNode;
25-
use crate::handlers::response::{ApiResponse, HandlerError, HandlerResult, IntoHandlerError};
25+
use crate::handlers::response::{
26+
ApiResponse, HandlerError, HandlerResult, IntoHandlerError, IntoHandlerErrorMsg,
27+
};
2628
use fold_db::schema::types::key_value::KeyValue;
2729
use fold_db::schema::types::operations::{MutationType, Query};
2830
use serde::{Deserialize, Serialize};
@@ -172,12 +174,7 @@ pub async fn resolve_ingestion_error(
172174
processor
173175
.execute_mutation(canonical, payload, key_value, MutationType::Update)
174176
.await
175-
.map_err(|e| {
176-
HandlerError::Internal(format!(
177-
"failed to update ingestion error '{}': {}",
178-
error_id, e
179-
))
180-
})?;
177+
.handler_err_msg(&format!("failed to update ingestion error '{}'", error_id))?;
181178

182179
tracing::info!(
183180
"fingerprints.handler: ingestion error '{}' resolved={}",

src/handlers/fingerprints/personas.rs

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::fingerprints::schemas::{EDGE, FINGERPRINT, MENTION, MENTION_BY_FINGER
2828
use crate::fold_node::FoldNode;
2929
use crate::handlers::response::{
3030
require_non_empty, ApiResponse, HandlerError, HandlerResult, IntoHandlerError,
31+
IntoHandlerErrorMsg,
3132
};
3233
use fold_db::schema::types::key_value::KeyValue;
3334
use fold_db::schema::types::operations::{MutationType, Query};
@@ -306,9 +307,7 @@ pub async fn delete_persona(
306307
MutationType::Delete,
307308
)
308309
.await
309-
.map_err(|e| {
310-
HandlerError::Internal(format!("failed to delete persona '{}': {}", persona_id, e))
311-
})?;
310+
.handler_err_msg(&format!("failed to delete persona '{}'", persona_id))?;
312311

313312
tracing::info!(
314313
"fingerprints.handler: deleted persona '{}' (underlying fingerprints/edges/mentions untouched)",
@@ -460,9 +459,10 @@ async fn fetch_fingerprint_views(
460459
sort_order: None,
461460
value_filters: None,
462461
};
463-
let records = processor.execute_query_json(query).await.map_err(|e| {
464-
HandlerError::Internal(format!("fingerprint '{}' query failed: {}", id, e))
465-
})?;
462+
let records = processor
463+
.execute_query_json(query)
464+
.await
465+
.handler_err_msg(&format!("fingerprint '{}' query failed", id))?;
466466
let Some(record) = records.first() else {
467467
tracing::warn!(
468468
"fingerprints.handler: fingerprint '{}' not found during enrichment",
@@ -532,12 +532,10 @@ async fn fetch_sample_mention_for_fingerprint(
532532
let junction_records = processor
533533
.execute_query_json(junction_query)
534534
.await
535-
.map_err(|e| {
536-
HandlerError::Internal(format!(
537-
"sample-mention junction query failed for '{}': {}",
538-
fingerprint_id, e
539-
))
540-
})?;
535+
.handler_err_msg(&format!(
536+
"sample-mention junction query failed for '{}'",
537+
fingerprint_id
538+
))?;
541539

542540
let Some(first) = junction_records.first() else {
543541
return Ok(None);
@@ -597,7 +595,7 @@ async fn fetch_edge_views(
597595
let records = processor
598596
.execute_query_json(query)
599597
.await
600-
.map_err(|e| HandlerError::Internal(format!("edge '{}' query failed: {}", id, e)))?;
598+
.handler_err_msg(&format!("edge '{}' query failed", id))?;
601599
let Some(record) = records.first() else {
602600
tracing::warn!(
603601
"fingerprints.handler: edge '{}' not found during enrichment",
@@ -654,7 +652,7 @@ async fn fetch_mention_views(
654652
let records = processor
655653
.execute_query_json(query)
656654
.await
657-
.map_err(|e| HandlerError::Internal(format!("mention '{}' query failed: {}", id, e)))?;
655+
.handler_err_msg(&format!("mention '{}' query failed", id))?;
658656
let Some(record) = records.first() else {
659657
tracing::warn!(
660658
"fingerprints.handler: mention '{}' not found during enrichment",
@@ -1006,9 +1004,7 @@ pub async fn apply_persona_patch(
10061004
processor
10071005
.execute_mutation(persona_canonical, payload, key_value, MutationType::Update)
10081006
.await
1009-
.map_err(|e| {
1010-
HandlerError::Internal(format!("failed to update persona '{}': {}", persona_id, e))
1011-
})?;
1007+
.handler_err_msg(&format!("failed to update persona '{}'", persona_id))?;
10121008

10131009
tracing::info!(
10141010
"fingerprints.handler: applied patch to persona '{}' \
@@ -1326,12 +1322,10 @@ pub async fn merge_personas(
13261322
MutationType::Update,
13271323
)
13281324
.await
1329-
.map_err(|e| {
1330-
HandlerError::Internal(format!(
1331-
"failed to write merged survivor persona '{}': {}",
1332-
survivor_id, e
1333-
))
1334-
})?;
1325+
.handler_err_msg(&format!(
1326+
"failed to write merged survivor persona '{}'",
1327+
survivor_id
1328+
))?;
13351329

13361330
let absorbed_key = KeyValue::new(Some(absorbed_id.clone()), None);
13371331
processor
@@ -1342,12 +1336,10 @@ pub async fn merge_personas(
13421336
MutationType::Delete,
13431337
)
13441338
.await
1345-
.map_err(|e| {
1346-
HandlerError::Internal(format!(
1347-
"survivor merged but failed to delete absorbed persona '{}': {}",
1348-
absorbed_id, e
1349-
))
1350-
})?;
1339+
.handler_err_msg(&format!(
1340+
"survivor merged but failed to delete absorbed persona '{}'",
1341+
absorbed_id
1342+
))?;
13511343

13521344
tracing::info!(
13531345
"fingerprints.handler: merged persona '{}' into '{}'",

src/handlers/fingerprints/reissue_identity_card.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ use crate::fold_node::FoldNode;
5656
use crate::handlers::fingerprints::my_identity_card::MyIdentityCardResponse;
5757
use crate::handlers::response::{
5858
require_non_empty, ApiResponse, HandlerError, HandlerResult, IntoHandlerError,
59+
IntoHandlerErrorMsg,
5960
};
6061

6162
/// Request body for the reissue endpoint. Both fields are optional
@@ -243,12 +244,7 @@ pub async fn reissue_identity_card(
243244
if request.display_name.is_some() {
244245
update_me_persona_name(&processor, &persona_canonical, &new_display_name)
245246
.await
246-
.map_err(|e| {
247-
HandlerError::Internal(format!(
248-
"Identity card updated but failed to sync Me persona name: {}",
249-
e
250-
))
251-
})?;
247+
.handler_err_msg("Identity card updated but failed to sync Me persona name")?;
252248
}
253249

254250
tracing::info!(

src/handlers/fingerprints/suggestions.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ use crate::fold_node::{FoldNode, OperationProcessor};
2323
use crate::handlers::fingerprints::personas::{
2424
get_persona, FingerprintView, PersonaDetailResponse,
2525
};
26-
use crate::handlers::response::{ApiResponse, HandlerError, HandlerResult, IntoHandlerError};
26+
use crate::handlers::response::{
27+
ApiResponse, HandlerError, HandlerResult, IntoHandlerError, IntoHandlerErrorMsg,
28+
};
2729
use fold_db::schema::types::field::HashRangeFilter;
2830
use fold_db::schema::types::key_value::KeyValue;
2931
use fold_db::schema::types::operations::{MutationType, Query};
@@ -228,12 +230,10 @@ pub async fn accept_suggested_persona(
228230
MutationType::Create,
229231
)
230232
.await
231-
.map_err(|e| {
232-
HandlerError::Internal(format!(
233-
"failed to create accepted persona '{}': {}",
234-
persona_id, e
235-
))
236-
})?;
233+
.handler_err_msg(&format!(
234+
"failed to create accepted persona '{}'",
235+
persona_id
236+
))?;
237237

238238
tracing::info!(
239239
"fingerprints.handler: accepted suggested persona '{}' with {} seeds",

src/handlers/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,6 @@ pub mod trust;
5252
pub use caller::current_caller_pubkey;
5353
pub(crate) use response::handler_response;
5454
pub use response::{
55-
get_db_guard, ApiResponse, HandlerError, HandlerResult, IntoHandlerError, IntoTypedHandlerError,
55+
get_db_guard, ApiResponse, HandlerError, HandlerResult, IntoHandlerError, IntoHandlerErrorMsg,
56+
IntoTypedHandlerError,
5657
};

src/handlers/response.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,25 @@ impl<T, E: fmt::Display> IntoHandlerError<T> for Result<T, E> {
229229
}
230230
}
231231

232+
/// Like `.handler_err()` but uses the supplied message verbatim — does NOT
233+
/// prepend "Failed to ". Use when the existing message has different
234+
/// capitalization, embeds a runtime ID inside the prefix, or otherwise
235+
/// doesn't fit the `"Failed to {verb}: {e}"` shape produced by `handler_err`.
236+
///
237+
/// ```ignore
238+
/// .handler_err_msg(&format!("fingerprint '{}' query failed", id))?
239+
/// // → HandlerError::Internal("fingerprint '<id>' query failed: <e>")
240+
/// ```
241+
pub trait IntoHandlerErrorMsg<T> {
242+
fn handler_err_msg(self, msg: &str) -> Result<T, HandlerError>;
243+
}
244+
245+
impl<T, E: fmt::Display> IntoHandlerErrorMsg<T> for Result<T, E> {
246+
fn handler_err_msg(self, msg: &str) -> Result<T, HandlerError> {
247+
self.map_err(|e| HandlerError::Internal(format!("{}: {}", msg, e)))
248+
}
249+
}
250+
232251
/// Extension trait for converting FoldDbError results using the typed From conversion.
233252
///
234253
/// Unlike `.handler_err()` which wraps every error as Internal(500), this preserves

0 commit comments

Comments
 (0)