use derive_more::Display;
mod controller;
pub mod media_exchange_state;
pub mod mute_state;
use derive_more::From;
use medea_client_api_proto::{TrackId, TrackPatchCommand};
#[doc(inline)]
pub use self::controller::{
MediaExchangeStateController, MuteStateController,
TransitableStateController,
};
pub type MediaExchangeState = TransitableState<
media_exchange_state::Stable,
media_exchange_state::Transition,
>;
pub type MuteState =
TransitableState<mute_state::Stable, mute_state::Transition>;
#[derive(Clone, Copy, Debug, Display, From)]
pub enum MediaState {
Mute(mute_state::Stable),
MediaExchange(media_exchange_state::Stable),
}
impl MediaState {
#[must_use]
pub fn generate_track_patch(self, track_id: TrackId) -> TrackPatchCommand {
match self {
Self::Mute(mute) => TrackPatchCommand {
id: track_id,
muted: Some(mute == mute_state::Stable::Muted),
enabled: None,
},
Self::MediaExchange(media_exchange) => TrackPatchCommand {
id: track_id,
enabled: Some(
media_exchange == media_exchange_state::Stable::Enabled,
),
muted: None,
},
}
}
#[must_use]
pub const fn opposite(self) -> Self {
match self {
Self::Mute(mute) => Self::Mute(mute.opposite()),
Self::MediaExchange(media_exchange) => {
Self::MediaExchange(media_exchange.opposite())
}
}
}
}
pub trait InStable: Clone + Copy + PartialEq {
type Transition: InTransition;
#[must_use]
fn start_transition(self) -> Self::Transition;
}
pub trait InTransition: Clone + Copy + PartialEq {
type Stable: InStable;
#[must_use]
fn intended(self) -> Self::Stable;
#[must_use]
fn set_inner(self, inner: Self::Stable) -> Self;
#[must_use]
fn into_inner(self) -> Self::Stable;
#[must_use]
fn opposite(self) -> Self;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TransitableState<S, T> {
Transition(T),
Stable(S),
}
impl<S, T> TransitableState<S, T>
where
T: InTransition<Stable = S> + Into<Self>,
S: InStable<Transition = T> + Into<Self>,
{
#[must_use]
pub fn transition_to(self, desired_state: S) -> Self {
if self == desired_state.into() {
return self;
}
match self {
Self::Stable(stable) => stable.start_transition().into(),
Self::Transition(transition) => {
if transition.intended() == desired_state {
self
} else {
transition.opposite().into()
}
}
}
}
#[must_use]
pub fn cancel_transition(self) -> Self {
match self {
Self::Stable(_) => self,
Self::Transition(t) => t.into_inner().into(),
}
}
}
impl From<media_exchange_state::Stable> for MediaExchangeState {
fn from(from: media_exchange_state::Stable) -> Self {
Self::Stable(from)
}
}
impl From<media_exchange_state::Transition> for MediaExchangeState {
fn from(from: media_exchange_state::Transition) -> Self {
Self::Transition(from)
}
}
impl From<mute_state::Stable> for MuteState {
fn from(from: mute_state::Stable) -> Self {
Self::Stable(from)
}
}
impl From<mute_state::Transition> for MuteState {
fn from(from: mute_state::Transition) -> Self {
Self::Transition(from)
}
}
#[cfg(test)]
mod test {
use super::*;
const DISABLED: MediaExchangeState =
TransitableState::Stable(media_exchange_state::Stable::Disabled);
const ENABLED: MediaExchangeState =
TransitableState::Stable(media_exchange_state::Stable::Enabled);
const ENABLING_DISABLED: MediaExchangeState = TransitableState::Transition(
media_exchange_state::Transition::Enabling(
media_exchange_state::Stable::Disabled,
),
);
const ENABLING_ENABLED: MediaExchangeState = TransitableState::Transition(
media_exchange_state::Transition::Enabling(
media_exchange_state::Stable::Enabled,
),
);
const DISABLING_DISABLED: MediaExchangeState = TransitableState::Transition(
media_exchange_state::Transition::Disabling(
media_exchange_state::Stable::Disabled,
),
);
const DISABLING_ENABLED: MediaExchangeState = TransitableState::Transition(
media_exchange_state::Transition::Disabling(
media_exchange_state::Stable::Enabled,
),
);
#[test]
fn transition_to() {
assert_eq!(
DISABLED.transition_to(media_exchange_state::Stable::Disabled),
DISABLED,
);
assert_eq!(
DISABLED.transition_to(media_exchange_state::Stable::Enabled),
ENABLING_DISABLED,
);
assert_eq!(
ENABLED.transition_to(media_exchange_state::Stable::Enabled),
ENABLED,
);
assert_eq!(
ENABLED.transition_to(media_exchange_state::Stable::Disabled),
DISABLING_ENABLED,
);
assert_eq!(
ENABLING_DISABLED
.transition_to(media_exchange_state::Stable::Disabled),
DISABLING_DISABLED,
);
assert_eq!(
ENABLING_DISABLED
.transition_to(media_exchange_state::Stable::Enabled),
ENABLING_DISABLED,
);
assert_eq!(
DISABLING_ENABLED
.transition_to(media_exchange_state::Stable::Disabled),
DISABLING_ENABLED,
);
assert_eq!(
DISABLING_ENABLED
.transition_to(media_exchange_state::Stable::Enabled),
ENABLING_ENABLED,
);
assert_eq!(
DISABLING_DISABLED
.transition_to(media_exchange_state::Stable::Disabled),
DISABLING_DISABLED,
);
assert_eq!(
DISABLING_DISABLED
.transition_to(media_exchange_state::Stable::Enabled),
ENABLING_DISABLED,
);
assert_eq!(
ENABLING_ENABLED
.transition_to(media_exchange_state::Stable::Disabled),
DISABLING_ENABLED,
);
assert_eq!(
ENABLING_ENABLED
.transition_to(media_exchange_state::Stable::Enabled),
ENABLING_ENABLED,
);
}
#[test]
fn cancel_transition() {
assert_eq!(DISABLED.cancel_transition(), DISABLED);
assert_eq!(ENABLED.cancel_transition(), ENABLED);
assert_eq!(ENABLING_DISABLED.cancel_transition(), DISABLED);
assert_eq!(ENABLING_ENABLED.cancel_transition(), ENABLED);
assert_eq!(DISABLING_DISABLED.cancel_transition(), DISABLED);
assert_eq!(DISABLING_ENABLED.cancel_transition(), ENABLED);
}
}