1use std::sync::Arc;
2
3use moq_net::Session;
4use url::Url;
5
6use crate::error::MoqError;
7use crate::ffi::Task;
8use crate::origin::MoqOriginProducer;
9
10struct Client {
11 config: moq_native::ClientConfig,
12 publish: Option<Arc<MoqOriginProducer>>,
13 consume: Option<Arc<MoqOriginProducer>>,
14}
15
16impl Client {
17 async fn connect(&self, url: Url) -> Result<Arc<MoqSession>, MoqError> {
18 let client = self.config.clone().init().map_err(map_connect_error)?;
19
20 let publish = self.publish.as_ref().map(|o| o.inner().consume());
21 let consume = self.consume.as_ref().map(|o| o.inner().clone());
22
23 let session = client
24 .with_publish(publish)
25 .with_consume(consume)
26 .connect(url)
27 .await
28 .map_err(map_connect_error)?;
29
30 Ok(Arc::new(MoqSession::new(session)))
31 }
32}
33
34fn map_connect_error(err: moq_native::Error) -> MoqError {
35 match err.connect_error() {
36 Some(moq_native::ConnectError::Unauthorized) => MoqError::Unauthorized,
37 Some(moq_native::ConnectError::Forbidden) => MoqError::Forbidden,
38 _ => MoqError::Connect(format!("{err}")),
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn maps_native_auth_connect_errors() {
48 assert!(matches!(
49 map_connect_error(moq_native::ConnectError::Unauthorized.into()),
50 MoqError::Unauthorized
51 ));
52 assert!(matches!(
53 map_connect_error(moq_native::ConnectError::Forbidden.into()),
54 MoqError::Forbidden
55 ));
56 }
57}
58
59#[derive(uniffi::Object)]
60pub struct MoqClient {
61 task: Task<Client>,
62}
63
64#[uniffi::export]
65impl MoqClient {
66 #[uniffi::constructor]
68 pub fn new() -> Arc<Self> {
69 let _guard = crate::ffi::RUNTIME.enter();
70 Arc::new(Self {
71 task: Task::new(Client {
72 config: moq_native::ClientConfig::default(),
73 publish: None,
74 consume: None,
75 }),
76 })
77 }
78
79 pub fn set_tls_disable_verify(&self, disable: bool) {
81 if let Some(mut state) = self.task.lock() {
82 state.config.tls.disable_verify = Some(disable);
83 }
84 }
85
86 pub fn set_tls_roots(&self, paths: Vec<String>) {
91 if let Some(mut state) = self.task.lock() {
92 state.config.tls.root = paths.into_iter().map(Into::into).collect();
93 }
94 }
95
96 pub fn set_tls_fingerprints(&self, fingerprints: Vec<String>) {
103 if let Some(mut state) = self.task.lock() {
104 state.config.tls.fingerprint = fingerprints;
105 }
106 }
107
108 pub fn set_bind(&self, addr: String) -> Result<(), MoqError> {
112 let parsed: std::net::SocketAddr = addr
113 .parse()
114 .map_err(|err| MoqError::Bind(format!("invalid bind address: {err}")))?;
115 if let Some(mut state) = self.task.lock() {
116 state.config.bind = parsed;
117 }
118 Ok(())
119 }
120
121 pub fn set_publish(&self, origin: Option<Arc<MoqOriginProducer>>) {
123 if let Some(mut state) = self.task.lock() {
124 state.publish = origin;
125 }
126 }
127
128 pub fn set_consume(&self, origin: Option<Arc<MoqOriginProducer>>) {
130 if let Some(mut state) = self.task.lock() {
131 state.consume = origin;
132 }
133 }
134
135 pub async fn connect(&self, url: String) -> Result<Arc<MoqSession>, MoqError> {
139 let url = Url::parse(&url)?;
140
141 self.task.run(|state| async move { state.connect(url).await }).await
142 }
143
144 pub fn cancel(&self) {
146 self.task.cancel();
147 }
148}
149
150#[derive(uniffi::Object)]
151pub struct MoqSession {
152 inner: Option<moq_net::Session>,
153 closed: Task<Session>,
154}
155
156impl MoqSession {
157 pub(crate) fn new(session: moq_net::Session) -> Self {
158 Self {
159 inner: Some(session.clone()),
160 closed: Task::new(session),
161 }
162 }
163}
164
165impl Drop for MoqSession {
166 fn drop(&mut self) {
167 let _guard = crate::ffi::RUNTIME.enter();
168 self.inner.take();
169 }
170}
171
172#[uniffi::export]
173impl MoqSession {
174 pub async fn closed(&self) -> Result<(), MoqError> {
176 self.closed
178 .run(|session| async move { session.closed().await.map_err(Into::into) })
179 .await
180 }
181
182 pub fn cancel(&self, code: u32) {
184 let _guard = crate::ffi::RUNTIME.enter();
185 if let Some(inner) = &self.inner {
186 inner.clone().close(moq_net::Error::Remote(code));
187 }
188 }
191
192 pub fn shutdown(&self) {
199 self.cancel(0);
200 }
201}