use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use std::time::{Duration, Instant};
pub const MAX_SESSION_DURATION: Duration = Duration::from_secs(10 * 60);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearnedEvent {
pub ts_ms: u64,
pub event_type: String,
pub x: Option<i32>,
pub y: Option<i32>,
pub key: Option<String>,
pub element_role: Option<String>,
pub element_name: Option<String>,
pub element_id: Option<String>,
pub app_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SessionStatus {
Idle,
Recording,
Stopped,
}
struct SessionInner {
status: SessionStatus,
task_description: Option<String>,
started_at: Option<Instant>,
events: Vec<LearnedEvent>,
}
pub struct LearningSession {
inner: Mutex<SessionInner>,
}
impl Default for LearningSession {
fn default() -> Self {
Self::new()
}
}
impl LearningSession {
pub fn new() -> Self {
Self {
inner: Mutex::new(SessionInner {
status: SessionStatus::Idle,
task_description: None,
started_at: None,
events: vec![],
}),
}
}
pub fn start(&self, task_description: Option<String>) -> Result<(), String> {
let mut g = self.inner.lock().unwrap();
if g.status == SessionStatus::Recording {
return Err("Already recording".to_string());
}
g.status = SessionStatus::Recording;
g.task_description = task_description;
g.started_at = Some(Instant::now());
g.events.clear();
Ok(())
}
pub fn stop(&self) -> Result<Vec<LearnedEvent>, String> {
let mut g = self.inner.lock().unwrap();
if g.status != SessionStatus::Recording {
return Err("Not recording".to_string());
}
g.status = SessionStatus::Stopped;
Ok(g.events.clone())
}
pub fn push_event(&self, event: LearnedEvent) {
let mut g = self.inner.lock().unwrap();
if g.status != SessionStatus::Recording {
return;
}
if let Some(started) = g.started_at {
if started.elapsed() > MAX_SESSION_DURATION {
g.status = SessionStatus::Stopped;
return;
}
}
g.events.push(event);
}
pub fn status(&self) -> SessionStatus {
self.inner.lock().unwrap().status.clone()
}
pub fn event_count(&self) -> usize {
self.inner.lock().unwrap().events.len()
}
pub fn elapsed_secs(&self) -> u64 {
let g = self.inner.lock().unwrap();
g.started_at.map(|t| t.elapsed().as_secs()).unwrap_or(0)
}
pub fn task_description(&self) -> Option<String> {
self.inner.lock().unwrap().task_description.clone()
}
}