Skip to main content

geam_core/host/
failure.rs

1use ecow::EcoString;
2use std::fmt::{self, Display, Formatter};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct HostFailure {
6    message: EcoString,
7}
8
9#[derive(Debug, PartialEq)]
10pub struct HostCallError {
11    kind: HostCallErrorKind,
12}
13
14#[derive(Debug, PartialEq)]
15pub(crate) enum HostCallErrorKind {
16    Failure(HostFailure),
17    Nested(crate::ExecutionError),
18}
19
20impl HostFailure {
21    pub fn new(message: impl Into<EcoString>) -> Self {
22        Self {
23            message: message.into(),
24        }
25    }
26
27    pub fn message(&self) -> &EcoString {
28        &self.message
29    }
30}
31
32impl HostCallError {
33    pub(crate) fn nested(error: crate::ExecutionError) -> Self {
34        Self {
35            kind: HostCallErrorKind::Nested(error),
36        }
37    }
38
39    pub(crate) fn into_kind(self) -> HostCallErrorKind {
40        self.kind
41    }
42}
43
44impl Display for HostFailure {
45    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
46        formatter.write_str(&self.message)
47    }
48}
49
50impl std::error::Error for HostFailure {}
51
52impl Display for HostCallError {
53    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
54        match &self.kind {
55            HostCallErrorKind::Failure(failure) => Display::fmt(failure, formatter),
56            HostCallErrorKind::Nested(error) => Display::fmt(error, formatter),
57        }
58    }
59}
60
61impl std::error::Error for HostCallError {}
62
63impl From<HostFailure> for HostCallError {
64    fn from(error: HostFailure) -> Self {
65        Self {
66            kind: HostCallErrorKind::Failure(error),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::{HostCallError, HostFailure};
74    use crate::{ExecutionError, InvariantError, ValueType};
75
76    #[test]
77    fn host_failure_owns_and_displays_its_message() {
78        let failure = HostFailure::new("database unavailable");
79
80        assert_eq!(failure.message(), "database unavailable");
81        assert_eq!(failure.to_string(), "database unavailable");
82    }
83
84    #[test]
85    fn host_call_error_preserves_the_owned_host_failure() {
86        let local = HostCallError::from(HostFailure::new("invalid input"));
87
88        assert_eq!(local.to_string(), "invalid input");
89        assert_eq!(
90            local.into_kind(),
91            super::HostCallErrorKind::Failure(HostFailure::new("invalid input")),
92        );
93    }
94
95    #[test]
96    fn host_call_error_preserves_a_nested_execution_failure() {
97        let execution = ExecutionError::Invariant(InvariantError::ListIndexOutOfBounds {
98            item_type: ValueType::Int,
99            index: 1,
100            length: 0,
101        });
102        let nested = HostCallError::nested(execution.clone());
103
104        assert_eq!(
105            nested.to_string(),
106            "list index out of bounds for Int list (index 1, length 0)",
107        );
108        assert_eq!(
109            nested.into_kind(),
110            super::HostCallErrorKind::Nested(execution),
111        );
112    }
113}