git_iris/companion/
session.rs

1//! Session state tracking for Iris Companion
2//!
3//! Tracks files touched, time elapsed, and commits made during a session.
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::path::PathBuf;
9use uuid::Uuid;
10
11/// Activity tracking for a single file
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct FileActivity {
14    /// Path to the file
15    pub path: PathBuf,
16    /// When this file was first touched in the session
17    pub first_touched: DateTime<Utc>,
18    /// When this file was last touched
19    pub last_touched: DateTime<Utc>,
20    /// Number of times this file was touched
21    pub touch_count: u32,
22}
23
24impl FileActivity {
25    /// Create a new file activity record
26    pub fn new(path: PathBuf) -> Self {
27        let now = Utc::now();
28        Self {
29            path,
30            first_touched: now,
31            last_touched: now,
32            touch_count: 1,
33        }
34    }
35
36    /// Record another touch
37    pub fn touch(&mut self) {
38        self.last_touched = Utc::now();
39        self.touch_count += 1;
40    }
41}
42
43/// Session state for the current Studio session
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct SessionState {
46    /// Unique session identifier
47    pub session_id: Uuid,
48    /// Repository path
49    pub repo_path: PathBuf,
50    /// Current branch name
51    pub branch: String,
52    /// When the session started
53    pub started_at: DateTime<Utc>,
54    /// Last activity timestamp
55    pub last_activity: DateTime<Utc>,
56    /// Files touched during this session
57    pub files_touched: HashMap<PathBuf, FileActivity>,
58    /// Commits made during this session (hashes)
59    pub commits_made: Vec<String>,
60}
61
62impl SessionState {
63    /// Create a new session
64    pub fn new(repo_path: PathBuf, branch: String) -> Self {
65        let now = Utc::now();
66        Self {
67            session_id: Uuid::new_v4(),
68            repo_path,
69            branch,
70            started_at: now,
71            last_activity: now,
72            files_touched: HashMap::new(),
73            commits_made: Vec::new(),
74        }
75    }
76
77    /// Record a file touch
78    pub fn touch_file(&mut self, path: PathBuf) {
79        self.last_activity = Utc::now();
80        self.files_touched
81            .entry(path.clone())
82            .and_modify(FileActivity::touch)
83            .or_insert_with(|| FileActivity::new(path));
84    }
85
86    /// Record a commit
87    pub fn record_commit(&mut self, hash: String) {
88        self.last_activity = Utc::now();
89        self.commits_made.push(hash);
90    }
91
92    /// Get session duration
93    pub fn duration(&self) -> chrono::Duration {
94        Utc::now() - self.started_at
95    }
96
97    /// Get number of files touched
98    pub fn files_count(&self) -> usize {
99        self.files_touched.len()
100    }
101
102    /// Get files ordered by most recently touched
103    pub fn recent_files(&self) -> Vec<&FileActivity> {
104        let mut files: Vec<_> = self.files_touched.values().collect();
105        files.sort_by(|a, b| b.last_touched.cmp(&a.last_touched));
106        files
107    }
108
109    /// Get time since last commit (if any)
110    pub fn time_since_last_commit(&self) -> Option<chrono::Duration> {
111        if self.commits_made.is_empty() {
112            None
113        } else {
114            Some(Utc::now() - self.last_activity)
115        }
116    }
117
118    /// Update branch (for branch switches)
119    pub fn set_branch(&mut self, branch: String) {
120        self.branch = branch;
121        self.last_activity = Utc::now();
122    }
123}