ruma_common/identifiers/
voip_version_id.rs

1//! Matrix VoIP version identifier.
2
3use std::fmt;
4
5use js_int::UInt;
6use ruma_macros::DisplayAsRefStr;
7use serde::{
8    de::{self, Visitor},
9    Deserialize, Deserializer, Serialize,
10};
11
12use crate::{IdParseError, PrivOwnedStr};
13
14/// A Matrix VoIP version ID.
15///
16/// A `VoipVersionId` representing VoIP version 0 can be converted or deserialized from a `UInt`,
17/// and can be converted or serialized back into a `UInt` as needed.
18///
19/// Custom room versions or ones that were introduced into the specification after this code was
20/// written are represented by a hidden enum variant. They can be converted or deserialized from a
21/// string slice, and can be converted or serialized back into a string as needed.
22///
23/// ```
24/// # use ruma_common::VoipVersionId;
25/// assert_eq!(VoipVersionId::try_from("1").unwrap().as_ref(), "1");
26/// ```
27///
28/// For simplicity, version 0 has a string representation, but trying to construct a `VoipVersionId`
29/// from a `"0"` string will not result in the `V0` variant.
30#[derive(Clone, Debug, PartialEq, Eq, Hash, DisplayAsRefStr)]
31#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
32pub enum VoipVersionId {
33    /// A version 0 VoIP call.
34    V0,
35
36    /// A version 1 VoIP call.
37    V1,
38
39    #[doc(hidden)]
40    _Custom(PrivOwnedStr),
41}
42
43impl VoipVersionId {
44    /// Creates a string slice from this `VoipVersionId`.
45    pub fn as_str(&self) -> &str {
46        match &self {
47            Self::V0 => "0",
48            Self::V1 => "1",
49            Self::_Custom(PrivOwnedStr(s)) => s,
50        }
51    }
52
53    /// Creates a byte slice from this `VoipVersionId`.
54    pub fn as_bytes(&self) -> &[u8] {
55        self.as_str().as_bytes()
56    }
57}
58
59impl From<VoipVersionId> for String {
60    fn from(id: VoipVersionId) -> Self {
61        match id {
62            VoipVersionId::_Custom(PrivOwnedStr(version)) => version.into(),
63            _ => id.as_str().to_owned(),
64        }
65    }
66}
67
68impl AsRef<str> for VoipVersionId {
69    fn as_ref(&self) -> &str {
70        self.as_str()
71    }
72}
73
74impl<'de> Deserialize<'de> for VoipVersionId {
75    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76    where
77        D: Deserializer<'de>,
78    {
79        struct CallVersionVisitor;
80
81        impl Visitor<'_> for CallVersionVisitor {
82            type Value = VoipVersionId;
83
84            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85                formatter.write_str("0 or string")
86            }
87
88            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
89            where
90                E: de::Error,
91            {
92                Ok(value.into())
93            }
94
95            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
96            where
97                E: de::Error,
98            {
99                let uint = UInt::try_from(value).map_err(de::Error::custom)?;
100                Self::Value::try_from(uint).map_err(de::Error::custom)
101            }
102        }
103
104        deserializer.deserialize_any(CallVersionVisitor)
105    }
106}
107
108impl Serialize for VoipVersionId {
109    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110    where
111        S: serde::Serializer,
112    {
113        match self {
114            Self::V0 => serializer.serialize_u64(0),
115            _ => serializer.serialize_str(self.as_str()),
116        }
117    }
118}
119
120impl TryFrom<UInt> for VoipVersionId {
121    type Error = IdParseError;
122
123    fn try_from(u: UInt) -> Result<Self, Self::Error> {
124        ruma_identifiers_validation::voip_version_id::validate(u)?;
125        Ok(Self::V0)
126    }
127}
128
129fn from<T>(s: T) -> VoipVersionId
130where
131    T: AsRef<str> + Into<Box<str>>,
132{
133    match s.as_ref() {
134        "1" => VoipVersionId::V1,
135        _ => VoipVersionId::_Custom(PrivOwnedStr(s.into())),
136    }
137}
138
139impl From<&str> for VoipVersionId {
140    fn from(s: &str) -> Self {
141        from(s)
142    }
143}
144
145impl From<String> for VoipVersionId {
146    fn from(s: String) -> Self {
147        from(s)
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use assert_matches2::assert_matches;
154    use js_int::uint;
155    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
156
157    use super::VoipVersionId;
158    use crate::IdParseError;
159
160    #[test]
161    fn valid_version_0() {
162        assert_eq!(VoipVersionId::try_from(uint!(0)), Ok(VoipVersionId::V0));
163    }
164
165    #[test]
166    fn invalid_uint_version() {
167        assert_matches!(
168            VoipVersionId::try_from(uint!(1)),
169            Err(IdParseError::InvalidVoipVersionId(_))
170        );
171    }
172
173    #[test]
174    fn valid_version_1() {
175        assert_eq!(VoipVersionId::from("1"), VoipVersionId::V1);
176    }
177
178    #[test]
179    fn valid_custom_string_version() {
180        assert_matches!(VoipVersionId::from("io.ruma.2"), version);
181        assert_eq!(version.as_ref(), "io.ruma.2");
182    }
183
184    #[test]
185    fn serialize_version_0() {
186        assert_eq!(to_json_value(&VoipVersionId::V0).unwrap(), json!(0));
187    }
188
189    #[test]
190    fn deserialize_version_0() {
191        assert_eq!(from_json_value::<VoipVersionId>(json!(0)).unwrap(), VoipVersionId::V0);
192    }
193
194    #[test]
195    fn serialize_version_1() {
196        assert_eq!(to_json_value(&VoipVersionId::V1).unwrap(), json!("1"));
197    }
198
199    #[test]
200    fn deserialize_version_1() {
201        assert_eq!(from_json_value::<VoipVersionId>(json!("1")).unwrap(), VoipVersionId::V1);
202    }
203
204    #[test]
205    fn serialize_custom_string() {
206        let version = VoipVersionId::from("io.ruma.1");
207        assert_eq!(to_json_value(&version).unwrap(), json!("io.ruma.1"));
208    }
209
210    #[test]
211    fn deserialize_custom_string() {
212        let version = VoipVersionId::from("io.ruma.1");
213        assert_eq!(from_json_value::<VoipVersionId>(json!("io.ruma.1")).unwrap(), version);
214    }
215}