videosdk-server-sdk 0.1.0

Rust server SDK for the VideoSDK v2 REST APIs
Documentation
//! A room-keyed view over a room's live participants.

use futures_util::stream::{self, BoxStream};
use futures_util::{Stream, StreamExt as _};
use reqwest::Method;
use serde::Serialize;

use crate::client::{CallOptions, Client};
use crate::error::{Error, Result};
use crate::pagination::{auto_page, paginate, static_page, ListParams, Page};
use crate::resources::escape;
use crate::resources::session_utils::resolve_active_session_id;
use crate::resources::sessions::{participants_fetcher, Participant};

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct RemoveWire<'a> {
    room_id: &'a str,
    participant_id: &'a str,
}

/// A room-keyed view over a room's live participants. Reached via
/// [`Client::participants`].
///
/// This is a convenience over [`SessionsResource`](crate::SessionsResource):
/// each method resolves the room's current session for you, so you work with a
/// `room_id` instead of a `session_id`. For historical sessions and their
/// participants, use [`Client::sessions`] directly.
#[derive(Debug, Clone, Copy)]
pub struct ParticipantsResource<'a> {
    client: &'a Client,
}

impl<'a> ParticipantsResource<'a> {
    pub(crate) fn new(client: &'a Client) -> Self {
        Self { client }
    }

    /// Lists the active participants in a room's current session, one page at a
    /// time. Resolves to an empty page when the room has no live session.
    pub async fn list(&self, room_id: &str, params: ListParams) -> Result<Page<Participant>> {
        let Some(session_id) = resolve_active_session_id(self.client, room_id).await? else {
            return Ok(static_page(Vec::new()));
        };
        let path = format!("/v2/sessions/{}/participants/active", escape(&session_id));
        paginate(
            participants_fetcher(self.client, path),
            &params,
            "data",
            None,
        )
        .await
    }

    /// Lists a room's active participants, transparently fetching every page.
    /// Yields nothing when the room has no live session.
    pub fn list_stream(
        &self,
        room_id: &str,
        params: ListParams,
    ) -> impl Stream<Item = Result<Participant>> + Send {
        let client = self.client.clone();
        let room_id = room_id.to_string();

        // The active session must be resolved before the first page can be
        // fetched, so defer that lookup into the stream too.
        stream::once(async move {
            let inner: BoxStream<'static, Result<Participant>> =
                match resolve_active_session_id(&client, &room_id).await {
                    Ok(Some(session_id)) => {
                        let path =
                            format!("/v2/sessions/{}/participants/active", escape(&session_id));
                        auto_page(participants_fetcher(&client, path), params, "data", None).boxed()
                    }
                    Ok(None) => stream::empty().boxed(),
                    Err(err) => stream::once(async move { Err(err) }).boxed(),
                };
            inner
        })
        .flatten()
    }

    /// Fetches a single active participant in a room's current session.
    ///
    /// Returns a not-found error when the room has no live session.
    pub async fn get(&self, room_id: &str, participant_id: &str) -> Result<Participant> {
        let Some(session_id) = resolve_active_session_id(self.client, room_id).await? else {
            return Err(Error::not_found(format!(
                "no active session for room {room_id}"
            )));
        };
        let path = format!(
            "/v2/sessions/{}/participants/{}",
            escape(&session_id),
            escape(participant_id)
        );
        self.client
            .json(Method::GET, &path, CallOptions::new())
            .await
    }

    /// Removes (kicks) a participant from a room's live session, returning the
    /// server's confirmation message. The server resolves the active session.
    pub async fn remove(&self, room_id: &str, participant_id: &str) -> Result<String> {
        let body = RemoveWire {
            room_id,
            participant_id,
        };
        self.client
            .message(
                Method::POST,
                "/v2/sessions/participants/remove",
                CallOptions::json(&body)?,
            )
            .await
    }
}