Skip to main content

restate_sdk_shared_core/
fmt.rs

1use std::fmt;
2use std::sync::OnceLock;
3
4pub trait ErrorFormatter: Send + Sync + fmt::Debug + 'static {
5    fn display_closed_error(&self, f: &mut fmt::Formatter<'_>, event: &str) -> fmt::Result {
6        write!(f, "State machine was closed when invoking '{event}'")
7    }
8}
9
10static GLOBAL_ERROR_FORMATTER: OnceLock<Box<dyn ErrorFormatter>> = OnceLock::new();
11
12/// Set the global error formatter.
13///
14/// The formatter can only be installed once: the first call wins and any
15/// subsequent call is a no-op. Returns `true` if this call
16/// installed the formatter, `false` if one was already set.
17pub fn set_error_formatter(formatter: impl ErrorFormatter + 'static) -> bool {
18    GLOBAL_ERROR_FORMATTER.set(Box::new(formatter)).is_ok()
19}
20
21#[derive(Debug)]
22struct DefaultErrorFormatter;
23
24impl ErrorFormatter for DefaultErrorFormatter {}
25
26macro_rules! delegate_to_formatter {
27    ($fn_name:ident($($param_name:ident: $param_type:ty),*) -> $return_type:ty) => {
28        pub(crate) fn $fn_name($($param_name: $param_type),*) -> $return_type {
29            if let Some(custom_formatter) = GLOBAL_ERROR_FORMATTER.get() {
30                custom_formatter.$fn_name($($param_name),*)
31            } else {
32                DefaultErrorFormatter.$fn_name($($param_name),*)
33            }
34        }
35    };
36}
37
38delegate_to_formatter!(display_closed_error(f: &mut fmt::Formatter<'_>, event: &str) -> fmt::Result);
39
40pub(crate) struct DiffFormatter<'a, 'b> {
41    fmt: &'a mut fmt::Formatter<'b>,
42    indentation: &'static str,
43}
44
45impl<'a, 'b: 'a> DiffFormatter<'a, 'b> {
46    pub(crate) fn new(fmt: &'a mut fmt::Formatter<'b>, indentation: &'static str) -> Self {
47        Self { fmt, indentation }
48    }
49
50    pub(crate) fn write_diff(
51        &mut self,
52        field_name: &'static str,
53        actual: impl fmt::Display,
54        expected: impl fmt::Display,
55    ) -> fmt::Result {
56        write!(
57            self.fmt,
58            "\n{}{field_name}: {actual} != {expected}",
59            self.indentation
60        )
61    }
62
63    pub(crate) fn write_bytes_diff(
64        &mut self,
65        field_name: &'static str,
66        actual: &[u8],
67        expected: &[u8],
68    ) -> fmt::Result {
69        write!(self.fmt, "\n{}{field_name}: ", self.indentation)?;
70        match (std::str::from_utf8(actual), std::str::from_utf8(expected)) {
71            (Ok(actual), Ok(expected)) => {
72                write!(self.fmt, "'{actual}' != '{expected}'",)
73            }
74            (Ok(actual), Err(_)) => {
75                write!(self.fmt, "'{actual}' != {expected:?}")
76            }
77            (Err(_), Ok(expected)) => {
78                write!(self.fmt, "{actual:?} != '{expected}'")
79            }
80            (Err(_), Err(_)) => {
81                write!(self.fmt, "{actual:?} != {expected:?}")
82            }
83        }
84    }
85}