mod builder;
mod handle;
mod types;
pub use builder::WebShareBuilder;
pub use handle::{WebShareHandle, WebShareLookup};
pub use types::{WebConfigInfo, WebShareSummary};
use rmux_proto::{
ListWebSharesRequest, LookupWebShareRequest, Request, Response, StopAllWebSharesRequest,
StopWebShareRequest, WebShareConfigRequest, WebShareRequest, WebShareResponse,
CAPABILITY_WEB_SHARE,
};
use crate::transport::TransportClient;
use crate::{Result, RmuxError};
async fn list_web_shares(transport: &TransportClient) -> Result<Vec<WebShareSummary>> {
require_web_share(transport).await?;
let response = transport
.request(Request::WebShare(WebShareRequest::List(
ListWebSharesRequest,
)))
.await?;
match response {
Response::WebShare(WebShareResponse::List(response)) => {
Ok(response.shares.into_iter().map(Into::into).collect())
}
Response::Error(error) => Err(error.into()),
response => Err(unexpected_response("web-share list", response)),
}
}
async fn stop_web_share(transport: &TransportClient, id: &str) -> Result<bool> {
require_web_share(transport).await?;
let response = transport
.request(Request::WebShare(WebShareRequest::Stop(
StopWebShareRequest {
share_id: id.to_owned(),
},
)))
.await?;
match response {
Response::WebShare(WebShareResponse::Stopped(response)) => Ok(response.stopped),
Response::Error(error) => Err(error.into()),
response => Err(unexpected_response("web-share stop", response)),
}
}
async fn stop_all_web_shares(transport: &TransportClient) -> Result<usize> {
require_web_share(transport).await?;
let response = transport
.request(Request::WebShare(WebShareRequest::StopAll(
StopAllWebSharesRequest,
)))
.await?;
match response {
Response::WebShare(WebShareResponse::StoppedAll(response)) => {
Ok(usize::try_from(response.stopped).unwrap_or(usize::MAX))
}
Response::Error(error) => Err(error.into()),
response => Err(unexpected_response("web-share stop-all", response)),
}
}
async fn lookup_summary(transport: &TransportClient, id: &str) -> Result<WebShareSummary> {
require_web_share(transport).await?;
let response = transport
.request(Request::WebShare(WebShareRequest::Lookup(
LookupWebShareRequest {
share_id: id.to_owned(),
},
)))
.await?;
match response {
Response::WebShare(WebShareResponse::Lookup(response)) => {
response.share.map(Into::into).ok_or_else(|| {
RmuxError::protocol(rmux_proto::RmuxError::Server(
"web share not found".to_owned(),
))
})
}
Response::Error(error) => Err(error.into()),
response => Err(unexpected_response("web-share lookup", response)),
}
}
async fn web_config(transport: &TransportClient) -> Result<WebConfigInfo> {
require_web_share(transport).await?;
let response = transport
.request(Request::WebShare(WebShareRequest::Config(
WebShareConfigRequest,
)))
.await?;
match response {
Response::WebShare(WebShareResponse::Config(response)) => Ok(response.listener.into()),
Response::Error(error) => Err(error.into()),
response => Err(unexpected_response("web-share config", response)),
}
}
async fn require_web_share(transport: &TransportClient) -> Result<()> {
crate::capabilities::require(transport, &[CAPABILITY_WEB_SHARE]).await
}
fn token_from_url(url: &str) -> Option<&str> {
let fragment = url.split_once('#')?.1;
fragment.split('&').find_map(|param| {
let (key, value) = param.split_once('=')?;
(key == "t").then_some(value)
})
}
fn unexpected_response(operation: &str, response: Response) -> RmuxError {
RmuxError::protocol(rmux_proto::RmuxError::Server(format!(
"rmux daemon sent `{}` response for {operation}",
response.command_name()
)))
}
#[cfg(test)]
mod tests {
use super::token_from_url;
#[test]
fn token_from_url_reads_current_web_share_fragment_contract() {
let url = "https://share.rmux.io/#e=ws://127.0.0.1:9777/share&t=abc123&theme=dark";
assert_eq!(token_from_url(url), Some("abc123"));
}
}