Skip to main content

rvoip_core_traits/
error.rs

1use std::fmt;
2
3pub type Result<T> = std::result::Result<T, RvoipError>;
4
5pub enum RvoipError {
6    NotImplemented(&'static str),
7    NoAdapterForTransport(crate::connection::Transport),
8    AdapterAlreadyRegistered(crate::connection::Transport),
9    ConnectionNotFound(crate::ids::ConnectionId),
10    SessionNotFound(crate::ids::SessionId),
11    ConversationNotFound(crate::ids::ConversationId),
12    BridgeNotFound(crate::ids::BridgeId),
13    AdmissionRejected(&'static str),
14
15    /// Lifecycle precondition violated — e.g. start_session on a Closed
16    /// Conversation, join_session on an Ended Session, end_session on an
17    /// already-Ended Session. The message identifies which transition
18    /// was rejected so callers can map it to a user-facing error.
19    InvalidState(&'static str),
20
21    /// A codec name reached `codec_to_pt` that no RTP payload-type
22    /// mapping is registered for. Surfaces as a clear "this codec can't
23    /// be bridged" diagnostic instead of being masked as a generic
24    /// transcoder error (carries the codec name for the operator).
25    UnsupportedCodec(String),
26    Adapter(String),
27    Other(anyhow_compat::AnyhowCompat),
28}
29
30impl RvoipError {
31    pub const fn diagnostic_class(&self) -> &'static str {
32        match self {
33            Self::NotImplemented(_) => "not-implemented",
34            Self::NoAdapterForTransport(_) => "adapter-missing",
35            Self::AdapterAlreadyRegistered(_) => "adapter-duplicate",
36            Self::ConnectionNotFound(_) => "connection-not-found",
37            Self::SessionNotFound(_) => "session-not-found",
38            Self::ConversationNotFound(_) => "conversation-not-found",
39            Self::BridgeNotFound(_) => "bridge-not-found",
40            Self::AdmissionRejected(_) => "admission-rejected",
41            Self::InvalidState(_) => "invalid-state",
42            Self::UnsupportedCodec(_) => "unsupported-codec",
43            Self::Adapter(_) => "adapter",
44            Self::Other(_) => "other",
45        }
46    }
47}
48
49impl fmt::Display for RvoipError {
50    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(
52            formatter,
53            "rvoip operation failed (class={})",
54            self.diagnostic_class()
55        )
56    }
57}
58
59impl fmt::Debug for RvoipError {
60    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
61        formatter
62            .debug_struct("RvoipError")
63            .field("class", &self.diagnostic_class())
64            .finish()
65    }
66}
67
68impl std::error::Error for RvoipError {}
69
70impl From<anyhow_compat::AnyhowCompat> for RvoipError {
71    fn from(error: anyhow_compat::AnyhowCompat) -> Self {
72        Self::Other(error)
73    }
74}
75
76pub mod anyhow_compat {
77    use std::error::Error as StdError;
78    use std::fmt;
79
80    pub struct AnyhowCompat(pub Box<dyn StdError + Send + Sync + 'static>);
81
82    impl fmt::Display for AnyhowCompat {
83        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84            f.write_str("erased error")
85        }
86    }
87
88    impl fmt::Debug for AnyhowCompat {
89        fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
90            formatter.write_str("AnyhowCompat([redacted])")
91        }
92    }
93
94    impl StdError for AnyhowCompat {
95        fn source(&self) -> Option<&(dyn StdError + 'static)> {
96            None
97        }
98    }
99}