Skip to main content

spacetimedb_sdk/
error.rs

1use std::sync::Arc;
2
3use thiserror::Error;
4
5#[derive(Error, Debug, Clone)]
6#[non_exhaustive]
7pub enum Error {
8    #[error("Connection is already disconnected or has terminated normally")]
9    Disconnected,
10
11    #[error("Failed to connect: {source}")]
12    FailedToConnect {
13        #[source]
14        source: InternalError,
15    },
16
17    #[error("Host returned error when processing subscription query: {error}")]
18    SubscriptionError { error: String },
19
20    #[error("Subscription has already ended")]
21    AlreadyEnded,
22
23    #[error("Unsubscribe already called on subscription")]
24    AlreadyUnsubscribed,
25
26    #[error(transparent)]
27    Internal(#[from] InternalError),
28}
29
30#[derive(Debug, Clone)]
31pub struct InternalError {
32    message: String,
33    cause: Option<Arc<dyn std::error::Error + Send + Sync>>,
34}
35
36impl InternalError {
37    pub(crate) fn new(message: impl Into<String>) -> Self {
38        Self {
39            message: message.into(),
40            cause: None,
41        }
42    }
43
44    #[doc(hidden)]
45    /// Called by codegen. Not part of this library's stable API.
46    pub fn with_cause(self, cause: impl std::error::Error + Send + Sync + 'static) -> Self {
47        Self {
48            cause: Some(Arc::new(cause)),
49            ..self
50        }
51    }
52
53    #[doc(hidden)]
54    /// Called by codegen. Not part of this library's stable API.
55    pub fn failed_parse(ty: &'static str, container: &'static str) -> Self {
56        Self::new(format!(
57            "Failed to parse {ty} from {container}.
58
59This is often caused by outdated bindings; try re-running `spacetime generate`."
60        ))
61    }
62
63    #[doc(hidden)]
64    /// Called by codegen. Not part of this library's stable API.
65    pub fn unknown_name(category: &'static str, name: impl std::fmt::Display, container: &'static str) -> Self {
66        Self::new(format!(
67            "Unknown {category} {name} in {container}
68
69This is often caused by outdated bindings; try re-running `spacetime generate`."
70        ))
71    }
72}
73
74impl std::fmt::Display for InternalError {
75    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
76        f.write_str(&self.message)?;
77        if let Some(cause) = &self.cause {
78            write!(f, ": {cause}")?;
79        }
80        Ok(())
81    }
82}
83
84impl std::error::Error for InternalError {
85    fn cause(&self) -> Option<&dyn std::error::Error> {
86        // `self.cause.as_deref()`,
87        // except that formulation is unable to convert our `dyn Error + Send + Sync`
88        // into a `dyn Error`.
89        // See [this StackOverflow answer](https://stackoverflow.com/questions/63810977/strange-behavior-when-adding-the-send-trait-to-a-boxed-trait-object).
90        if let Some(cause) = &self.cause {
91            Some(cause)
92        } else {
93            None
94        }
95    }
96    fn description(&self) -> &str {
97        &self.message
98    }
99    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
100        None
101    }
102}
103
104pub type Result<T> = std::result::Result<T, Error>;