1use dashmap::{DashMap, DashSet};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(tag = "type", rename_all = "snake_case")]
8pub enum RtcMessage {
9 Offer {
10 from: Uuid,
11 to: Uuid,
12 sdp: String,
13 #[serde(default, skip_serializing_if = "serde_json::Value::is_null")]
14 meta: serde_json::Value,
15 },
16 Answer {
17 from: Uuid,
18 to: Uuid,
19 sdp: String,
20 #[serde(default, skip_serializing_if = "serde_json::Value::is_null")]
21 meta: serde_json::Value,
22 },
23 Candidate {
24 from: Uuid,
25 to: Uuid,
26 candidate: String,
27 },
28 CandidateBatch {
29 from: Uuid,
30 to: Uuid,
31 candidates: Vec<String>,
32 },
33 Status {
34 from: Uuid,
35 to: Uuid,
36 status: String,
37 },
38 PeerList {
39 peers: Vec<Uuid>,
40 },
41 PeerDiscovery {
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 room: Option<String>,
44 },
45 TurnCredentials(TurnCredential),
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct TurnCredential {
50 pub username: String,
51 pub credential: String,
52 pub ttl_seconds: u64,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub server: Option<String>,
55}
56
57#[derive(Debug, Default)]
58pub struct RtcRouter {
59 seen_candidates: DashMap<(Uuid, Uuid), DashSet<String>>,
61}
62
63impl RtcRouter {
64 pub fn new() -> Self {
65 Self {
66 seen_candidates: DashMap::new(),
67 }
68 }
69
70 pub fn dedup_candidate(&self, from: Uuid, to: Uuid, candidate: &str) -> bool {
71 let set = self.seen_candidates.entry((from, to)).or_default();
72 set.insert(candidate.to_string())
73 }
74
75 pub fn dedup_batch(&self, from: Uuid, to: Uuid, candidates: Vec<String>) -> Vec<String> {
76 candidates
77 .into_iter()
78 .filter(|c| self.dedup_candidate(from, to, c))
79 .collect()
80 }
81}