Skip to main content

dex_sdk/
handler_error.rs

1// Copyright (c) 2026 Super Durable, Inc.
2//
3// Licensed under the Super Durable Source License 1.0.
4// You may not use this file except in compliance with the License.
5// See the LICENSE file in the repository root.
6//
7// SPDX-License-Identifier: LicenseRef-Super-Durable-1.0
8
9use std::error::Error;
10use std::fmt::{Display, Formatter};
11
12/// Result returned by application Step and RPC handlers.
13pub type HandlerResult<T> = Result<T, HandlerError>;
14
15#[derive(Clone, Debug, Eq, PartialEq)]
16/// Reports an application-defined Step or RPC failure to Dex.
17///
18/// Return this error from [`crate::Step::wait_for`], [`crate::Step::execute`], or an RPC handler.
19/// Dex records the message and applies the configured retry or failure policy.
20pub struct HandlerError {
21    message: String,
22    error_type: String,
23}
24
25impl HandlerError {
26    /// Creates a handler error with a developer-facing message.
27    pub fn new(message: impl Into<String>) -> Self {
28        Self {
29            message: message.into(),
30            error_type: std::any::type_name::<Self>().to_string(),
31        }
32    }
33
34    pub(crate) fn invalid_step_result(
35        flow_type: &str,
36        step_type: Option<&str>,
37        method: &str,
38        detail: impl std::fmt::Display,
39    ) -> Self {
40        let target = step_type.map_or_else(
41            || format!("RPC in Flow {flow_type}"),
42            |step_type| format!("Flow {flow_type} Step {step_type}"),
43        );
44        Self {
45            message: format!("{target} {method} returned an invalid result: {detail}"),
46            error_type: "dex_sdk::SdkError::InvalidStepResult".to_string(),
47        }
48    }
49
50    pub(crate) fn error_type(&self) -> &str {
51        &self.error_type
52    }
53}
54
55impl Display for HandlerError {
56    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
57        formatter.write_str(&self.message)
58    }
59}
60
61impl Error for HandlerError {}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn invalid_step_result_has_stable_context_and_type() {
69        let error = HandlerError::invalid_step_result(
70            "OrderFlow",
71            Some("ApproveOrder"),
72            "execute",
73            "missing movement",
74        );
75        assert_eq!("dex_sdk::SdkError::InvalidStepResult", error.error_type());
76        assert!(
77            error
78                .to_string()
79                .contains("Flow OrderFlow Step ApproveOrder")
80        );
81    }
82}