rustant_core/voice/
toggle.rs1use super::meeting_session::{MeetingRecordingSession, MeetingResult, MeetingStatus};
7use super::session::VoiceCommandSession;
8use crate::config::{AgentConfig, MeetingConfig};
9use crate::error::VoiceError;
10use std::path::PathBuf;
11use std::sync::Arc;
12use tokio::sync::Mutex;
13use tracing::info;
14
15pub struct ToggleState {
20 voice_session: Mutex<Option<VoiceCommandSession>>,
21 meeting_session: Mutex<Option<MeetingRecordingSession>>,
22}
23
24impl ToggleState {
25 pub fn new() -> Arc<Self> {
27 Arc::new(Self {
28 voice_session: Mutex::new(None),
29 meeting_session: Mutex::new(None),
30 })
31 }
32
33 pub async fn voice_start(
37 &self,
38 config: AgentConfig,
39 workspace: PathBuf,
40 on_transcription: Arc<dyn Fn(String) + Send + Sync>,
41 ) -> Result<(), VoiceError> {
42 let mut guard = self.voice_session.lock().await;
43 if guard.as_ref().is_some_and(|s| s.is_active()) {
44 return Err(VoiceError::PipelineError {
45 message: "Voice command session is already active".into(),
46 });
47 }
48
49 let session = VoiceCommandSession::start(config, workspace, on_transcription).await?;
50 *guard = Some(session);
51 info!("Voice command session toggled ON");
52 Ok(())
53 }
54
55 pub async fn voice_stop(&self) -> Result<(), VoiceError> {
57 let mut guard = self.voice_session.lock().await;
58 match guard.take() {
59 Some(session) => {
60 session.stop().await?;
61 info!("Voice command session toggled OFF");
62 Ok(())
63 }
64 None => Err(VoiceError::PipelineError {
65 message: "No active voice command session".into(),
66 }),
67 }
68 }
69
70 pub async fn voice_active(&self) -> bool {
72 let guard = self.voice_session.lock().await;
73 guard.as_ref().is_some_and(|s| s.is_active())
74 }
75
76 pub async fn meeting_start(
80 &self,
81 config: MeetingConfig,
82 title: Option<String>,
83 ) -> Result<(), String> {
84 let mut guard = self.meeting_session.lock().await;
85 if guard.as_ref().is_some_and(|s| s.is_active()) {
86 return Err("Meeting recording is already active".into());
87 }
88
89 let session = MeetingRecordingSession::start(config, title).await?;
90 *guard = Some(session);
91 info!("Meeting recording toggled ON");
92 Ok(())
93 }
94
95 pub async fn meeting_stop(&self) -> Result<MeetingResult, String> {
97 let mut guard = self.meeting_session.lock().await;
98 match guard.take() {
99 Some(session) => {
100 let result = session.stop().await?;
101 info!("Meeting recording toggled OFF");
102 Ok(result)
103 }
104 None => Err("No active meeting recording".into()),
105 }
106 }
107
108 pub async fn meeting_active(&self) -> bool {
110 let guard = self.meeting_session.lock().await;
111 guard.as_ref().is_some_and(|s| s.is_active())
112 }
113
114 pub async fn meeting_status(&self) -> Option<MeetingStatus> {
116 let guard = self.meeting_session.lock().await;
117 match guard.as_ref() {
118 Some(session) if session.is_active() => Some(session.status().await),
119 _ => None,
120 }
121 }
122
123 pub fn voice_session_active_sync(&self) -> Option<bool> {
128 self.voice_session
129 .try_lock()
130 .ok()
131 .map(|guard| guard.as_ref().is_some_and(|s| s.is_active()))
132 }
133
134 pub fn meeting_session_active_sync(&self) -> Option<bool> {
137 self.meeting_session
138 .try_lock()
139 .ok()
140 .map(|guard| guard.as_ref().is_some_and(|s| s.is_active()))
141 }
142}
143
144impl Default for ToggleState {
145 fn default() -> Self {
146 Self {
147 voice_session: Mutex::new(None),
148 meeting_session: Mutex::new(None),
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[tokio::test]
158 async fn test_toggle_state_initial() {
159 let state = ToggleState::new();
160 assert!(!state.voice_active().await);
161 assert!(!state.meeting_active().await);
162 assert!(state.meeting_status().await.is_none());
163 }
164
165 #[tokio::test]
166 async fn test_voice_stop_without_start() {
167 let state = ToggleState::new();
168 let result = state.voice_stop().await;
169 assert!(result.is_err());
170 }
171
172 #[tokio::test]
173 async fn test_meeting_stop_without_start() {
174 let state = ToggleState::new();
175 let result = state.meeting_stop().await;
176 assert!(result.is_err());
177 }
178}