venus_server/
protocol.rs

1//! WebSocket protocol messages for Venus server.
2//!
3//! Defines the message types exchanged between client and server.
4
5use serde::{Deserialize, Serialize};
6use venus::widgets::{WidgetDef, WidgetValue};
7use venus_core::graph::CellId;
8
9// Re-export MoveDirection from venus_core for use in protocol messages
10pub use venus_core::graph::MoveDirection;
11
12/// Messages sent from client to server.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(tag = "type", rename_all = "snake_case")]
15pub enum ClientMessage {
16    /// Request current notebook state.
17    GetState,
18
19    /// Edit a cell's source code.
20    CellEdit {
21        /// Cell identifier.
22        cell_id: CellId,
23        /// New source code.
24        source: String,
25    },
26
27    /// Execute a specific cell.
28    ExecuteCell {
29        /// Cell to execute.
30        cell_id: CellId,
31    },
32
33    /// Execute all cells.
34    ExecuteAll,
35
36    /// Execute cells that need re-execution.
37    ExecuteDirty,
38
39    /// Interrupt running execution.
40    Interrupt,
41
42    /// Sync notebook to .ipynb format.
43    Sync,
44
45    /// Request dependency graph.
46    GetGraph,
47
48    /// Update a widget value.
49    WidgetUpdate {
50        /// Cell containing the widget.
51        cell_id: CellId,
52        /// Widget identifier within the cell.
53        widget_id: String,
54        /// New widget value.
55        value: WidgetValue,
56    },
57
58    /// Select a history entry to use as the current output.
59    SelectHistory {
60        /// Cell to select history for.
61        cell_id: CellId,
62        /// History index (0 = oldest).
63        index: usize,
64    },
65
66    /// Insert a new cell.
67    InsertCell {
68        /// Cell ID to insert after. None = insert at end.
69        after_cell_id: Option<CellId>,
70    },
71
72    /// Delete a cell.
73    DeleteCell {
74        /// Cell to delete.
75        cell_id: CellId,
76    },
77
78    /// Duplicate a cell.
79    DuplicateCell {
80        /// Cell to duplicate.
81        cell_id: CellId,
82    },
83
84    /// Move a cell up or down.
85    MoveCell {
86        /// Cell to move.
87        cell_id: CellId,
88        /// Direction to move.
89        direction: MoveDirection,
90    },
91
92    /// Undo the last cell management operation.
93    Undo,
94
95    /// Redo the last undone operation.
96    Redo,
97
98    /// Restart the kernel (kill WorkerPool, clear memory state, preserve source).
99    RestartKernel,
100
101    /// Clear all cell outputs without restarting the kernel.
102    ClearOutputs,
103
104    /// Rename a cell's display name.
105    RenameCell {
106        /// Cell to rename.
107        cell_id: CellId,
108        /// New display name.
109        new_display_name: String,
110    },
111
112    /// Insert a new markdown cell.
113    InsertMarkdownCell {
114        /// Markdown content.
115        content: String,
116        /// Cell ID to insert after. None = insert at beginning.
117        after_cell_id: Option<CellId>,
118    },
119
120    /// Edit a markdown cell's content.
121    EditMarkdownCell {
122        /// Cell to edit.
123        cell_id: CellId,
124        /// New markdown content.
125        new_content: String,
126    },
127
128    /// Delete a markdown cell.
129    DeleteMarkdownCell {
130        /// Cell to delete.
131        cell_id: CellId,
132    },
133
134    /// Move a markdown cell up or down.
135    MoveMarkdownCell {
136        /// Cell to move.
137        cell_id: CellId,
138        /// Direction to move.
139        direction: MoveDirection,
140    },
141}
142
143/// Messages sent from server to client.
144#[derive(Debug, Clone, Serialize, Deserialize)]
145#[serde(tag = "type", rename_all = "snake_case")]
146pub enum ServerMessage {
147    /// Full notebook state (sent on connection or refresh).
148    NotebookState {
149        /// Path to the notebook file.
150        path: String,
151        /// All cells in the notebook.
152        cells: Vec<CellState>,
153        /// Source order (cell IDs in the order they appear in the .rs file).
154        source_order: Vec<CellId>,
155        /// Execution order (topologically sorted cell IDs for dependency resolution).
156        execution_order: Vec<CellId>,
157    },
158
159    /// Cell execution started.
160    CellStarted {
161        /// Cell that started executing.
162        cell_id: CellId,
163    },
164
165    /// Cell execution completed successfully.
166    CellCompleted {
167        /// Cell that completed.
168        cell_id: CellId,
169        /// Execution time in milliseconds.
170        duration_ms: u64,
171        /// Cell output (serialized).
172        output: Option<CellOutput>,
173    },
174
175    /// Cell execution failed.
176    CellError {
177        /// Cell that failed.
178        cell_id: CellId,
179        /// Error message.
180        error: String,
181        /// Source location if available.
182        location: Option<SourceLocation>,
183    },
184
185    /// Compilation error (before execution).
186    CompileError {
187        /// Cell with compilation error.
188        cell_id: CellId,
189        /// Compiler errors.
190        errors: Vec<CompileErrorInfo>,
191    },
192
193    /// Dependency graph updated.
194    GraphUpdated {
195        /// New dependency edges.
196        edges: Vec<DependencyEdge>,
197        /// Parallel execution levels.
198        levels: Vec<Vec<CellId>>,
199    },
200
201    /// Notebook file changed externally.
202    FileChanged {
203        /// Cells that were modified.
204        modified_cells: Vec<CellId>,
205        /// Cells that were added.
206        added_cells: Vec<CellState>,
207        /// Cells that were removed.
208        removed_cells: Vec<CellId>,
209    },
210
211    /// Sync completed.
212    SyncCompleted {
213        /// Path to generated .ipynb file.
214        ipynb_path: String,
215    },
216
217    /// Execution was aborted by user request.
218    ExecutionAborted {
219        /// The cell that was interrupted (if known).
220        cell_id: Option<CellId>,
221    },
222
223    /// Generic error message.
224    Error {
225        /// Error description.
226        message: String,
227    },
228
229    /// Cell insertion result.
230    CellInserted {
231        /// ID of the newly created cell.
232        cell_id: CellId,
233        /// Error message if insertion failed.
234        error: Option<String>,
235    },
236
237    /// Cell deletion result.
238    CellDeleted {
239        /// ID of the deleted cell.
240        cell_id: CellId,
241        /// Error message if deletion failed.
242        error: Option<String>,
243    },
244
245    /// Cell duplication result.
246    CellDuplicated {
247        /// ID of the original cell.
248        original_cell_id: CellId,
249        /// ID of the new duplicated cell.
250        new_cell_id: CellId,
251        /// Error message if duplication failed.
252        error: Option<String>,
253    },
254
255    /// Cell move result.
256    CellMoved {
257        /// ID of the moved cell.
258        cell_id: CellId,
259        /// Error message if move failed.
260        error: Option<String>,
261    },
262
263    /// History entry selected for a cell.
264    HistorySelected {
265        /// Cell whose history was changed.
266        cell_id: CellId,
267        /// New history index.
268        index: usize,
269        /// Total history count.
270        count: usize,
271        /// The output at this history entry.
272        output: Option<CellOutput>,
273        /// Cells that are now dirty (need re-execution).
274        dirty_cells: Vec<CellId>,
275    },
276
277    /// Undo operation result.
278    UndoResult {
279        /// Whether the undo succeeded.
280        success: bool,
281        /// Error message if undo failed.
282        error: Option<String>,
283        /// Description of what was undone (e.g., "Deleted cell 'foo'").
284        description: Option<String>,
285    },
286
287    /// Redo operation result.
288    RedoResult {
289        /// Whether the redo succeeded.
290        success: bool,
291        /// Error message if redo failed.
292        error: Option<String>,
293        /// Description of what was redone.
294        description: Option<String>,
295    },
296
297    /// Current undo/redo state (sent after each operation).
298    UndoRedoState {
299        /// Whether undo is available.
300        can_undo: bool,
301        /// Whether redo is available.
302        can_redo: bool,
303        /// Description of what will be undone (for UI tooltip).
304        undo_description: Option<String>,
305        /// Description of what will be redone (for UI tooltip).
306        redo_description: Option<String>,
307    },
308
309    /// Kernel restart completed.
310    KernelRestarted {
311        /// Error message if restart failed.
312        error: Option<String>,
313    },
314
315    /// All outputs cleared.
316    OutputsCleared {
317        /// Error message if clear failed.
318        error: Option<String>,
319    },
320
321    /// Cell rename result.
322    CellRenamed {
323        /// ID of the renamed cell.
324        cell_id: CellId,
325        /// New display name.
326        new_display_name: String,
327        /// Error message if rename failed.
328        error: Option<String>,
329    },
330
331    /// Markdown cell insertion result.
332    MarkdownCellInserted {
333        /// ID of the newly created markdown cell.
334        cell_id: CellId,
335        /// Error message if insertion failed.
336        error: Option<String>,
337    },
338
339    /// Markdown cell edit result.
340    MarkdownCellEdited {
341        /// ID of the edited markdown cell.
342        cell_id: CellId,
343        /// Error message if edit failed.
344        error: Option<String>,
345    },
346
347    /// Markdown cell deletion result.
348    MarkdownCellDeleted {
349        /// ID of the deleted markdown cell.
350        cell_id: CellId,
351        /// Error message if deletion failed.
352        error: Option<String>,
353    },
354
355    /// Markdown cell move result.
356    MarkdownCellMoved {
357        /// ID of the moved markdown cell.
358        cell_id: CellId,
359        /// Error message if move failed.
360        error: Option<String>,
361    },
362}
363
364/// State of a single cell (code or markdown).
365#[derive(Debug, Clone, Serialize, Deserialize)]
366#[serde(tag = "cell_type", rename_all = "snake_case")]
367pub enum CellState {
368    /// Code cell (executable function).
369    Code {
370        /// Unique cell identifier.
371        id: CellId,
372        /// Cell name (function name).
373        name: String,
374        /// Human-readable display name.
375        display_name: String,
376        /// Cell source code.
377        source: String,
378        /// Doc comment / description.
379        description: Option<String>,
380        /// Return type.
381        return_type: String,
382        /// Dependencies (parameter names).
383        dependencies: Vec<String>,
384        /// Current execution status.
385        status: CellStatus,
386        /// Last output if available.
387        output: Option<CellOutput>,
388        /// Whether the cell needs re-execution.
389        dirty: bool,
390    },
391    /// Markdown cell (non-executable documentation).
392    Markdown {
393        /// Unique cell identifier.
394        id: CellId,
395        /// Markdown content.
396        content: String,
397    },
398}
399
400impl CellState {
401    /// Get the cell ID.
402    pub fn id(&self) -> CellId {
403        match self {
404            CellState::Code { id, .. } | CellState::Markdown { id, .. } => *id,
405        }
406    }
407
408    /// Get the cell name (only for code cells).
409    pub fn name(&self) -> Option<&str> {
410        match self {
411            CellState::Code { name, .. } => Some(name),
412            CellState::Markdown { .. } => None,
413        }
414    }
415
416    /// Check if cell is dirty (only code cells can be dirty).
417    pub fn is_dirty(&self) -> bool {
418        match self {
419            CellState::Code { dirty, .. } => *dirty,
420            CellState::Markdown { .. } => false,
421        }
422    }
423
424    /// Set dirty flag (only for code cells).
425    pub fn set_dirty(&mut self, value: bool) {
426        if let CellState::Code { dirty, .. } = self {
427            *dirty = value;
428        }
429    }
430
431    /// Get status (only for code cells).
432    pub fn status(&self) -> Option<CellStatus> {
433        match self {
434            CellState::Code { status, .. } => Some(*status),
435            CellState::Markdown { .. } => None,
436        }
437    }
438
439    /// Set status (only for code cells).
440    pub fn set_status(&mut self, new_status: CellStatus) {
441        if let CellState::Code { status, .. } = self {
442            *status = new_status;
443        }
444    }
445
446    /// Set output (only for code cells).
447    pub fn set_output(&mut self, new_output: Option<CellOutput>) {
448        if let CellState::Code { output, .. } = self {
449            *output = new_output;
450        }
451    }
452
453    /// Clear output (only for code cells).
454    pub fn clear_output(&mut self) {
455        if let CellState::Code { output, .. } = self {
456            *output = None;
457        }
458    }
459}
460
461/// Cell execution status.
462#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
463#[serde(rename_all = "snake_case")]
464pub enum CellStatus {
465    /// Cell has not been executed.
466    #[default]
467    Idle,
468    /// Cell is currently compiling.
469    Compiling,
470    /// Cell is currently executing.
471    Running,
472    /// Cell completed successfully.
473    Success,
474    /// Cell failed with an error.
475    Error,
476}
477
478/// Cell output representation.
479#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct CellOutput {
481    /// Plain text representation.
482    pub text: Option<String>,
483    /// HTML representation.
484    pub html: Option<String>,
485    /// Image data (base64 encoded PNG).
486    pub image: Option<String>,
487    /// Structured JSON data.
488    pub json: Option<serde_json::Value>,
489    /// Interactive widgets defined by this cell.
490    #[serde(default, skip_serializing_if = "Vec::is_empty")]
491    pub widgets: Vec<WidgetDef>,
492}
493
494/// Source location for error reporting.
495#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct SourceLocation {
497    /// Line number (1-indexed).
498    pub line: u32,
499    /// Column number (1-indexed).
500    pub column: u32,
501    /// End line (for spans).
502    pub end_line: Option<u32>,
503    /// End column (for spans).
504    pub end_column: Option<u32>,
505}
506
507/// Compiler error information.
508#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct CompileErrorInfo {
510    /// Error message.
511    pub message: String,
512    /// Error code (e.g., "E0308").
513    pub code: Option<String>,
514    /// Source location.
515    pub location: Option<SourceLocation>,
516    /// Rendered error (with colors/formatting removed).
517    pub rendered: Option<String>,
518}
519
520/// Dependency edge in the graph.
521#[derive(Debug, Clone, Serialize, Deserialize)]
522pub struct DependencyEdge {
523    /// Source cell (dependency).
524    pub from: CellId,
525    /// Target cell (dependent).
526    pub to: CellId,
527    /// Parameter name used for this dependency.
528    pub param_name: String,
529}
530
531#[cfg(test)]
532mod tests {
533    use super::*;
534
535    #[test]
536    fn test_client_message_serialization() {
537        let msg = ClientMessage::ExecuteCell {
538            cell_id: CellId::new(1),
539        };
540        let json = serde_json::to_string(&msg).unwrap();
541        assert!(json.contains("execute_cell"));
542
543        let parsed: ClientMessage = serde_json::from_str(&json).unwrap();
544        match parsed {
545            ClientMessage::ExecuteCell { cell_id } => {
546                assert_eq!(cell_id, CellId::new(1));
547            }
548            _ => panic!("Wrong message type"),
549        }
550    }
551
552    #[test]
553    fn test_server_message_serialization() {
554        let msg = ServerMessage::CellStarted {
555            cell_id: CellId::new(42),
556        };
557        let json = serde_json::to_string(&msg).unwrap();
558        assert!(json.contains("cell_started"));
559    }
560
561    #[test]
562    fn test_cell_status_default() {
563        assert_eq!(CellStatus::default(), CellStatus::Idle);
564    }
565}