openrtc 0.2.1

OpenRTC: a Rust-first P2P runtime for device discovery, signaling, and iroh/QUIC networking.
Documentation
//! Scope classification hooks for correlation + session labeling.
//!
//! OpenRTC uses an application-provided `scope` string for session admission.
//! Historically, some higher-level flows used well-known scope values/prefixes
//! (e.g. `drive-grant:<id>` and `user-device`) and OpenRTC inferred `session_kind`
//! / `grant_id` directly from those strings.
//!
//! This module centralizes that logic behind a small trait so OpenRTC core is
//! not structurally tied to any app-domain naming conventions. The default
//! classifier preserves legacy behavior for existing Plutonium/OpenRTC scopes.

#![cfg(not(target_arch = "wasm32"))]

use std::sync::Arc;

/// Derived labels for logs / diagnostics.
#[derive(Debug, Clone, Default)]
pub struct ScopeClassification {
    pub session_kind: Option<&'static str>,
    pub grant_id: Option<String>,
    pub scope: Option<String>,
}

pub trait ScopeClassifier: Send + Sync + 'static {
    /// Given the admitted scopes on a connection (ordered arbitrary), choose
    /// the most specific classification. Implementations should be cheap.
    fn classify_scopes(&self, scopes: &[String]) -> ScopeClassification;
}

/// Legacy classifier that preserves current behavior for existing OpenRTC/Plutonium
/// scope conventions:
/// - prefer any `drive-grant:<id>` scope → `session_kind=drive-grant-guest`, `grant_id=<id>`
/// - else if `user-device` present → `session_kind=app-user-device`
#[derive(Debug, Clone, Default)]
pub struct LegacyScopeClassifier;

impl ScopeClassifier for LegacyScopeClassifier {
    fn classify_scopes(&self, scopes: &[String]) -> ScopeClassification {
        for scope in scopes {
            if let Some(grant_id) = scope.strip_prefix("drive-grant:") {
                return ScopeClassification {
                    session_kind: Some("drive-grant-guest"),
                    grant_id: Some(grant_id.to_string()),
                    scope: Some(scope.clone()),
                };
            }
        }

        for scope in scopes {
            if scope == "user-device" {
                return ScopeClassification {
                    session_kind: Some("app-user-device"),
                    grant_id: None,
                    scope: Some(scope.clone()),
                };
            }
        }

        ScopeClassification::default()
    }
}

pub fn default_scope_classifier() -> Arc<dyn ScopeClassifier> {
    Arc::new(LegacyScopeClassifier::default())
}