Implement Improved Signalling for 1:1 VoIP
According to MSC2746
This commit is contained in:
parent
67d6df3dd2
commit
906d576a27
@ -10,9 +10,9 @@ Breaking changes:
|
|||||||
* Add `user_id` field to `PushConditionRoomCtx`
|
* Add `user_id` field to `PushConditionRoomCtx`
|
||||||
* Remove `PartialEq` implementation on `NotificationPowerLevels`
|
* Remove `PartialEq` implementation on `NotificationPowerLevels`
|
||||||
* Remove `PartialEq` implementation for `events::call::SessionDescription`
|
* Remove `PartialEq` implementation for `events::call::SessionDescription`
|
||||||
* Split `events::call::SessionDescription` into `AnswerSessionDescription`
|
* Use new `events::call::AnswerSessionDescription` for `CallAnswerEventContent`
|
||||||
and `OfferSessionDescription`
|
and `OfferSessionDescription` for `CallInviteEventContent`
|
||||||
* Remove `SessionDescriptionType`
|
* Use new `VoipVersionId` and `VoipId` types for `events::call` events
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
|
|
||||||
@ -20,6 +20,7 @@ Improvements:
|
|||||||
* Change `events::relation::BundledAnnotation` to a struct instead of an enum
|
* Change `events::relation::BundledAnnotation` to a struct instead of an enum
|
||||||
* Remove `BundledReaction`
|
* Remove `BundledReaction`
|
||||||
* Add unstable support for polls (MSC3381)
|
* Add unstable support for polls (MSC3381)
|
||||||
|
* Add unstable support for Improved Signalling for 1:1 VoIP (MSC2746)
|
||||||
|
|
||||||
# 0.9.2
|
# 0.9.2
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ unstable-msc2448 = []
|
|||||||
unstable-msc2675 = []
|
unstable-msc2675 = []
|
||||||
unstable-msc2676 = []
|
unstable-msc2676 = []
|
||||||
unstable-msc2677 = []
|
unstable-msc2677 = []
|
||||||
|
unstable-msc2746 = []
|
||||||
unstable-msc3245 = ["unstable-msc3246"]
|
unstable-msc3245 = ["unstable-msc3246"]
|
||||||
unstable-msc3246 = ["unstable-msc3551", "thiserror"]
|
unstable-msc3246 = ["unstable-msc3551", "thiserror"]
|
||||||
unstable-msc3381 = ["unstable-msc1767"]
|
unstable-msc3381 = ["unstable-msc1767"]
|
||||||
|
@ -6,9 +6,74 @@ pub mod answer;
|
|||||||
pub mod candidates;
|
pub mod candidates;
|
||||||
pub mod hangup;
|
pub mod hangup;
|
||||||
pub mod invite;
|
pub mod invite;
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
pub mod negotiate;
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
pub mod reject;
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
pub mod select_answer;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{serde::StringEnum, PrivOwnedStr};
|
||||||
|
|
||||||
|
/// A VoIP session description.
|
||||||
|
///
|
||||||
|
/// This is the same type as WebRTC's [`RTCSessionDescriptionInit`].
|
||||||
|
///
|
||||||
|
/// [`RTCSessionDescriptionInit`]: (https://www.w3.org/TR/webrtc/#dom-rtcsessiondescriptioninit):
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub struct SessionDescription {
|
||||||
|
/// The type of session description.
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub session_type: SessionDescriptionType,
|
||||||
|
|
||||||
|
/// The SDP text of the session description.
|
||||||
|
///
|
||||||
|
/// With the `unstable-msc2746` feature, this field is unused if the type is `rollback` and
|
||||||
|
/// defaults to an empty string.
|
||||||
|
#[cfg_attr(feature = "unstable-msc2746", serde(default))]
|
||||||
|
pub sdp: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionDescription {
|
||||||
|
/// Creates a new `SessionDescription` with the given session type and SDP text.
|
||||||
|
pub fn new(session_type: SessionDescriptionType, sdp: String) -> Self {
|
||||||
|
Self { session_type, sdp }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of VoIP session description.
|
||||||
|
///
|
||||||
|
/// This is the same type as WebRTC's [`RTCSdpType`].
|
||||||
|
///
|
||||||
|
/// [`RTCSdpType`]: (https://www.w3.org/TR/webrtc/#dom-rtcsdptype):
|
||||||
|
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
|
||||||
|
#[ruma_enum(rename_all = "lowercase")]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum SessionDescriptionType {
|
||||||
|
/// The description must be treated as an SDP final answer, and the offer-answer exchange must
|
||||||
|
/// be considered complete.
|
||||||
|
Answer,
|
||||||
|
|
||||||
|
/// The description must be treated as an SDP offer.
|
||||||
|
Offer,
|
||||||
|
|
||||||
|
/// The description must be treated as an SDP answer, but not final.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
PrAnswer,
|
||||||
|
|
||||||
|
/// The description must be treated as cancelling the current SDP negotiation and moving the
|
||||||
|
/// SDP offer back to what it was in the previous stable state.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
Rollback,
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
_Custom(PrivOwnedStr),
|
||||||
|
}
|
||||||
|
|
||||||
/// A VoIP answer session description.
|
/// A VoIP answer session description.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
@ -40,3 +105,30 @@ impl OfferSessionDescription {
|
|||||||
Self { sdp }
|
Self { sdp }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The capabilities of a client in a VoIP call.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub struct CallCapabilities {
|
||||||
|
/// Whether this client supports [DTMF].
|
||||||
|
///
|
||||||
|
/// Defaults to `false`.
|
||||||
|
///
|
||||||
|
/// [DTMF]: https://w3c.github.io/webrtc-pc/#peer-to-peer-dtmf
|
||||||
|
#[serde(rename = "m.call.dtmf", default)]
|
||||||
|
pub dtmf: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
impl CallCapabilities {
|
||||||
|
/// Creates a default `CallCapabilities`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this `CallCapabilities` only contains default values.
|
||||||
|
pub fn is_default(&self) -> bool {
|
||||||
|
!self.dtmf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
//!
|
//!
|
||||||
//! [`m.call.answer`]: https://spec.matrix.org/v1.2/client-server-api/#mcallanswer
|
//! [`m.call.answer`]: https://spec.matrix.org/v1.2/client-server-api/#mcallanswer
|
||||||
|
|
||||||
use js_int::UInt;
|
|
||||||
use ruma_macros::EventContent;
|
use ruma_macros::EventContent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::AnswerSessionDescription;
|
use super::AnswerSessionDescription;
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
use super::CallCapabilities;
|
||||||
|
use crate::{OwnedVoipId, VoipVersionId};
|
||||||
|
|
||||||
/// The content of an `m.call.answer` event.
|
/// The content of an `m.call.answer` event.
|
||||||
///
|
///
|
||||||
@ -18,16 +20,56 @@ pub struct CallAnswerEventContent {
|
|||||||
/// The VoIP session description object.
|
/// The VoIP session description object.
|
||||||
pub answer: AnswerSessionDescription,
|
pub answer: AnswerSessionDescription,
|
||||||
|
|
||||||
/// The ID of the call this event relates to.
|
/// A unique identifier for the call.
|
||||||
pub call_id: String,
|
pub call_id: OwnedVoipId,
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
/// **Required in VoIP version 1.** A unique ID for this session for the duration of the call.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub party_id: Option<OwnedVoipId>,
|
||||||
|
|
||||||
/// The version of the VoIP specification this messages adheres to.
|
/// The version of the VoIP specification this messages adheres to.
|
||||||
pub version: UInt,
|
pub version: VoipVersionId,
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
/// **Added in VoIP version 1.** The VoIP capabilities of the client.
|
||||||
|
#[serde(default, skip_serializing_if = "CallCapabilities::is_default")]
|
||||||
|
pub capabilities: CallCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallAnswerEventContent {
|
impl CallAnswerEventContent {
|
||||||
/// Creates an `AnswerEventContent` with the given answer, call ID and VoIP version.
|
/// Creates an `CallAnswerEventContent` with the given answer, call ID and VoIP version.
|
||||||
pub fn new(answer: AnswerSessionDescription, call_id: String, version: UInt) -> Self {
|
pub fn new(
|
||||||
Self { answer, call_id, version }
|
answer: AnswerSessionDescription,
|
||||||
|
call_id: OwnedVoipId,
|
||||||
|
version: VoipVersionId,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
answer,
|
||||||
|
call_id,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
party_id: None,
|
||||||
|
version,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
capabilities: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a VoIP version 0 `CallAnswerEventContent` with all the required
|
||||||
|
/// fields.
|
||||||
|
pub fn version_0(answer: AnswerSessionDescription, call_id: OwnedVoipId) -> Self {
|
||||||
|
Self::new(answer, call_id, VoipVersionId::V0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a VoIP version 1 `CallAnswerEventContent` with all the required
|
||||||
|
/// fields.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
pub fn version_1(
|
||||||
|
answer: AnswerSessionDescription,
|
||||||
|
call_id: OwnedVoipId,
|
||||||
|
party_id: OwnedVoipId,
|
||||||
|
capabilities: CallCapabilities,
|
||||||
|
) -> Self {
|
||||||
|
Self { answer, call_id, party_id: Some(party_id), version: VoipVersionId::V1, capabilities }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ use js_int::UInt;
|
|||||||
use ruma_macros::EventContent;
|
use ruma_macros::EventContent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{OwnedVoipId, VoipVersionId};
|
||||||
|
|
||||||
/// The content of an `m.call.candidates` event.
|
/// The content of an `m.call.candidates` event.
|
||||||
///
|
///
|
||||||
/// This event is sent by callers after sending an invite and by the callee after answering. Its
|
/// This event is sent by callers after sending an invite and by the callee after answering. Its
|
||||||
@ -14,21 +16,56 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[ruma_event(type = "m.call.candidates", kind = MessageLike)]
|
#[ruma_event(type = "m.call.candidates", kind = MessageLike)]
|
||||||
pub struct CallCandidatesEventContent {
|
pub struct CallCandidatesEventContent {
|
||||||
/// The ID of the call this event relates to.
|
/// A unique identifier for the call.
|
||||||
pub call_id: String,
|
pub call_id: OwnedVoipId,
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
/// **Required in VoIP version 1.** The unique ID for this session for the duration of the
|
||||||
|
/// call.
|
||||||
|
///
|
||||||
|
/// Must be the same as the one sent by the previous invite or answer from
|
||||||
|
/// this session.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub party_id: Option<OwnedVoipId>,
|
||||||
|
|
||||||
/// A list of candidates.
|
/// A list of candidates.
|
||||||
|
///
|
||||||
|
/// With the `unstable-msc2746` feature, in VoIP version 1, this list should end with a
|
||||||
|
/// `Candidate` with an empty `candidate` field when no more candidates will be sent.
|
||||||
pub candidates: Vec<Candidate>,
|
pub candidates: Vec<Candidate>,
|
||||||
|
|
||||||
/// The version of the VoIP specification this messages adheres to.
|
/// The version of the VoIP specification this messages adheres to.
|
||||||
pub version: UInt,
|
pub version: VoipVersionId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallCandidatesEventContent {
|
impl CallCandidatesEventContent {
|
||||||
/// Creates a new `CandidatesEventContent` with the given call id, candidate list and VoIP
|
/// Creates a new `CallCandidatesEventContent` with the given call id, candidate list and VoIP
|
||||||
/// version.
|
/// version.
|
||||||
pub fn new(call_id: String, candidates: Vec<Candidate>, version: UInt) -> Self {
|
pub fn new(call_id: OwnedVoipId, candidates: Vec<Candidate>, version: VoipVersionId) -> Self {
|
||||||
Self { call_id, candidates, version }
|
Self {
|
||||||
|
call_id,
|
||||||
|
candidates,
|
||||||
|
version,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
party_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a VoIP version 0 `CallCandidatesEventContent` with all the
|
||||||
|
/// required fields.
|
||||||
|
pub fn version_0(call_id: OwnedVoipId, candidates: Vec<Candidate>) -> Self {
|
||||||
|
Self::new(call_id, candidates, VoipVersionId::V0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a VoIP version 1 `CallCandidatesEventContent` with all the
|
||||||
|
/// required fields.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
pub fn version_1(
|
||||||
|
call_id: OwnedVoipId,
|
||||||
|
party_id: OwnedVoipId,
|
||||||
|
candidates: Vec<Candidate>,
|
||||||
|
) -> Self {
|
||||||
|
Self { call_id, party_id: Some(party_id), candidates, version: VoipVersionId::V1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,35 +2,80 @@
|
|||||||
//!
|
//!
|
||||||
//! [`m.call.hangup`]: https://spec.matrix.org/v1.2/client-server-api/#mcallhangup
|
//! [`m.call.hangup`]: https://spec.matrix.org/v1.2/client-server-api/#mcallhangup
|
||||||
|
|
||||||
use js_int::UInt;
|
|
||||||
use ruma_macros::EventContent;
|
use ruma_macros::EventContent;
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
use serde::Serializer;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{serde::StringEnum, PrivOwnedStr};
|
use crate::{serde::StringEnum, OwnedVoipId, PrivOwnedStr, VoipVersionId};
|
||||||
|
|
||||||
/// The content of an `m.call.hangup` event.
|
/// The content of an `m.call.hangup` event.
|
||||||
///
|
///
|
||||||
/// Sent by either party to signal their termination of the call. This can be sent either once the
|
/// Sent by either party to signal their termination of the call.
|
||||||
/// call has has been established or before to abort the call.
|
///
|
||||||
|
/// In VoIP version 0, this can be sent either once the call has been established or before to abort
|
||||||
|
/// the call.
|
||||||
|
///
|
||||||
|
/// With the `unstable-msc2746` feature, and if the call is using VoIP version 1, this should only
|
||||||
|
/// be sent by the caller after sending the invite or by the callee after answering the invite. To
|
||||||
|
/// reject an invite, send an [`m.call.reject`] event.
|
||||||
|
///
|
||||||
|
/// [`m.call.reject`]: super::reject::CallRejectEventContent
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[ruma_event(type = "m.call.hangup", kind = MessageLike)]
|
#[ruma_event(type = "m.call.hangup", kind = MessageLike)]
|
||||||
pub struct CallHangupEventContent {
|
pub struct CallHangupEventContent {
|
||||||
/// The ID of the call this event relates to.
|
/// A unique identifier for the call.
|
||||||
pub call_id: String,
|
pub call_id: OwnedVoipId,
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
/// **Required in VoIP version 1.** A unique ID for this session for the duration of the call.
|
||||||
|
///
|
||||||
|
/// Must be the same as the one sent by the previous invite or answer from
|
||||||
|
/// this session.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub party_id: Option<OwnedVoipId>,
|
||||||
|
|
||||||
/// The version of the VoIP specification this messages adheres to.
|
/// The version of the VoIP specification this messages adheres to.
|
||||||
pub version: UInt,
|
pub version: VoipVersionId,
|
||||||
|
|
||||||
/// Optional error reason for the hangup.
|
/// Optional error reason for the hangup.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
///
|
||||||
|
/// With the `unstable-msc2746` feature, this field defaults to `Some(Reason::UserHangup)`.
|
||||||
|
#[cfg_attr(not(feature = "unstable-msc2746"), serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "unstable-msc2746",
|
||||||
|
serde(
|
||||||
|
default = "Reason::option_with_default",
|
||||||
|
serialize_with = "Reason::serialize_option_with_default"
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub reason: Option<Reason>,
|
pub reason: Option<Reason>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallHangupEventContent {
|
impl CallHangupEventContent {
|
||||||
/// Creates a new `HangupEventContent` with the given call ID and VoIP version.
|
/// Creates a new `CallHangupEventContent` with the given call ID and VoIP version.
|
||||||
pub fn new(call_id: String, version: UInt) -> Self {
|
pub fn new(call_id: OwnedVoipId, version: VoipVersionId) -> Self {
|
||||||
Self { call_id, version, reason: None }
|
Self {
|
||||||
|
call_id,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
party_id: None,
|
||||||
|
version,
|
||||||
|
reason: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a VoIP version 0 `CallHangupEventContent` with all the required
|
||||||
|
/// fields.
|
||||||
|
pub fn version_0(call_id: OwnedVoipId) -> Self {
|
||||||
|
Self::new(call_id, VoipVersionId::V0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a VoIP version 1 `CallHangupEventContent` with all the required
|
||||||
|
/// fields.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
pub fn version_1(call_id: OwnedVoipId, party_id: OwnedVoipId, reason: Reason) -> Self {
|
||||||
|
Self { call_id, party_id: Some(party_id), version: VoipVersionId::V1, reason: Some(reason) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +95,32 @@ pub enum Reason {
|
|||||||
/// Party did not answer in time.
|
/// Party did not answer in time.
|
||||||
InviteTimeout,
|
InviteTimeout,
|
||||||
|
|
||||||
|
/// The connection failed after some media was exchanged.
|
||||||
|
///
|
||||||
|
/// Note that, in the case of an ICE renegotiation, a client should be sure to send
|
||||||
|
/// `ice_timeout` rather than `ice_failed` if media had previously been received successfully,
|
||||||
|
/// even if the ICE renegotiation itself failed.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
IceTimeout,
|
||||||
|
|
||||||
|
/// The user chose to end the call.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
UserHangup,
|
||||||
|
|
||||||
|
/// The client was unable to start capturing media in such a way as it is unable to continue
|
||||||
|
/// the call.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
UserMediaFailed,
|
||||||
|
|
||||||
|
/// The user is busy.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
UserBusy,
|
||||||
|
|
||||||
|
/// Some other failure occurred that meant the client was unable to continue the call rather
|
||||||
|
/// than the user choosing to end it.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
UnknownError,
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
_Custom(PrivOwnedStr),
|
_Custom(PrivOwnedStr),
|
||||||
}
|
}
|
||||||
@ -59,4 +130,31 @@ impl Reason {
|
|||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
self.as_ref()
|
self.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
fn serialize_option_with_default<S>(
|
||||||
|
reason: &Option<Reason>,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
if let Some(reason) = &reason {
|
||||||
|
reason.serialize(serializer)
|
||||||
|
} else {
|
||||||
|
Self::default().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
fn option_with_default() -> Option<Self> {
|
||||||
|
Some(Self::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
impl Default for Reason {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::UserHangup
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,12 @@ use js_int::UInt;
|
|||||||
use ruma_macros::EventContent;
|
use ruma_macros::EventContent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
use super::CallCapabilities;
|
||||||
use super::OfferSessionDescription;
|
use super::OfferSessionDescription;
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
use crate::OwnedUserId;
|
||||||
|
use crate::{OwnedVoipId, VoipVersionId};
|
||||||
|
|
||||||
/// The content of an `m.call.invite` event.
|
/// The content of an `m.call.invite` event.
|
||||||
///
|
///
|
||||||
@ -16,7 +21,12 @@ use super::OfferSessionDescription;
|
|||||||
#[ruma_event(type = "m.call.invite", kind = MessageLike)]
|
#[ruma_event(type = "m.call.invite", kind = MessageLike)]
|
||||||
pub struct CallInviteEventContent {
|
pub struct CallInviteEventContent {
|
||||||
/// A unique identifier for the call.
|
/// A unique identifier for the call.
|
||||||
pub call_id: String,
|
pub call_id: OwnedVoipId,
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
/// **Required in VoIP version 1.** A unique ID for this session for the duration of the call.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub party_id: Option<OwnedVoipId>,
|
||||||
|
|
||||||
/// The time in milliseconds that the invite is valid for.
|
/// The time in milliseconds that the invite is valid for.
|
||||||
///
|
///
|
||||||
@ -28,17 +38,70 @@ pub struct CallInviteEventContent {
|
|||||||
pub offer: OfferSessionDescription,
|
pub offer: OfferSessionDescription,
|
||||||
|
|
||||||
/// The version of the VoIP specification this messages adheres to.
|
/// The version of the VoIP specification this messages adheres to.
|
||||||
pub version: UInt,
|
pub version: VoipVersionId,
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
/// **Added in VoIP version 1.** The VoIP capabilities of the client.
|
||||||
|
#[serde(default, skip_serializing_if = "CallCapabilities::is_default")]
|
||||||
|
pub capabilities: CallCapabilities,
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
/// **Added in VoIP version 1.** The intended target of the invite, if any.
|
||||||
|
///
|
||||||
|
/// If this is `None`, the invite is intended for any member of the room, except the sender.
|
||||||
|
///
|
||||||
|
/// The invite should be ignored if the invitee is set and doesn't match the user's ID.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub invitee: Option<OwnedUserId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallInviteEventContent {
|
impl CallInviteEventContent {
|
||||||
/// Creates a new `InviteEventContent` with the given call ID, lifetime and VoIP version.
|
/// Creates a new `CallInviteEventContent` with the given call ID, lifetime, offer and VoIP
|
||||||
|
/// version.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
call_id: String,
|
call_id: OwnedVoipId,
|
||||||
lifetime: UInt,
|
lifetime: UInt,
|
||||||
offer: OfferSessionDescription,
|
offer: OfferSessionDescription,
|
||||||
version: UInt,
|
version: VoipVersionId,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { call_id, lifetime, offer, version }
|
Self {
|
||||||
|
call_id,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
party_id: None,
|
||||||
|
lifetime,
|
||||||
|
offer,
|
||||||
|
version,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
capabilities: Default::default(),
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
invitee: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a version 0 `CallInviteEventContent` with all the required
|
||||||
|
/// fields.
|
||||||
|
pub fn version_0(call_id: OwnedVoipId, lifetime: UInt, offer: OfferSessionDescription) -> Self {
|
||||||
|
Self::new(call_id, lifetime, offer, VoipVersionId::V0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a version 1 `CallInviteEventContent` with all the required
|
||||||
|
/// fields.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
pub fn version_1(
|
||||||
|
call_id: OwnedVoipId,
|
||||||
|
party_id: OwnedVoipId,
|
||||||
|
lifetime: UInt,
|
||||||
|
offer: OfferSessionDescription,
|
||||||
|
capabilities: CallCapabilities,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
call_id,
|
||||||
|
party_id: Some(party_id),
|
||||||
|
lifetime,
|
||||||
|
offer,
|
||||||
|
version: VoipVersionId::V1,
|
||||||
|
capabilities,
|
||||||
|
invitee: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
crates/ruma-common/src/events/call/negotiate.rs
Normal file
50
crates/ruma-common/src/events/call/negotiate.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//! Types for the `m.call.negotiate` event [MSC2746].
|
||||||
|
//!
|
||||||
|
//! [MSC2746]: https://github.com/matrix-org/matrix-spec-proposals/pull/2746
|
||||||
|
|
||||||
|
use js_int::UInt;
|
||||||
|
use ruma_macros::EventContent;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::SessionDescription;
|
||||||
|
use crate::OwnedVoipId;
|
||||||
|
|
||||||
|
/// **Added in VoIP version 1.** The content of an `m.call.negotiate` event.
|
||||||
|
///
|
||||||
|
/// This event is sent by either party after the call is established to renegotiate it. It can be
|
||||||
|
/// used for media pause, hold/resume, ICE restarts and voice/video call up/downgrading.
|
||||||
|
///
|
||||||
|
/// First an event must be sent with an `offer` session description, which is replied to with an
|
||||||
|
/// event with an `answer` session description.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[ruma_event(type = "m.call.negotiate", kind = MessageLike)]
|
||||||
|
pub struct CallNegotiateEventContent {
|
||||||
|
/// The ID of the call this event relates to.
|
||||||
|
pub call_id: OwnedVoipId,
|
||||||
|
|
||||||
|
/// The unique ID for this session for the duration of the call.
|
||||||
|
///
|
||||||
|
/// Must be the same as the one sent by the previous invite or answer from
|
||||||
|
/// this session.
|
||||||
|
pub party_id: OwnedVoipId,
|
||||||
|
|
||||||
|
/// The time in milliseconds that the negotiation is valid for.
|
||||||
|
pub lifetime: UInt,
|
||||||
|
|
||||||
|
/// The session description of the negotiation.
|
||||||
|
pub description: SessionDescription,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallNegotiateEventContent {
|
||||||
|
/// Creates a `CallNegotiateEventContent` with the given call ID, party ID, lifetime and
|
||||||
|
/// description.
|
||||||
|
pub fn new(
|
||||||
|
call_id: OwnedVoipId,
|
||||||
|
party_id: OwnedVoipId,
|
||||||
|
lifetime: UInt,
|
||||||
|
description: SessionDescription,
|
||||||
|
) -> Self {
|
||||||
|
Self { call_id, party_id, lifetime, description }
|
||||||
|
}
|
||||||
|
}
|
40
crates/ruma-common/src/events/call/reject.rs
Normal file
40
crates/ruma-common/src/events/call/reject.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//! Types for the `m.call.reject` event [MSC2746].
|
||||||
|
//!
|
||||||
|
//! [MSC2746]: https://github.com/matrix-org/matrix-spec-proposals/pull/2746
|
||||||
|
|
||||||
|
use ruma_macros::EventContent;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{OwnedVoipId, VoipVersionId};
|
||||||
|
|
||||||
|
/// **Added in VoIP version 1.** The content of an `m.call.reject` event.
|
||||||
|
///
|
||||||
|
/// Starting from VoIP version 1, this event is sent by the callee to reject an invite.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[ruma_event(type = "m.call.reject", kind = MessageLike)]
|
||||||
|
pub struct CallRejectEventContent {
|
||||||
|
/// The ID of the call this event relates to.
|
||||||
|
pub call_id: OwnedVoipId,
|
||||||
|
|
||||||
|
/// A unique ID for this session for the duration of the call.
|
||||||
|
pub party_id: OwnedVoipId,
|
||||||
|
|
||||||
|
/// The version of the VoIP specification this messages adheres to.
|
||||||
|
///
|
||||||
|
/// Cannot be older than `VoipVersionId::V1`.
|
||||||
|
pub version: VoipVersionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallRejectEventContent {
|
||||||
|
/// Creates a `CallRejectEventContent` with the given call ID, VoIP version and party ID.
|
||||||
|
pub fn new(call_id: OwnedVoipId, party_id: OwnedVoipId, version: VoipVersionId) -> Self {
|
||||||
|
Self { call_id, party_id, version }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a version 1 `CallRejectEventContent` with all the required
|
||||||
|
/// fields.
|
||||||
|
pub fn version_1(call_id: OwnedVoipId, party_id: OwnedVoipId) -> Self {
|
||||||
|
Self::new(call_id, party_id, VoipVersionId::V1)
|
||||||
|
}
|
||||||
|
}
|
55
crates/ruma-common/src/events/call/select_answer.rs
Normal file
55
crates/ruma-common/src/events/call/select_answer.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//! Types for the `m.call.select_answer` event [MSC2746].
|
||||||
|
//!
|
||||||
|
//! [MSC2746]: https://github.com/matrix-org/matrix-spec-proposals/pull/2746
|
||||||
|
|
||||||
|
use ruma_macros::EventContent;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{OwnedVoipId, VoipVersionId};
|
||||||
|
|
||||||
|
/// **Added in VoIP version 1.** The content of an `m.call.select_answer` event.
|
||||||
|
///
|
||||||
|
/// This event is sent by the caller when it has chosen an answer.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[ruma_event(type = "m.call.select_answer", kind = MessageLike)]
|
||||||
|
pub struct CallSelectAnswerEventContent {
|
||||||
|
/// The ID of the call this event relates to.
|
||||||
|
pub call_id: OwnedVoipId,
|
||||||
|
|
||||||
|
/// A unique ID for this session for the duration of the call.
|
||||||
|
///
|
||||||
|
/// Must be the same as the one sent by the previous invite from this session.
|
||||||
|
pub party_id: OwnedVoipId,
|
||||||
|
|
||||||
|
/// The party ID of the selected answer to the previously sent invite.
|
||||||
|
pub selected_party_id: OwnedVoipId,
|
||||||
|
|
||||||
|
/// The version of the VoIP specification this messages adheres to.
|
||||||
|
///
|
||||||
|
/// Cannot be older than `VoipVersionId::V1`.
|
||||||
|
pub version: VoipVersionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallSelectAnswerEventContent {
|
||||||
|
/// Creates a `CallSelectAnswerEventContent` with the given call ID, VoIP version, party ID and
|
||||||
|
/// selected party ID.
|
||||||
|
pub fn new(
|
||||||
|
call_id: OwnedVoipId,
|
||||||
|
party_id: OwnedVoipId,
|
||||||
|
selected_party_id: OwnedVoipId,
|
||||||
|
version: VoipVersionId,
|
||||||
|
) -> Self {
|
||||||
|
Self { call_id, party_id, selected_party_id, version }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method to create a version 1 `CallSelectAnswerEventContent` with all the
|
||||||
|
/// required fields.
|
||||||
|
pub fn version_1(
|
||||||
|
call_id: OwnedVoipId,
|
||||||
|
party_id: OwnedVoipId,
|
||||||
|
selected_party_id: OwnedVoipId,
|
||||||
|
) -> Self {
|
||||||
|
Self::new(call_id, party_id, selected_party_id, VoipVersionId::V1)
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,12 @@ event_enum! {
|
|||||||
"m.call.invite" => super::call::invite,
|
"m.call.invite" => super::call::invite,
|
||||||
"m.call.hangup" => super::call::hangup,
|
"m.call.hangup" => super::call::hangup,
|
||||||
"m.call.candidates" => super::call::candidates,
|
"m.call.candidates" => super::call::candidates,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
"m.call.negotiate" => super::call::negotiate,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
"m.call.reject" => super::call::reject,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
"m.call.select_answer" => super::call::select_answer,
|
||||||
#[cfg(feature = "unstable-msc1767")]
|
#[cfg(feature = "unstable-msc1767")]
|
||||||
"m.emote" => super::emote,
|
"m.emote" => super::emote,
|
||||||
#[cfg(feature = "unstable-msc3551")]
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
@ -346,6 +352,8 @@ impl AnyMessageLikeEventContent {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "unstable-msc3381")]
|
#[cfg(feature = "unstable-msc3381")]
|
||||||
Self::PollStart(_) => None,
|
Self::PollStart(_) => None,
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
Self::CallNegotiate(_) | Self::CallReject(_) | Self::CallSelectAnswer(_) => None,
|
||||||
Self::CallAnswer(_)
|
Self::CallAnswer(_)
|
||||||
| Self::CallInvite(_)
|
| Self::CallInvite(_)
|
||||||
| Self::CallHangup(_)
|
| Self::CallHangup(_)
|
||||||
|
@ -32,10 +32,13 @@ pub use self::{
|
|||||||
signatures::{DeviceSignatures, EntitySignatures, ServerSignatures, Signatures},
|
signatures::{DeviceSignatures, EntitySignatures, ServerSignatures, Signatures},
|
||||||
transaction_id::{OwnedTransactionId, TransactionId},
|
transaction_id::{OwnedTransactionId, TransactionId},
|
||||||
user_id::{OwnedUserId, UserId},
|
user_id::{OwnedUserId, UserId},
|
||||||
|
voip_id::{OwnedVoipId, VoipId},
|
||||||
|
voip_version_id::VoipVersionId,
|
||||||
};
|
};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use ruma_identifiers_validation::error::{
|
pub use ruma_identifiers_validation::error::{
|
||||||
Error as IdParseError, MatrixIdError, MatrixToError, MatrixUriError, MxcUriError,
|
Error as IdParseError, MatrixIdError, MatrixToError, MatrixUriError, MxcUriError,
|
||||||
|
VoipVersionIdError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod matrix_uri;
|
pub mod matrix_uri;
|
||||||
@ -58,6 +61,8 @@ mod server_name;
|
|||||||
mod session_id;
|
mod session_id;
|
||||||
mod signatures;
|
mod signatures;
|
||||||
mod transaction_id;
|
mod transaction_id;
|
||||||
|
mod voip_id;
|
||||||
|
mod voip_version_id;
|
||||||
|
|
||||||
/// Generates a random identifier localpart.
|
/// Generates a random identifier localpart.
|
||||||
#[cfg(feature = "rand")]
|
#[cfg(feature = "rand")]
|
||||||
|
38
crates/ruma-common/src/identifiers/voip_id.rs
Normal file
38
crates/ruma-common/src/identifiers/voip_id.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//! VoIP identifier.
|
||||||
|
|
||||||
|
use ruma_macros::IdZst;
|
||||||
|
|
||||||
|
/// A VoIP identifier.
|
||||||
|
///
|
||||||
|
/// VoIP IDs in Matrix are opaque strings. This type is provided simply for its semantic
|
||||||
|
/// value.
|
||||||
|
///
|
||||||
|
/// You can create one from a string (using `VoipId::parse()`) but the recommended way is to
|
||||||
|
/// use `VoipId::new()` to generate a random one. If that function is not available for you,
|
||||||
|
/// you need to activate this crate's `rand` Cargo feature.
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
|
||||||
|
pub struct VoipId(str);
|
||||||
|
|
||||||
|
impl VoipId {
|
||||||
|
/// Creates a random VoIP identifier.
|
||||||
|
///
|
||||||
|
/// This will currently be a UUID without hyphens, but no guarantees are made about the
|
||||||
|
/// structure of client secrets generated from this function.
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
|
#[allow(clippy::new_ret_no_self)]
|
||||||
|
pub fn new() -> OwnedVoipId {
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
VoipId::from_borrowed(&id.simple().to_string()).to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::VoipId;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_from() {
|
||||||
|
assert!(<&VoipId>::try_from("this_-_a_valid_secret_1337").is_ok())
|
||||||
|
}
|
||||||
|
}
|
224
crates/ruma-common/src/identifiers/voip_version_id.rs
Normal file
224
crates/ruma-common/src/identifiers/voip_version_id.rs
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
//! Matrix VoIP version identifier.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use js_int::UInt;
|
||||||
|
use ruma_macros::DisplayAsRefStr;
|
||||||
|
use serde::{
|
||||||
|
de::{self, Visitor},
|
||||||
|
Deserialize, Deserializer, Serialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{IdParseError, PrivOwnedStr};
|
||||||
|
|
||||||
|
/// A Matrix VoIP version ID.
|
||||||
|
///
|
||||||
|
/// A `VoipVersionId` representing VoIP version 0 can be converted or deserialized from a `UInt`,
|
||||||
|
/// and can be converted or serialized back into a `UInt` as needed.
|
||||||
|
///
|
||||||
|
/// Custom room versions or ones that were introduced into the specification after this code was
|
||||||
|
/// written are represented by a hidden enum variant. They can be converted or deserialized from a
|
||||||
|
/// string slice, and can be converted or serialized back into a string as needed.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use ruma_common::VoipVersionId;
|
||||||
|
/// assert_eq!(VoipVersionId::try_from("1").unwrap().as_ref(), "1");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For simplicity, version 0 has a string representation, but trying to construct a `VoipVersionId`
|
||||||
|
/// from a `"0"` string will not result in the `V0` variant.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, DisplayAsRefStr)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
pub enum VoipVersionId {
|
||||||
|
/// A version 0 VoIP call.
|
||||||
|
V0,
|
||||||
|
|
||||||
|
/// A version 1 VoIP call.
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
V1,
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
_Custom(PrivOwnedStr),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VoipVersionId {
|
||||||
|
/// Creates a string slice from this `VoipVersionId`.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match &self {
|
||||||
|
Self::V0 => "0",
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
Self::V1 => "1",
|
||||||
|
Self::_Custom(PrivOwnedStr(s)) => s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a byte slice from this `VoipVersionId`.
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.as_str().as_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VoipVersionId> for String {
|
||||||
|
fn from(id: VoipVersionId) -> Self {
|
||||||
|
match id {
|
||||||
|
VoipVersionId::_Custom(PrivOwnedStr(version)) => version.into(),
|
||||||
|
_ => id.as_str().to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for VoipVersionId {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for VoipVersionId {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct CallVersionVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for CallVersionVisitor {
|
||||||
|
type Value = VoipVersionId;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
formatter.write_str("0 or string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
let uint = UInt::try_from(value).map_err(de::Error::custom)?;
|
||||||
|
Self::Value::try_from(uint).map_err(de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(CallVersionVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for VoipVersionId {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::V0 => serializer.serialize_u64(0),
|
||||||
|
_ => serializer.serialize_str(self.as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<UInt> for VoipVersionId {
|
||||||
|
type Error = IdParseError;
|
||||||
|
|
||||||
|
fn try_from(u: UInt) -> Result<Self, Self::Error> {
|
||||||
|
ruma_identifiers_validation::voip_version_id::validate(u)?;
|
||||||
|
Ok(Self::V0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from<T>(s: T) -> VoipVersionId
|
||||||
|
where
|
||||||
|
T: AsRef<str> + Into<Box<str>>,
|
||||||
|
{
|
||||||
|
match s.as_ref() {
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
"1" => VoipVersionId::V1,
|
||||||
|
_ => VoipVersionId::_Custom(PrivOwnedStr(s.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for VoipVersionId {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
from(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for VoipVersionId {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
from(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
use js_int::uint;
|
||||||
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
|
use super::VoipVersionId;
|
||||||
|
use crate::IdParseError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_version_0() {
|
||||||
|
assert_eq!(VoipVersionId::try_from(uint!(0)), Ok(VoipVersionId::V0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_uint_version() {
|
||||||
|
assert_matches!(
|
||||||
|
VoipVersionId::try_from(uint!(1)),
|
||||||
|
Err(IdParseError::InvalidVoipVersionId(_))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
fn valid_version_1() {
|
||||||
|
assert_eq!(VoipVersionId::try_from("1"), Ok(VoipVersionId::V1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_custom_string_version() {
|
||||||
|
let version = assert_matches!(
|
||||||
|
VoipVersionId::try_from("io.ruma.2"),
|
||||||
|
Ok(version) => version
|
||||||
|
);
|
||||||
|
assert_eq!(version.as_ref(), "io.ruma.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_version_0() {
|
||||||
|
assert_eq!(to_json_value(&VoipVersionId::V0).unwrap(), json!(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_version_0() {
|
||||||
|
assert_eq!(from_json_value::<VoipVersionId>(json!(0)).unwrap(), VoipVersionId::V0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
fn serialize_version_1() {
|
||||||
|
assert_eq!(to_json_value(&VoipVersionId::V1).unwrap(), json!("1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "unstable-msc2746")]
|
||||||
|
fn deserialize_version_1() {
|
||||||
|
assert_eq!(from_json_value::<VoipVersionId>(json!("1")).unwrap(), VoipVersionId::V1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_custom_string() {
|
||||||
|
let version = VoipVersionId::try_from("io.ruma.1").unwrap();
|
||||||
|
assert_eq!(to_json_value(&version).unwrap(), json!("io.ruma.1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_custom_string() {
|
||||||
|
let version = VoipVersionId::try_from("io.ruma.1").unwrap();
|
||||||
|
assert_eq!(from_json_value::<VoipVersionId>(json!("io.ruma.1")).unwrap(), version);
|
||||||
|
}
|
||||||
|
}
|
587
crates/ruma-common/tests/events/call.rs
Normal file
587
crates/ruma-common/tests/events/call.rs
Normal file
@ -0,0 +1,587 @@
|
|||||||
|
#![cfg(feature = "unstable-msc2746")]
|
||||||
|
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
use assign::assign;
|
||||||
|
use js_int::uint;
|
||||||
|
use ruma_common::{
|
||||||
|
event_id,
|
||||||
|
events::{
|
||||||
|
call::{
|
||||||
|
answer::CallAnswerEventContent,
|
||||||
|
candidates::{CallCandidatesEventContent, Candidate},
|
||||||
|
hangup::{CallHangupEventContent, Reason},
|
||||||
|
invite::CallInviteEventContent,
|
||||||
|
negotiate::CallNegotiateEventContent,
|
||||||
|
reject::CallRejectEventContent,
|
||||||
|
select_answer::CallSelectAnswerEventContent,
|
||||||
|
AnswerSessionDescription, CallCapabilities, OfferSessionDescription,
|
||||||
|
SessionDescription, SessionDescriptionType,
|
||||||
|
},
|
||||||
|
AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned, OriginalMessageLikeEvent,
|
||||||
|
},
|
||||||
|
room_id, user_id, MilliSecondsSinceUnixEpoch, VoipVersionId,
|
||||||
|
};
|
||||||
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invite_content_serialization() {
|
||||||
|
let event_content = CallInviteEventContent::version_0(
|
||||||
|
"abcdef".into(),
|
||||||
|
uint!(30000),
|
||||||
|
OfferSessionDescription::new("not a real sdp".to_owned()),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"lifetime": 30000,
|
||||||
|
"version": 0,
|
||||||
|
"offer": {
|
||||||
|
"type": "offer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invite_event_serialization() {
|
||||||
|
let event = OriginalMessageLikeEvent {
|
||||||
|
content: CallInviteEventContent::version_1(
|
||||||
|
"abcdef".into(),
|
||||||
|
"9876".into(),
|
||||||
|
uint!(60000),
|
||||||
|
OfferSessionDescription::new("not a real sdp".to_owned()),
|
||||||
|
CallCapabilities::new(),
|
||||||
|
),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"lifetime": 60000,
|
||||||
|
"version": "1",
|
||||||
|
"offer": {
|
||||||
|
"type": "offer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.invite",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invite_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"lifetime": 60000,
|
||||||
|
"version": "1",
|
||||||
|
"offer": {
|
||||||
|
"type": "offer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.invite",
|
||||||
|
});
|
||||||
|
|
||||||
|
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||||
|
let message_event = assert_matches!(
|
||||||
|
event,
|
||||||
|
AnyMessageLikeEvent::CallInvite(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
|
);
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.call_id, "abcdef");
|
||||||
|
assert_eq!(content.party_id.unwrap(), "9876");
|
||||||
|
assert_eq!(content.lifetime, uint!(60000));
|
||||||
|
assert_eq!(content.version, VoipVersionId::V1);
|
||||||
|
assert_eq!(content.offer.sdp, "not a real sdp");
|
||||||
|
assert!(!content.capabilities.dtmf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn answer_content_serialization() {
|
||||||
|
let event_content = CallAnswerEventContent::version_0(
|
||||||
|
AnswerSessionDescription::new("not a real sdp".to_owned()),
|
||||||
|
"abcdef".into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"version": 0,
|
||||||
|
"answer": {
|
||||||
|
"type": "answer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn answer_event_serialization() {
|
||||||
|
let event = OriginalMessageLikeEvent {
|
||||||
|
content: CallAnswerEventContent::version_1(
|
||||||
|
AnswerSessionDescription::new("not a real sdp".to_owned()),
|
||||||
|
"abcdef".into(),
|
||||||
|
"9876".into(),
|
||||||
|
assign!(CallCapabilities::new(), { dtmf: true }),
|
||||||
|
),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "1",
|
||||||
|
"answer": {
|
||||||
|
"type": "answer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"m.call.dtmf": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.answer",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn answer_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "org.matrix.1b",
|
||||||
|
"answer": {
|
||||||
|
"type": "answer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"m.call.dtmf": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.answer",
|
||||||
|
});
|
||||||
|
|
||||||
|
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||||
|
let message_event = assert_matches!(
|
||||||
|
event,
|
||||||
|
AnyMessageLikeEvent::CallAnswer(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
|
);
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.call_id, "abcdef");
|
||||||
|
assert_eq!(content.party_id.unwrap(), "9876");
|
||||||
|
assert_eq!(content.version.as_ref(), "org.matrix.1b");
|
||||||
|
assert_eq!(content.answer.sdp, "not a real sdp");
|
||||||
|
assert!(content.capabilities.dtmf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn candidates_content_serialization() {
|
||||||
|
let event_content = CallCandidatesEventContent::version_0(
|
||||||
|
"abcdef".into(),
|
||||||
|
vec![Candidate::new("not a real candidate".to_owned(), "0".to_owned(), uint!(0))],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"version": 0,
|
||||||
|
"candidates": [
|
||||||
|
{
|
||||||
|
"candidate": "not a real candidate",
|
||||||
|
"sdpMid": "0",
|
||||||
|
"sdpMLineIndex": 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn candidates_event_serialization() {
|
||||||
|
let event = OriginalMessageLikeEvent {
|
||||||
|
content: CallCandidatesEventContent::version_1(
|
||||||
|
"abcdef".into(),
|
||||||
|
"9876".into(),
|
||||||
|
vec![
|
||||||
|
Candidate::new("not a real candidate".to_owned(), "0".to_owned(), uint!(0)),
|
||||||
|
Candidate::new("another fake candidate".to_owned(), "0".to_owned(), uint!(1)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "1",
|
||||||
|
"candidates": [
|
||||||
|
{
|
||||||
|
"candidate": "not a real candidate",
|
||||||
|
"sdpMid": "0",
|
||||||
|
"sdpMLineIndex": 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"candidate": "another fake candidate",
|
||||||
|
"sdpMid": "0",
|
||||||
|
"sdpMLineIndex": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.candidates",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn candidates_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "1",
|
||||||
|
"candidates": [
|
||||||
|
{
|
||||||
|
"candidate": "not a real candidate",
|
||||||
|
"sdpMid": "0",
|
||||||
|
"sdpMLineIndex": 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"candidate": "another fake candidate",
|
||||||
|
"sdpMid": "0",
|
||||||
|
"sdpMLineIndex": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.candidates",
|
||||||
|
});
|
||||||
|
|
||||||
|
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||||
|
let message_event = assert_matches!(
|
||||||
|
event,
|
||||||
|
AnyMessageLikeEvent::CallCandidates(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
|
);
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.call_id, "abcdef");
|
||||||
|
assert_eq!(content.party_id.unwrap(), "9876");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V1);
|
||||||
|
assert_eq!(content.candidates.len(), 2);
|
||||||
|
assert_eq!(content.candidates[0].candidate, "not a real candidate");
|
||||||
|
assert_eq!(content.candidates[0].sdp_mid, "0");
|
||||||
|
assert_eq!(content.candidates[0].sdp_m_line_index, uint!(0));
|
||||||
|
assert_eq!(content.candidates[1].candidate, "another fake candidate");
|
||||||
|
assert_eq!(content.candidates[1].sdp_mid, "0");
|
||||||
|
assert_eq!(content.candidates[1].sdp_m_line_index, uint!(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hangup_content_serialization() {
|
||||||
|
let event_content = CallHangupEventContent::version_0("abcdef".into());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"version": 0,
|
||||||
|
"reason": "user_hangup",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hangup_event_serialization() {
|
||||||
|
let event = OriginalMessageLikeEvent {
|
||||||
|
content: CallHangupEventContent::version_1(
|
||||||
|
"abcdef".into(),
|
||||||
|
"9876".into(),
|
||||||
|
Reason::IceFailed,
|
||||||
|
),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "1",
|
||||||
|
"reason": "ice_failed",
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.hangup",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hangup_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "1",
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.hangup",
|
||||||
|
});
|
||||||
|
|
||||||
|
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||||
|
let message_event = assert_matches!(
|
||||||
|
event,
|
||||||
|
AnyMessageLikeEvent::CallHangup(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
|
);
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.call_id, "abcdef");
|
||||||
|
assert_eq!(content.party_id.unwrap(), "9876");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V1);
|
||||||
|
assert_eq!(content.reason, Some(Reason::UserHangup));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn negotiate_event_serialization() {
|
||||||
|
let event = OriginalMessageLikeEvent {
|
||||||
|
content: CallNegotiateEventContent::new(
|
||||||
|
"abcdef".into(),
|
||||||
|
"9876".into(),
|
||||||
|
uint!(30000),
|
||||||
|
SessionDescription::new(SessionDescriptionType::Offer, "not a real sdp".to_owned()),
|
||||||
|
),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"lifetime": 30000,
|
||||||
|
"description": {
|
||||||
|
"type": "offer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.negotiate",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn negotiate_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"lifetime": 30000,
|
||||||
|
"description": {
|
||||||
|
"type": "pranswer",
|
||||||
|
"sdp": "not a real sdp",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.negotiate",
|
||||||
|
});
|
||||||
|
|
||||||
|
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||||
|
let message_event = assert_matches!(
|
||||||
|
event,
|
||||||
|
AnyMessageLikeEvent::CallNegotiate(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
|
);
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.call_id, "abcdef");
|
||||||
|
assert_eq!(content.party_id, "9876");
|
||||||
|
assert_eq!(content.lifetime, uint!(30000));
|
||||||
|
assert_eq!(content.description.session_type, SessionDescriptionType::PrAnswer);
|
||||||
|
assert_eq!(content.description.sdp, "not a real sdp")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reject_event_serialization() {
|
||||||
|
let event = OriginalMessageLikeEvent {
|
||||||
|
content: CallRejectEventContent::version_1("abcdef".into(), "9876".into()),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "1",
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.reject",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reject_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"version": "1",
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.reject",
|
||||||
|
});
|
||||||
|
|
||||||
|
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||||
|
let message_event = assert_matches!(
|
||||||
|
event,
|
||||||
|
AnyMessageLikeEvent::CallReject(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
|
);
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.call_id, "abcdef");
|
||||||
|
assert_eq!(content.party_id, "9876");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_answer_event_serialization() {
|
||||||
|
let event = OriginalMessageLikeEvent {
|
||||||
|
content: CallSelectAnswerEventContent::version_1(
|
||||||
|
"abcdef".into(),
|
||||||
|
"9876".into(),
|
||||||
|
"6336".into(),
|
||||||
|
),
|
||||||
|
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||||
|
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||||
|
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||||
|
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||||
|
unsigned: MessageLikeUnsigned::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&event).unwrap(),
|
||||||
|
json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"selected_party_id": "6336",
|
||||||
|
"version": "1",
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.select_answer",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_answer_event_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"content": {
|
||||||
|
"call_id": "abcdef",
|
||||||
|
"party_id": "9876",
|
||||||
|
"selected_party_id": "6336",
|
||||||
|
"version": "1",
|
||||||
|
},
|
||||||
|
"event_id": "$event:notareal.hs",
|
||||||
|
"origin_server_ts": 134_829_848,
|
||||||
|
"room_id": "!roomid:notareal.hs",
|
||||||
|
"sender": "@user:notareal.hs",
|
||||||
|
"type": "m.call.select_answer",
|
||||||
|
});
|
||||||
|
|
||||||
|
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
|
||||||
|
let message_event = assert_matches!(
|
||||||
|
event,
|
||||||
|
AnyMessageLikeEvent::CallSelectAnswer(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
|
);
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.call_id, "abcdef");
|
||||||
|
assert_eq!(content.party_id, "9876");
|
||||||
|
assert_eq!(content.selected_party_id, "6336");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V1);
|
||||||
|
}
|
@ -1,15 +1,11 @@
|
|||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use js_int::{uint, UInt};
|
use js_int::uint;
|
||||||
use ruma_common::{
|
use ruma_common::{
|
||||||
event_id, events::MessageLikeEvent, room_id, user_id, MilliSecondsSinceUnixEpoch,
|
events::{AnyMessageLikeEvent, MessageLikeEvent},
|
||||||
|
MilliSecondsSinceUnixEpoch, VoipVersionId,
|
||||||
};
|
};
|
||||||
use serde_json::{from_value as from_json_value, json};
|
use serde_json::{from_value as from_json_value, json};
|
||||||
|
|
||||||
use ruma_common::events::{
|
|
||||||
call::{answer::CallAnswerEventContent, AnswerSessionDescription},
|
|
||||||
AnyMessageLikeEvent, OriginalMessageLikeEvent,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ui() {
|
fn ui() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
@ -27,7 +23,7 @@ fn deserialize_message_event() {
|
|||||||
"sdp": "Hello"
|
"sdp": "Hello"
|
||||||
},
|
},
|
||||||
"call_id": "foofoo",
|
"call_id": "foofoo",
|
||||||
"version": 1
|
"version": 0
|
||||||
},
|
},
|
||||||
"event_id": "$h29iv0s8:example.com",
|
"event_id": "$h29iv0s8:example.com",
|
||||||
"origin_server_ts": 1,
|
"origin_server_ts": 1,
|
||||||
@ -36,29 +32,19 @@ fn deserialize_message_event() {
|
|||||||
"type": "m.call.answer"
|
"type": "m.call.answer"
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_matches!(
|
let message_event = assert_matches!(
|
||||||
from_json_value::<AnyMessageLikeEvent>(json_data)
|
from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(),
|
||||||
.unwrap(),
|
AnyMessageLikeEvent::CallAnswer(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
AnyMessageLikeEvent::CallAnswer(MessageLikeEvent::Original(OriginalMessageLikeEvent {
|
|
||||||
content: CallAnswerEventContent {
|
|
||||||
answer: AnswerSessionDescription {
|
|
||||||
sdp,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
call_id,
|
|
||||||
version,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
event_id,
|
|
||||||
origin_server_ts,
|
|
||||||
room_id,
|
|
||||||
sender,
|
|
||||||
unsigned,
|
|
||||||
})) if sdp == "Hello" && call_id == "foofoo" && version == UInt::new(1).unwrap()
|
|
||||||
&& event_id == event_id!("$h29iv0s8:example.com")
|
|
||||||
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1))
|
|
||||||
&& room_id == room_id!("!roomid:room.com")
|
|
||||||
&& sender == user_id!("@carl:example.com")
|
|
||||||
&& unsigned.is_empty()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(message_event.event_id, "$h29iv0s8:example.com");
|
||||||
|
assert_eq!(message_event.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(1)));
|
||||||
|
assert_eq!(message_event.room_id, "!roomid:room.com");
|
||||||
|
assert_eq!(message_event.sender, "@carl:example.com");
|
||||||
|
assert!(message_event.unsigned.is_empty());
|
||||||
|
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.answer.sdp, "Hello");
|
||||||
|
assert_eq!(content.call_id, "foofoo");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V0);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ use js_int::{uint, UInt};
|
|||||||
use ruma_common::{
|
use ruma_common::{
|
||||||
event_id,
|
event_id,
|
||||||
events::{
|
events::{
|
||||||
call::{answer::CallAnswerEventContent, AnswerSessionDescription},
|
|
||||||
room::{ImageInfo, MediaSource, ThumbnailInfo},
|
room::{ImageInfo, MediaSource, ThumbnailInfo},
|
||||||
sticker::StickerEventContent,
|
sticker::StickerEventContent,
|
||||||
AnyMessageLikeEvent, AnyMessageLikeEventContent, AnySyncMessageLikeEvent, MessageLikeEvent,
|
AnyMessageLikeEvent, AnyMessageLikeEventContent, AnySyncMessageLikeEvent, MessageLikeEvent,
|
||||||
@ -12,7 +11,7 @@ use ruma_common::{
|
|||||||
},
|
},
|
||||||
mxc_uri, room_id,
|
mxc_uri, room_id,
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
user_id, MilliSecondsSinceUnixEpoch,
|
user_id, MilliSecondsSinceUnixEpoch, VoipVersionId,
|
||||||
};
|
};
|
||||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
@ -127,24 +126,20 @@ fn deserialize_message_call_answer_content() {
|
|||||||
"sdp": "Hello"
|
"sdp": "Hello"
|
||||||
},
|
},
|
||||||
"call_id": "foofoo",
|
"call_id": "foofoo",
|
||||||
"version": 1
|
"version": 0
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_matches!(
|
let content = assert_matches!(
|
||||||
from_json_value::<Raw<AnyMessageLikeEventContent>>(json_data)
|
from_json_value::<Raw<AnyMessageLikeEventContent>>(json_data)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.deserialize_content(MessageLikeEventType::CallAnswer)
|
.deserialize_content(MessageLikeEventType::CallAnswer)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
AnyMessageLikeEventContent::CallAnswer(CallAnswerEventContent {
|
AnyMessageLikeEventContent::CallAnswer(content) => content
|
||||||
answer: AnswerSessionDescription {
|
|
||||||
sdp,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
call_id,
|
|
||||||
version,
|
|
||||||
..
|
|
||||||
}) if sdp == "Hello" && call_id == "foofoo" && version == UInt::new(1).unwrap()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(content.answer.sdp, "Hello");
|
||||||
|
assert_eq!(content.call_id, "foofoo");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -156,7 +151,7 @@ fn deserialize_message_call_answer() {
|
|||||||
"sdp": "Hello"
|
"sdp": "Hello"
|
||||||
},
|
},
|
||||||
"call_id": "foofoo",
|
"call_id": "foofoo",
|
||||||
"version": 1
|
"version": 0
|
||||||
},
|
},
|
||||||
"event_id": "$h29iv0s8:example.com",
|
"event_id": "$h29iv0s8:example.com",
|
||||||
"origin_server_ts": 1,
|
"origin_server_ts": 1,
|
||||||
@ -165,30 +160,20 @@ fn deserialize_message_call_answer() {
|
|||||||
"type": "m.call.answer"
|
"type": "m.call.answer"
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_matches!(
|
let message_event = assert_matches!(
|
||||||
from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(),
|
from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(),
|
||||||
AnyMessageLikeEvent::CallAnswer(MessageLikeEvent::Original(OriginalMessageLikeEvent {
|
AnyMessageLikeEvent::CallAnswer(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
content: CallAnswerEventContent {
|
|
||||||
answer: AnswerSessionDescription {
|
|
||||||
sdp,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
call_id,
|
|
||||||
version,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
event_id,
|
|
||||||
origin_server_ts,
|
|
||||||
room_id,
|
|
||||||
sender,
|
|
||||||
unsigned,
|
|
||||||
})) if sdp == "Hello" && call_id == "foofoo" && version == UInt::new(1).unwrap()
|
|
||||||
&& event_id == event_id!("$h29iv0s8:example.com")
|
|
||||||
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1))
|
|
||||||
&& room_id == room_id!("!roomid:room.com")
|
|
||||||
&& sender == user_id!("@carl:example.com")
|
|
||||||
&& unsigned.is_empty()
|
|
||||||
);
|
);
|
||||||
|
assert_eq!(message_event.event_id, "$h29iv0s8:example.com");
|
||||||
|
assert_eq!(message_event.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(1)));
|
||||||
|
assert_eq!(message_event.room_id, "!roomid:room.com");
|
||||||
|
assert_eq!(message_event.sender, "@carl:example.com");
|
||||||
|
assert!(message_event.unsigned.is_empty());
|
||||||
|
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.answer.sdp, "Hello");
|
||||||
|
assert_eq!(content.call_id, "foofoo");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -280,7 +265,7 @@ fn deserialize_message_then_convert_to_full() {
|
|||||||
"sdp": "Hello"
|
"sdp": "Hello"
|
||||||
},
|
},
|
||||||
"call_id": "foofoo",
|
"call_id": "foofoo",
|
||||||
"version": 1
|
"version": 0
|
||||||
},
|
},
|
||||||
"event_id": "$h29iv0s8:example.com",
|
"event_id": "$h29iv0s8:example.com",
|
||||||
"origin_server_ts": 1,
|
"origin_server_ts": 1,
|
||||||
@ -290,30 +275,18 @@ fn deserialize_message_then_convert_to_full() {
|
|||||||
|
|
||||||
let sync_ev: AnySyncMessageLikeEvent = from_json_value(json_data).unwrap();
|
let sync_ev: AnySyncMessageLikeEvent = from_json_value(json_data).unwrap();
|
||||||
|
|
||||||
assert_matches!(
|
let message_event = assert_matches!(
|
||||||
sync_ev.into_full_event(rid.to_owned()),
|
sync_ev.into_full_event(rid.to_owned()),
|
||||||
AnyMessageLikeEvent::CallAnswer(MessageLikeEvent::Original(OriginalMessageLikeEvent {
|
AnyMessageLikeEvent::CallAnswer(MessageLikeEvent::Original(message_event)) => message_event
|
||||||
content: CallAnswerEventContent {
|
|
||||||
answer: AnswerSessionDescription {
|
|
||||||
sdp,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
call_id,
|
|
||||||
version,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
event_id,
|
|
||||||
origin_server_ts,
|
|
||||||
room_id,
|
|
||||||
sender,
|
|
||||||
unsigned,
|
|
||||||
})) if sdp == "Hello"
|
|
||||||
&& call_id == "foofoo"
|
|
||||||
&& version == uint!(1)
|
|
||||||
&& event_id == "$h29iv0s8:example.com"
|
|
||||||
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1))
|
|
||||||
&& room_id == "!roomid:room.com"
|
|
||||||
&& sender == "@carl:example.com"
|
|
||||||
&& unsigned.is_empty()
|
|
||||||
);
|
);
|
||||||
|
assert_eq!(message_event.event_id, "$h29iv0s8:example.com");
|
||||||
|
assert_eq!(message_event.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(1)));
|
||||||
|
assert_eq!(message_event.room_id, "!roomid:room.com");
|
||||||
|
assert_eq!(message_event.sender, "@carl:example.com");
|
||||||
|
assert!(message_event.unsigned.is_empty());
|
||||||
|
|
||||||
|
let content = message_event.content;
|
||||||
|
assert_eq!(content.answer.sdp, "Hello");
|
||||||
|
assert_eq!(content.call_id, "foofoo");
|
||||||
|
assert_eq!(content.version, VoipVersionId::V0);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![cfg(feature = "events")]
|
#![cfg(feature = "events")]
|
||||||
|
|
||||||
mod audio;
|
mod audio;
|
||||||
|
mod call;
|
||||||
mod enums;
|
mod enums;
|
||||||
mod ephemeral_event;
|
mod ephemeral_event;
|
||||||
mod event;
|
mod event;
|
||||||
|
@ -15,4 +15,5 @@ all-features = true
|
|||||||
compat = []
|
compat = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
js_int = "0.2.0"
|
||||||
thiserror = "1.0.26"
|
thiserror = "1.0.26"
|
||||||
|
@ -30,6 +30,10 @@ pub enum Error {
|
|||||||
#[error("invalid Matrix Content URI: {0}")]
|
#[error("invalid Matrix Content URI: {0}")]
|
||||||
InvalidMxcUri(#[from] MxcUriError),
|
InvalidMxcUri(#[from] MxcUriError),
|
||||||
|
|
||||||
|
/// The value isn't a valid VoIP version Id.
|
||||||
|
#[error("invalid VoIP version ID: {0}")]
|
||||||
|
InvalidVoipVersionId(#[from] VoipVersionIdError),
|
||||||
|
|
||||||
/// The server name part of the the ID string is not a valid server name.
|
/// The server name part of the the ID string is not a valid server name.
|
||||||
#[error("server name is not a valid IP address or domain name")]
|
#[error("server name is not a valid IP address or domain name")]
|
||||||
InvalidServerName,
|
InvalidServerName,
|
||||||
@ -149,6 +153,15 @@ pub enum MatrixUriError {
|
|||||||
UnknownQueryItem,
|
UnknownQueryItem,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error occurred while validating a `VoipVersionId`.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum VoipVersionIdError {
|
||||||
|
/// The value of the `UInt` is not 0.
|
||||||
|
#[error("UInt value is not 0")]
|
||||||
|
WrongUintValue,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
@ -15,6 +15,7 @@ pub mod room_version_id;
|
|||||||
pub mod server_name;
|
pub mod server_name;
|
||||||
pub mod session_id;
|
pub mod session_id;
|
||||||
pub mod user_id;
|
pub mod user_id;
|
||||||
|
pub mod voip_version_id;
|
||||||
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|
||||||
|
11
crates/ruma-identifiers-validation/src/voip_version_id.rs
Normal file
11
crates/ruma-identifiers-validation/src/voip_version_id.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use js_int::{uint, UInt};
|
||||||
|
|
||||||
|
use crate::{error::VoipVersionIdError, Error};
|
||||||
|
|
||||||
|
pub fn validate(u: UInt) -> Result<(), Error> {
|
||||||
|
if u != uint!(0) {
|
||||||
|
return Err(VoipVersionIdError::WrongUintValue.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -134,6 +134,7 @@ unstable-msc2677 = [
|
|||||||
"ruma-client-api/unstable-msc2677",
|
"ruma-client-api/unstable-msc2677",
|
||||||
"ruma-common/unstable-msc2677",
|
"ruma-common/unstable-msc2677",
|
||||||
]
|
]
|
||||||
|
unstable-msc2746 = ["ruma-common/unstable-msc2746"]
|
||||||
unstable-msc2870 = ["ruma-signatures/unstable-msc2870"]
|
unstable-msc2870 = ["ruma-signatures/unstable-msc2870"]
|
||||||
unstable-msc3245 = ["ruma-common/unstable-msc3245"]
|
unstable-msc3245 = ["ruma-common/unstable-msc3245"]
|
||||||
unstable-msc3246 = ["ruma-common/unstable-msc3246"]
|
unstable-msc3246 = ["ruma-common/unstable-msc3246"]
|
||||||
@ -163,6 +164,7 @@ __ci = [
|
|||||||
"unstable-msc2675",
|
"unstable-msc2675",
|
||||||
"unstable-msc2676",
|
"unstable-msc2676",
|
||||||
"unstable-msc2677",
|
"unstable-msc2677",
|
||||||
|
"unstable-msc2746",
|
||||||
"unstable-msc2870",
|
"unstable-msc2870",
|
||||||
"unstable-msc3245",
|
"unstable-msc3245",
|
||||||
"unstable-msc3246",
|
"unstable-msc3246",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user