medea_control_api_proto/control/endpoint/
web_rtc_play.rs1use std::str::FromStr;
6
7use derive_more::with_trait::{AsRef, Display, Error, From, Into};
8use ref_cast::RefCast;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
11use url::Url;
12
13use crate::control::{
14 endpoint::{self, web_rtc_publish},
15 member, room,
16};
17
18#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct WebRtcPlay {
24 pub id: Id,
28
29 pub spec: Spec,
33}
34
35#[derive(Clone, Debug, Eq, PartialEq)]
39#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
40pub struct Spec {
41 pub src: LocalSrcUri,
43
44 #[cfg_attr(feature = "serde", serde(default))]
49 pub force_relay: bool,
50}
51
52#[derive(
56 AsRef,
57 Clone,
58 Debug,
59 Display,
60 Eq,
61 From,
62 Hash,
63 Into,
64 Ord,
65 PartialEq,
66 PartialOrd,
67 RefCast,
68)]
69#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
70#[cfg_attr(feature = "serde", serde(transparent))]
71#[from(Box<str>, &str, String)]
72#[into(Box<str>, String)]
73#[repr(transparent)]
74pub struct Id(Box<str>);
75
76impl AsRef<endpoint::Id> for Id {
77 fn as_ref(&self) -> &endpoint::Id {
78 endpoint::Id::ref_cast(&self.0)
79 }
80}
81
82#[derive(Clone, Debug, Display, Eq, PartialEq)]
88#[display("local://{room_id}/{member_id}/{endpoint_id}")]
89pub struct LocalSrcUri {
90 pub room_id: room::Id,
94
95 pub member_id: member::Id,
99
100 pub endpoint_id: web_rtc_publish::Id,
105}
106
107impl FromStr for LocalSrcUri {
108 type Err = LocalSrcUriParseError;
109
110 fn from_str(val: &str) -> Result<Self, Self::Err> {
111 if val.is_empty() {
112 return Err(LocalSrcUriParseError::Empty);
113 }
114
115 let url = Url::parse(val)
116 .map_err(|e| LocalSrcUriParseError::UrlParseErr(val.into(), e))?;
117 if url.scheme() != "local" {
118 return Err(LocalSrcUriParseError::NotLocal(val.into()));
119 }
120
121 let room_id = url
122 .host_str()
123 .filter(|h| !h.is_empty())
124 .ok_or_else(|| LocalSrcUriParseError::MissingPaths(val.into()))?
125 .into();
126
127 let mut path = url
128 .path_segments()
129 .ok_or_else(|| LocalSrcUriParseError::MissingPaths(val.into()))?;
130
131 let member_id = path
132 .next()
133 .filter(|id| !id.is_empty())
134 .ok_or_else(|| LocalSrcUriParseError::MissingPaths(val.into()))?
135 .into();
136
137 let endpoint_id = path
138 .next()
139 .filter(|id| !id.is_empty())
140 .ok_or_else(|| LocalSrcUriParseError::MissingPaths(val.into()))?
141 .into();
142
143 if path.next().is_some() {
144 return Err(LocalSrcUriParseError::TooManyPaths(val.into()));
145 }
146
147 Ok(Self { room_id, member_id, endpoint_id })
148 }
149}
150
151#[cfg(feature = "serde")]
152impl<'de> Deserialize<'de> for LocalSrcUri {
153 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154 where
155 D: Deserializer<'de>,
156 {
157 String::deserialize(deserializer)?
158 .parse::<Self>()
159 .map_err(D::Error::custom)
160 }
161}
162
163#[cfg(feature = "serde")]
164impl Serialize for LocalSrcUri {
165 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
166 where
167 S: Serializer,
168 {
169 serializer.serialize_str(&self.to_string())
170 }
171}
172
173#[derive(Debug, Display, Error)]
175pub enum LocalSrcUriParseError {
176 #[display("Provided URI protocol is not `local://`: {_0}")]
178 NotLocal(#[error(not(source))] Box<str>),
179
180 #[display("Too many paths in URI: {_0}")]
184 TooManyPaths(#[error(not(source))] Box<str>),
185
186 #[display("Missing paths in URI: {_0}")]
190 MissingPaths(#[error(not(source))] Box<str>),
191
192 #[display("Cannot parse provided URI `{_0}`: {_1}")]
194 UrlParseErr(Box<str>, #[error(source)] url::ParseError),
195
196 #[display("Provided URI cannot be empty")]
198 Empty,
199}