openrtc 0.2.0

OpenRTC: a Rust-first P2P runtime for device discovery, signaling, and iroh/QUIC networking.
Documentation
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AppNamespace {
    app_tag: String,
}

const SPACE_PREFIX: &str = "space::";

impl AppNamespace {
    pub fn new(app_tag: impl Into<String>) -> Self {
        Self {
            app_tag: app_tag.into(),
        }
    }

    pub fn app_tag(&self) -> &str {
        &self.app_tag
    }

    /// Returns true if this namespace is a Space Mode space (not an app-scoped namespace).
    pub fn is_space(&self) -> bool {
        self.app_tag.starts_with(SPACE_PREFIX)
    }

    /// The namespace ID for Space Mode spaces (the sha256 hash portion).
    fn space_id(&self) -> &str {
        self.app_tag.trim_start_matches(SPACE_PREFIX)
    }

    /// Root path: `apps/{appTag}` for app-scoped, `spaces/{namespaceId}` for Space Mode.
    pub fn app_root(&self) -> String {
        if self.is_space() {
            format!("spaces/{}", self.space_id())
        } else {
            format!("apps/{}", self.app_tag)
        }
    }

    pub fn user_root(&self, user_id: &str) -> String {
        format!("{}/users/{}", self.app_root(), user_id)
    }

    pub fn devices_collection(&self, user_id: &str) -> String {
        if self.is_space() {
            format!("{}/devices", self.app_root())
        } else {
            format!("{}/devices", self.user_root(user_id))
        }
    }

    pub fn sessions_collection(&self) -> String {
        format!("{}/sessions", self.app_root())
    }

    pub fn messages_collection(&self) -> String {
        format!("{}/messages", self.app_root())
    }

    pub fn rooms_collection(&self) -> String {
        format!("{}/rooms", self.app_root())
    }

    pub fn room_members_collection(&self, room_id: &str) -> String {
        format!("{}/rooms/{}/members", self.app_root(), room_id)
    }

    pub fn room_access_collection(&self, room_id: &str) -> String {
        format!("{}/rooms/{}/access", self.app_root(), room_id)
    }

    pub fn ephemeral_collection(&self) -> String {
        format!("{}/ephemeral", self.app_root())
    }
}

/// Namespace for Space Mode spaces (no user identity required).
/// All paths are rooted at `spaces/{namespace_id}` where namespace_id = sha256("apiKey:spaceKey")
/// (colon-separated UTF-8 string, matching `deriveSpaceNamespace` in the TypeScript SDK).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpaceNamespace {
    namespace_id: String,
}

impl SpaceNamespace {
    pub fn new(namespace_id: impl Into<String>) -> Self {
        Self {
            namespace_id: namespace_id.into(),
        }
    }

    pub fn namespace_id(&self) -> &str {
        &self.namespace_id
    }

    pub fn space_root(&self) -> String {
        format!("spaces/{}", self.namespace_id)
    }

    pub fn devices_collection(&self) -> String {
        format!("{}/devices", self.space_root())
    }

    pub fn sessions_collection(&self) -> String {
        format!("{}/sessions", self.space_root())
    }

    pub fn rooms_collection(&self) -> String {
        format!("{}/rooms", self.space_root())
    }

    pub fn room_members_collection(&self, room_id: &str) -> String {
        format!("{}/rooms/{}/members", self.space_root(), room_id)
    }

    pub fn room_access_collection(&self, room_id: &str) -> String {
        format!("{}/rooms/{}/access", self.space_root(), room_id)
    }
}

/// Unified namespace type covering both app-scoped and space-scoped (Space Mode) paths.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RoomNamespace {
    App(AppNamespace),
    Space(SpaceNamespace),
}

impl RoomNamespace {
    pub fn app_tag(&self) -> &str {
        match self {
            RoomNamespace::App(ns) => ns.app_tag(),
            RoomNamespace::Space(ns) => ns.namespace_id(),
        }
    }

    pub fn room_members_collection(&self, room_id: &str) -> String {
        match self {
            RoomNamespace::App(ns) => ns.room_members_collection(room_id),
            RoomNamespace::Space(ns) => ns.room_members_collection(room_id),
        }
    }

    pub fn rooms_collection(&self) -> String {
        match self {
            RoomNamespace::App(ns) => ns.rooms_collection(),
            RoomNamespace::Space(ns) => ns.rooms_collection(),
        }
    }

    pub fn room_access_collection(&self, room_id: &str) -> String {
        match self {
            RoomNamespace::App(ns) => ns.room_access_collection(room_id),
            RoomNamespace::Space(ns) => ns.room_access_collection(room_id),
        }
    }
}

// ---------------------------------------------------------------------------
// Billing schema helpers (read-only paths used by the SDK)
// ---------------------------------------------------------------------------

/// Path to a developer app's billing document, keyed by API key.
/// Document lives at `developer_apps/{apiKey}` and is written by Firebase Functions.
pub fn developer_app_doc(api_key: &str) -> String {
    format!("developer_apps/{}", api_key)
}

/// Path to the usage counters sub-document for a developer app.
pub fn developer_app_usage_doc(api_key: &str) -> String {
    format!("developer_apps/{}/usage/counters", api_key)
}