#![allow(clippy::multiple_inherent_impl)] use std::borrow::Cow;
#[cfg(target_family = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
use tracerr::{Trace, Traced};
use crate::{
api::Error,
connection,
media::{
self, EnumerateDevicesError, EnumerateDisplaysError,
GetDisplayMediaError, GetUserMediaError, InitLocalTracksError,
InvalidOutputAudioDeviceIdError, MicVolumeError,
},
peer::{
sender::CreateError, InsertLocalTracksError, LocalMediaError,
UpdateLocalStreamError,
},
platform, room,
rpc::{rpc_session::ConnectionLostReason, ReconnectError, SessionError},
utils::Caused,
};
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct StateError {
message: Cow<'static, str>,
trace: Trace,
}
impl StateError {
#[must_use]
pub fn new<T: Into<Cow<'static, str>>>(message: T, trace: Trace) -> Self {
Self {
message: message.into(),
trace,
}
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl StateError {
#[must_use]
pub fn message(&self) -> String {
self.message.to_string()
}
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum LocalMediaInitExceptionKind {
GetUserMediaFailed,
GetUserMediaAudioFailed,
GetUserMediaVideoFailed,
GetDisplayMediaFailed,
LocalTrackIsEnded,
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct LocalMediaInitException {
kind: LocalMediaInitExceptionKind,
message: Cow<'static, str>,
cause: Option<platform::Error>,
trace: Trace,
}
impl LocalMediaInitException {
#[must_use]
pub fn new<M: Into<Cow<'static, str>>>(
kind: LocalMediaInitExceptionKind,
message: M,
cause: Option<platform::Error>,
trace: Trace,
) -> Self {
Self {
kind,
message: message.into(),
cause,
trace,
}
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl LocalMediaInitException {
#[allow(clippy::missing_const_for_fn)]
#[must_use]
pub fn kind(&self) -> LocalMediaInitExceptionKind {
self.kind
}
#[must_use]
pub fn message(&self) -> String {
self.message.to_string()
}
#[must_use]
pub fn cause(&self) -> Option<platform::Error> {
self.cause.clone()
}
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct EnumerateDevicesException {
cause: platform::Error,
trace: Trace,
}
impl EnumerateDevicesException {
#[must_use]
pub const fn new(cause: platform::Error, trace: Trace) -> Self {
Self { cause, trace }
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl EnumerateDevicesException {
#[must_use]
pub fn cause(&self) -> platform::Error {
self.cause.clone()
}
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct InvalidOutputAudioDeviceIdException {
trace: Trace,
}
impl InvalidOutputAudioDeviceIdException {
#[must_use]
pub const fn new(trace: Trace) -> Self {
Self { trace }
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl InvalidOutputAudioDeviceIdException {
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct MicVolumeException {
cause: platform::Error,
trace: Trace,
}
impl MicVolumeException {
#[must_use]
pub const fn new(cause: platform::Error, trace: Trace) -> Self {
Self { cause, trace }
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl MicVolumeException {
#[must_use]
pub fn cause(&self) -> platform::Error {
self.cause.clone()
}
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum RpcClientExceptionKind {
ConnectionLost,
AuthorizationFailed,
SessionFinished,
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct RpcClientException {
kind: RpcClientExceptionKind,
message: Cow<'static, str>,
cause: Option<platform::Error>,
trace: Trace,
}
impl RpcClientException {
#[must_use]
pub fn new<M: Into<Cow<'static, str>>>(
kind: RpcClientExceptionKind,
message: M,
cause: Option<platform::Error>,
trace: Trace,
) -> Self {
Self {
kind,
message: message.into(),
cause,
trace,
}
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl RpcClientException {
#[allow(clippy::missing_const_for_fn)] #[must_use]
pub fn kind(&self) -> RpcClientExceptionKind {
self.kind
}
#[must_use]
pub fn message(&self) -> String {
self.message.to_string()
}
#[must_use]
pub fn cause(&self) -> Option<platform::Error> {
self.cause.clone()
}
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct InternalException {
message: Cow<'static, str>,
cause: Option<platform::Error>,
trace: Trace,
}
impl InternalException {
#[must_use]
pub fn new<T: Into<Cow<'static, str>>>(
message: T,
cause: Option<platform::Error>,
trace: Trace,
) -> Self {
Self {
message: message.into(),
trace,
cause,
}
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl InternalException {
#[must_use]
pub fn message(&self) -> String {
self.message.to_string()
}
#[must_use]
pub fn cause(&self) -> Option<platform::Error> {
self.cause.clone()
}
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct FormatException(Cow<'static, str>);
impl FormatException {
#[must_use]
pub fn new<T: Into<Cow<'static, str>>>(message: T) -> Self {
Self(message.into())
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl FormatException {
#[must_use]
pub fn message(&self) -> String {
self.0.to_string()
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Clone, Copy, Debug)]
pub enum MediaStateTransitionExceptionKind {
OppositeState,
ProhibitedState,
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct MediaStateTransitionException {
message: Cow<'static, str>,
kind: MediaStateTransitionExceptionKind,
trace: Trace,
}
impl MediaStateTransitionException {
#[must_use]
pub fn new<T: Into<Cow<'static, str>>>(
message: T,
trace: Trace,
kind: MediaStateTransitionExceptionKind,
) -> Self {
Self {
message: message.into(),
trace,
kind,
}
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl MediaStateTransitionException {
#[must_use]
pub fn message(&self) -> String {
self.message.to_string()
}
#[must_use]
pub fn trace(&self) -> String {
self.trace.to_string()
}
#[allow(clippy::missing_const_for_fn)]
#[must_use]
pub fn kind(&self) -> MediaStateTransitionExceptionKind {
self.kind
}
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[derive(Debug)]
pub struct MediaSettingsUpdateException {
message: Cow<'static, str>,
cause: Traced<room::ChangeMediaStateError>,
rolled_back: bool,
}
impl MediaSettingsUpdateException {
#[must_use]
pub fn new<T: Into<Cow<'static, str>>>(
message: T,
cause: Traced<room::ChangeMediaStateError>,
rolled_back: bool,
) -> Self {
Self {
message: message.into(),
rolled_back,
cause,
}
}
}
#[cfg_attr(target_family = "wasm", allow(clippy::unused_unit))]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl MediaSettingsUpdateException {
#[must_use]
pub fn message(&self) -> String {
self.message.to_string()
}
#[must_use]
pub fn cause(&self) -> Error {
self.cause.clone().into()
}
#[allow(clippy::missing_const_for_fn)]
#[must_use]
pub fn rolled_back(&self) -> bool {
self.rolled_back
}
}
impl From<Traced<media::HandleDetachedError>> for Error {
fn from(err: Traced<media::HandleDetachedError>) -> Self {
let (err, trace) = err.split();
StateError::new(err.to_string(), trace).into()
}
}
impl From<Traced<connection::HandleDetachedError>> for Error {
fn from(err: Traced<connection::HandleDetachedError>) -> Self {
let (err, trace) = err.split();
StateError::new(err.to_string(), trace).into()
}
}
impl From<Traced<room::HandleDetachedError>> for Error {
fn from(err: Traced<room::HandleDetachedError>) -> Self {
let (err, trace) = err.split();
StateError::new(err.to_string(), trace).into()
}
}
impl From<Traced<EnumerateDevicesError>> for Error {
fn from(err: Traced<EnumerateDevicesError>) -> Self {
let (err, stacktrace) = err.split();
match err {
EnumerateDevicesError::Failed(err) => {
EnumerateDevicesException::new(err, stacktrace).into()
}
EnumerateDevicesError::Detached => {
StateError::new(err.to_string(), stacktrace).into()
}
}
}
}
impl From<Traced<EnumerateDisplaysError>> for Error {
fn from(err: Traced<EnumerateDisplaysError>) -> Self {
let (err, stacktrace) = err.split();
match err {
EnumerateDisplaysError::Failed(err) => {
InternalException::new(err.to_string(), Some(err), stacktrace)
.into()
}
EnumerateDisplaysError::Detached => {
StateError::new(err.to_string(), stacktrace).into()
}
}
}
}
impl From<Traced<InvalidOutputAudioDeviceIdError>> for Error {
fn from(err: Traced<InvalidOutputAudioDeviceIdError>) -> Self {
let (_, trace) = err.split();
InvalidOutputAudioDeviceIdException::new(trace).into()
}
}
impl From<Traced<MicVolumeError>> for Error {
fn from(err: Traced<MicVolumeError>) -> Self {
let (err, stacktrace) = err.split();
match err {
MicVolumeError::MicVolumeError(err) => {
MicVolumeException::new(err, stacktrace).into()
}
MicVolumeError::Detached => {
StateError::new(err.to_string(), stacktrace).into()
}
}
}
}
impl From<Traced<InitLocalTracksError>> for Error {
fn from(err: Traced<InitLocalTracksError>) -> Self {
use GetDisplayMediaError as Gdm;
use GetUserMediaError as Gum;
use InitLocalTracksError as Err;
use LocalMediaInitExceptionKind as Kind;
let (err, stacktrace) = err.split();
let message = err.to_string();
let (kind, cause) = match err {
Err::Detached => {
return StateError::new(message, stacktrace).into()
}
Err::GetUserMediaFailed(Gum::PlatformRequestFailed(
platform::GetUserMediaError::Audio(cause),
)) => (Kind::GetUserMediaAudioFailed, Some(cause)),
Err::GetUserMediaFailed(Gum::PlatformRequestFailed(
platform::GetUserMediaError::Video(cause),
)) => (Kind::GetUserMediaVideoFailed, Some(cause)),
Err::GetUserMediaFailed(Gum::PlatformRequestFailed(
platform::GetUserMediaError::Unknown(cause),
)) => (Kind::GetUserMediaFailed, Some(cause)),
Err::GetDisplayMediaFailed(Gdm::PlatformRequestFailed(cause)) => {
(Kind::GetDisplayMediaFailed, Some(cause))
}
Err::GetUserMediaFailed(Gum::LocalTrackIsEnded(_))
| Err::GetDisplayMediaFailed(Gdm::LocalTrackIsEnded(_)) => {
(Kind::LocalTrackIsEnded, None)
}
};
LocalMediaInitException::new(kind, message, cause, stacktrace).into()
}
}
impl From<Traced<ReconnectError>> for Error {
fn from(err: Traced<ReconnectError>) -> Self {
let (err, trace) = err.split();
match err {
ReconnectError::Detached => {
StateError::new(err.to_string(), trace).into()
}
ReconnectError::Session(err) => Traced::compose(err, trace).into(),
}
}
}
impl From<Traced<SessionError>> for Error {
fn from(err: Traced<SessionError>) -> Self {
use ConnectionLostReason as Reason;
use RpcClientExceptionKind as Kind;
use SessionError as SE;
let (err, trace) = err.split();
let message = err.to_string();
let mut cause = None;
let kind = match err {
SE::SessionFinished(_) => Some(Kind::SessionFinished),
SE::NoCredentials
| SE::SessionUnexpectedlyDropped
| SE::NewConnectionInfo => None,
SE::RpcClient(e) => {
cause = e.cause();
None
}
SE::AuthorizationFailed => Some(Kind::AuthorizationFailed),
SE::ConnectionLost(reason) => {
if let Reason::ConnectError(e) = reason {
cause = e.into_inner().cause();
};
Some(Kind::ConnectionLost)
}
};
if let Some(rpc_kind) = kind {
RpcClientException::new(rpc_kind, message, cause, trace).into()
} else {
InternalException::new(message, cause, trace).into()
}
}
}
impl From<Traced<room::RoomJoinError>> for Error {
fn from(err: Traced<room::RoomJoinError>) -> Self {
let (err, trace) = err.split();
let message = err.to_string();
match err {
room::RoomJoinError::Detached
| room::RoomJoinError::CallbackNotSet(_) => {
StateError::new(message, trace).into()
}
room::RoomJoinError::ConnectionInfoParse(_) => {
FormatException::new(message).into()
}
room::RoomJoinError::SessionError(err) => {
Traced::compose(err, trace).into()
}
}
}
}
impl From<Traced<connection::ChangeMediaStateError>> for Error {
fn from(err: Traced<connection::ChangeMediaStateError>) -> Self {
let (err, trace) = err.split();
let message = err.to_string();
match err {
connection::ChangeMediaStateError::Detached => {
StateError::new(err.to_string(), trace).into()
}
connection::ChangeMediaStateError::ProhibitedState(_) => {
MediaStateTransitionException::new(
message,
trace,
MediaStateTransitionExceptionKind::ProhibitedState,
)
.into()
}
connection::ChangeMediaStateError::TransitionIntoOppositeState(
_,
) => MediaStateTransitionException::new(
message,
trace,
MediaStateTransitionExceptionKind::OppositeState,
)
.into(),
}
}
}
impl From<Traced<room::ChangeMediaStateError>> for Error {
fn from(err: Traced<room::ChangeMediaStateError>) -> Self {
let (err, trace) = err.split();
let message = err.to_string();
match err {
room::ChangeMediaStateError::Detached => {
StateError::new(err.to_string(), trace).into()
}
room::ChangeMediaStateError::CouldNotGetLocalMedia(err) => {
Traced::compose(err, trace).into()
}
room::ChangeMediaStateError::ProhibitedState(_) => {
MediaStateTransitionException::new(
message,
trace,
MediaStateTransitionExceptionKind::ProhibitedState,
)
.into()
}
room::ChangeMediaStateError::TransitionIntoOppositeState(_) => {
MediaStateTransitionException::new(
message,
trace,
MediaStateTransitionExceptionKind::OppositeState,
)
.into()
}
room::ChangeMediaStateError::InvalidLocalTracks(_)
| room::ChangeMediaStateError::InsertLocalTracksError(_) => {
InternalException::new(message, None, trace).into()
}
}
}
}
impl From<room::ConstraintsUpdateError> for Error {
fn from(err: room::ConstraintsUpdateError) -> Self {
let message = err.to_string();
let (err, rolled_back) = match err {
room::ConstraintsUpdateError::Recovered(err) => (err, true),
room::ConstraintsUpdateError::RecoverFailed {
recover_reason,
..
} => (recover_reason, false),
room::ConstraintsUpdateError::Errored(err) => (err, false),
};
MediaSettingsUpdateException::new(message, err, rolled_back).into()
}
}
impl From<Traced<LocalMediaError>> for Error {
fn from(err: Traced<LocalMediaError>) -> Self {
use InsertLocalTracksError as IE;
use LocalMediaError as ME;
use UpdateLocalStreamError as UE;
let (err, trace) = err.split();
let message = err.to_string();
match err {
ME::UpdateLocalStreamError(err) => match err {
UE::CouldNotGetLocalMedia(err) => {
Traced::compose(err, trace).into()
}
UE::InvalidLocalTracks(_)
| UE::InsertLocalTracksError(
IE::InvalidMediaTrack | IE::NotEnoughTracks,
) => InternalException::new(message, None, trace).into(),
UE::InsertLocalTracksError(IE::CouldNotInsertLocalTrack(_)) => {
InternalException::new(message, None, trace).into()
}
},
ME::SenderCreateError(CreateError::TransceiverNotFound(_)) => {
InternalException::new(message, None, trace).into()
}
ME::SenderCreateError(CreateError::CannotDisableRequiredSender) => {
MediaStateTransitionException::new(
message,
trace,
MediaStateTransitionExceptionKind::ProhibitedState,
)
.into()
}
}
}
}