enigma_rtc/
mock.rs

1use std::sync::atomic::{AtomicBool, Ordering};
2use std::sync::Mutex;
3
4use tokio::sync::mpsc;
5
6use crate::error::{EnigmaRtcError, RtcResult};
7use crate::signaling::decode_signaling;
8use crate::types::{RtcEvent, SignalingMessage};
9use crate::webrtc::RtcEngine;
10
11pub struct MockWebRtcEngine {
12    next_offer: Mutex<String>,
13    next_answer: Mutex<String>,
14    applied_answers: Mutex<Vec<String>>,
15    remote_candidates: Mutex<Vec<String>>,
16    event_sender: Mutex<Option<mpsc::UnboundedSender<RtcEvent>>>,
17    microphone_enabled: AtomicBool,
18    camera_enabled: AtomicBool,
19}
20
21impl MockWebRtcEngine {
22    pub fn new() -> Self {
23        Self {
24            next_offer: Mutex::new("mock-offer".to_string()),
25            next_answer: Mutex::new("mock-answer".to_string()),
26            applied_answers: Mutex::new(Vec::new()),
27            remote_candidates: Mutex::new(Vec::new()),
28            event_sender: Mutex::new(None),
29            microphone_enabled: AtomicBool::new(true),
30            camera_enabled: AtomicBool::new(true),
31        }
32    }
33
34    pub fn set_offer(&self, sdp: impl Into<String>) {
35        if let Ok(mut guard) = self.next_offer.lock() {
36            *guard = sdp.into();
37        }
38    }
39
40    pub fn set_answer(&self, sdp: impl Into<String>) {
41        if let Ok(mut guard) = self.next_answer.lock() {
42            *guard = sdp.into();
43        }
44    }
45
46    pub fn emit_candidate(&self, candidate: &str) -> RtcResult<()> {
47        let sender = self
48            .event_sender
49            .lock()
50            .map_err(|_| EnigmaRtcError::ChannelClosed)?
51            .clone();
52        if let Some(tx) = sender {
53            let message = SignalingMessage::IceCandidate {
54                candidate: candidate.to_string(),
55                sdp_mid: Some("0".to_string()),
56                sdp_mline_index: Some(0),
57            };
58            tx.send(RtcEvent::LocalIceCandidate(message))
59                .map_err(|_| EnigmaRtcError::ChannelClosed)?;
60        }
61        Ok(())
62    }
63
64    pub fn last_applied_answer(&self) -> Option<String> {
65        self.applied_answers
66            .lock()
67            .ok()
68            .and_then(|v| v.last().cloned())
69    }
70
71    pub fn candidates(&self) -> Vec<String> {
72        self.remote_candidates
73            .lock()
74            .map(|v| v.clone())
75            .unwrap_or_default()
76    }
77
78    pub fn microphone_enabled(&self) -> bool {
79        self.microphone_enabled.load(Ordering::SeqCst)
80    }
81
82    pub fn camera_enabled(&self) -> bool {
83        self.camera_enabled.load(Ordering::SeqCst)
84    }
85}
86
87impl RtcEngine for MockWebRtcEngine {
88    fn set_event_sender(&self, sender: mpsc::UnboundedSender<RtcEvent>) -> RtcResult<()> {
89        let mut slot = self
90            .event_sender
91            .lock()
92            .map_err(|_| EnigmaRtcError::ChannelClosed)?;
93        *slot = Some(sender);
94        Ok(())
95    }
96
97    fn create_offer(&self) -> RtcResult<String> {
98        self.next_offer
99            .lock()
100            .map(|s| s.clone())
101            .map_err(|_| EnigmaRtcError::InvalidState)
102    }
103
104    fn create_answer(&self, offer_sdp: &str) -> RtcResult<String> {
105        if offer_sdp.trim().is_empty() {
106            return Err(EnigmaRtcError::InvalidSdp);
107        }
108        self.next_answer
109            .lock()
110            .map(|s| s.clone())
111            .map_err(|_| EnigmaRtcError::InvalidState)
112    }
113
114    fn apply_answer(&self, answer_sdp: &str) -> RtcResult<()> {
115        if answer_sdp.trim().is_empty() {
116            return Err(EnigmaRtcError::InvalidSdp);
117        }
118        if let Ok(mut guard) = self.applied_answers.lock() {
119            guard.push(answer_sdp.to_string());
120            return Ok(());
121        }
122        Err(EnigmaRtcError::InvalidState)
123    }
124
125    fn add_remote_candidate(&self, candidate_json: &str) -> RtcResult<()> {
126        match decode_signaling(candidate_json)? {
127            SignalingMessage::IceCandidate { candidate, .. } => {
128                if let Ok(mut guard) = self.remote_candidates.lock() {
129                    guard.push(candidate);
130                    return Ok(());
131                }
132                Err(EnigmaRtcError::InvalidState)
133            }
134            _ => Err(EnigmaRtcError::InvalidCandidate),
135        }
136    }
137
138    fn set_microphone_enabled(&self, enabled: bool) -> RtcResult<()> {
139        self.microphone_enabled.store(enabled, Ordering::SeqCst);
140        Ok(())
141    }
142
143    fn set_camera_enabled(&self, enabled: bool) -> RtcResult<()> {
144        self.camera_enabled.store(enabled, Ordering::SeqCst);
145        Ok(())
146    }
147}