scirs2_metrics/visualization/advanced_interactive/
collaboration.rs1#![allow(clippy::too_many_arguments)]
7#![allow(dead_code)]
8
9use super::core::{CollaborationConfig, PermissionLevel};
10use crate::error::{MetricsError, Result};
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use std::collections::HashMap;
14use std::time::{Duration, Instant};
15
16#[derive(Debug)]
18pub struct CollaborationManager {
19 config: CollaborationConfig,
21 sessions: HashMap<String, UserSession>,
23 shared_state: SharedState,
25 operation_history: Vec<Operation>,
27 conflict_resolver: ConflictResolver,
29}
30
31#[derive(Debug, Clone)]
33pub struct UserSession {
34 pub session_id: String,
36 pub user_id: String,
38 pub user_name: String,
40 pub permissions: PermissionLevel,
42 pub last_activity: Instant,
44 pub cursor_position: Option<CursorPosition>,
46 pub selections: Vec<Selection>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct CursorPosition {
53 pub x: f64,
55 pub y: f64,
57 pub widget_id: Option<String>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct Selection {
64 pub id: String,
66 pub widget_ids: Vec<String>,
68 pub bounds: SelectionBounds,
70 pub selection_type: SelectionType,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct SelectionBounds {
77 pub x: f64,
79 pub y: f64,
81 pub width: f64,
83 pub height: f64,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub enum SelectionType {
90 Single,
92 Multiple,
94 Area,
96 Text,
98}
99
100#[derive(Debug, Clone)]
102pub struct SharedState {
103 pub dashboard_state: Value,
105 pub widget_states: HashMap<String, Value>,
107 pub global_settings: HashMap<String, Value>,
109 pub version_vector: HashMap<String, u64>,
111}
112
113#[derive(Debug, Clone)]
115pub struct Operation {
116 pub id: String,
118 pub operation_type: OperationType,
120 pub user_id: String,
122 pub timestamp: Instant,
124 pub data: Value,
126 pub target: String,
128 pub dependencies: Vec<String>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub enum OperationType {
135 Create,
137 Update,
139 Delete,
141 Move,
143 Resize,
145 StyleChange,
147 DataUpdate,
149 Custom(String),
151}
152
153#[derive(Debug)]
155pub struct ConflictResolver {
156 strategy: ConflictResolutionStrategy,
158 pending_conflicts: Vec<Conflict>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub enum ConflictResolutionStrategy {
165 LastWriterWins,
167 FirstWriterWins,
169 OperationalTransform,
171 Manual,
173 Custom(String),
175}
176
177#[derive(Debug, Clone)]
179pub struct Conflict {
180 pub id: String,
182 pub operations: Vec<Operation>,
184 pub conflict_type: ConflictType,
186 pub status: ConflictStatus,
188 pub proposed_resolution: Option<Operation>,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub enum ConflictType {
195 ConcurrentModification,
197 VersionMismatch,
199 PermissionConflict,
201 DataIntegrity,
203 Custom(String),
205}
206
207#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
209pub enum ConflictStatus {
210 Pending,
212 AutoResolved,
214 ManuallyResolved,
216 Ignored,
218}
219
220impl CollaborationManager {
221 pub fn new(config: CollaborationConfig) -> Self {
223 Self {
224 config,
225 sessions: HashMap::new(),
226 shared_state: SharedState::new(),
227 operation_history: Vec::new(),
228 conflict_resolver: ConflictResolver::new(),
229 }
230 }
231
232 pub fn add_session(&mut self, session: UserSession) -> Result<()> {
234 if self.sessions.len() >= self.config.max_collaborators as usize {
235 return Err(MetricsError::ComputationError(
236 "Maximum number of collaborators reached".to_string(),
237 ));
238 }
239
240 self.sessions.insert(session.session_id.clone(), session);
241 Ok(())
242 }
243
244 pub fn remove_session(&mut self, session_id: &str) -> Result<()> {
246 self.sessions.remove(session_id);
247 Ok(())
248 }
249
250 pub fn apply_operation(&mut self, operation: Operation) -> Result<()> {
252 if let Some(session) = self.sessions.get(&operation.user_id) {
254 if !self.has_permission(&session.permissions, &operation.operation_type) {
255 return Err(MetricsError::InvalidInput(
256 "Insufficient permissions".to_string(),
257 ));
258 }
259 }
260
261 if let Some(conflict) = self
263 .conflict_resolver
264 .detect_conflict(&operation, &self.operation_history)
265 {
266 self.conflict_resolver.pending_conflicts.push(conflict);
267 return Ok(()); }
269
270 self.apply_operation_to_state(&operation)?;
272
273 self.operation_history.push(operation);
275
276 Ok(())
277 }
278
279 fn apply_operation_to_state(&mut self, operation: &Operation) -> Result<()> {
281 match operation.operation_type {
282 OperationType::Create => {
283 self.shared_state
284 .widget_states
285 .insert(operation.target.clone(), operation.data.clone());
286 }
287 OperationType::Update => {
288 if let Some(widget_state) =
289 self.shared_state.widget_states.get_mut(&operation.target)
290 {
291 if let (Value::Object(current), Value::Object(update)) =
293 (widget_state, &operation.data)
294 {
295 for (key, value) in update {
296 current.insert(key.clone(), value.clone());
297 }
298 }
299 }
300 }
301 OperationType::Delete => {
302 self.shared_state.widget_states.remove(&operation.target);
303 }
304 _ => {
305 }
307 }
308
309 self.shared_state
311 .version_vector
312 .entry(operation.user_id.clone())
313 .and_modify(|v| *v += 1)
314 .or_insert(1);
315
316 Ok(())
317 }
318
319 fn has_permission(&self, permission: &PermissionLevel, operation: &OperationType) -> bool {
321 match (permission, operation) {
322 (PermissionLevel::Admin, _) => true,
323 (
324 PermissionLevel::Edit,
325 OperationType::Create | OperationType::Update | OperationType::Delete,
326 ) => true,
327 (PermissionLevel::Comment, _) => false, (PermissionLevel::ReadOnly, _) => false,
329 _ => false,
330 }
331 }
332
333 pub fn get_active_sessions(&self) -> Vec<&UserSession> {
335 self.sessions.values().collect()
336 }
337
338 pub fn update_cursor(&mut self, session_id: &str, position: CursorPosition) -> Result<()> {
340 if let Some(session) = self.sessions.get_mut(session_id) {
341 session.cursor_position = Some(position);
342 session.last_activity = Instant::now();
343 }
344 Ok(())
345 }
346
347 pub fn get_shared_state(&self) -> &SharedState {
349 &self.shared_state
350 }
351
352 pub fn resolve_conflicts(&mut self) -> Result<Vec<Operation>> {
354 self.conflict_resolver.resolve_conflicts()
355 }
356}
357
358impl SharedState {
359 pub fn new() -> Self {
361 Self {
362 dashboard_state: Value::Object(serde_json::Map::new()),
363 widget_states: HashMap::new(),
364 global_settings: HashMap::new(),
365 version_vector: HashMap::new(),
366 }
367 }
368}
369
370impl ConflictResolver {
371 pub fn new() -> Self {
373 Self {
374 strategy: ConflictResolutionStrategy::LastWriterWins,
375 pending_conflicts: Vec::new(),
376 }
377 }
378
379 pub fn detect_conflict(
381 &self,
382 operation: &Operation,
383 history: &[Operation],
384 ) -> Option<Conflict> {
385 for existing_op in history.iter().rev().take(10) {
387 if existing_op.target == operation.target
388 && existing_op.timestamp
389 > operation
390 .timestamp
391 .checked_sub(Duration::from_secs(5))
392 .unwrap_or(operation.timestamp)
393 {
394 return Some(Conflict {
395 id: format!("conflict_{}", scirs2_core::random::random::<u64>()),
396 operations: vec![existing_op.clone(), operation.clone()],
397 conflict_type: ConflictType::ConcurrentModification,
398 status: ConflictStatus::Pending,
399 proposed_resolution: None,
400 });
401 }
402 }
403 None
404 }
405
406 pub fn resolve_conflicts(&mut self) -> Result<Vec<Operation>> {
408 let mut resolved_operations = Vec::new();
409
410 for conflict in &mut self.pending_conflicts {
411 match self.strategy {
412 ConflictResolutionStrategy::LastWriterWins => {
413 if let Some(latest_op) =
414 conflict.operations.iter().max_by_key(|op| op.timestamp)
415 {
416 resolved_operations.push(latest_op.clone());
417 conflict.status = ConflictStatus::AutoResolved;
418 }
419 }
420 ConflictResolutionStrategy::FirstWriterWins => {
421 if let Some(earliest_op) =
422 conflict.operations.iter().min_by_key(|op| op.timestamp)
423 {
424 resolved_operations.push(earliest_op.clone());
425 conflict.status = ConflictStatus::AutoResolved;
426 }
427 }
428 _ => {
429 }
431 }
432 }
433
434 self.pending_conflicts
436 .retain(|conflict| conflict.status == ConflictStatus::Pending);
437
438 Ok(resolved_operations)
439 }
440}
441
442impl Default for SharedState {
443 fn default() -> Self {
444 Self::new()
445 }
446}