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_core::widgets::{WidgetDef, WidgetValue};
7use venus_core::graph::{CellId, DefinitionType};
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    /// Insert a new definition cell.
143    InsertDefinitionCell {
144        /// Definition content (source code).
145        content: String,
146        /// Type of definition.
147        definition_type: DefinitionType,
148        /// Cell ID to insert after. None = insert at beginning.
149        after_cell_id: Option<CellId>,
150    },
151
152    /// Edit a definition cell's content.
153    EditDefinitionCell {
154        /// Cell to edit.
155        cell_id: CellId,
156        /// New definition content.
157        new_content: String,
158    },
159
160    /// Delete a definition cell.
161    DeleteDefinitionCell {
162        /// Cell to delete.
163        cell_id: CellId,
164    },
165
166    /// Move a definition cell up or down.
167    MoveDefinitionCell {
168        /// Cell to move.
169        cell_id: CellId,
170        /// Direction to move.
171        direction: MoveDirection,
172    },
173}
174
175/// Messages sent from server to client.
176#[derive(Debug, Clone, Serialize, Deserialize)]
177#[serde(tag = "type", rename_all = "snake_case")]
178pub enum ServerMessage {
179    /// Full notebook state (sent on connection or refresh).
180    NotebookState {
181        /// Path to the notebook file.
182        path: String,
183        /// All cells in the notebook.
184        cells: Vec<CellState>,
185        /// Source order (cell IDs in the order they appear in the .rs file).
186        source_order: Vec<CellId>,
187        /// Execution order (topologically sorted cell IDs for dependency resolution).
188        execution_order: Vec<CellId>,
189        /// Path to the workspace root (directory containing Cargo.toml).
190        workspace_root: Option<String>,
191        /// Path to the Cargo.toml file for LSP configuration.
192        cargo_toml_path: Option<String>,
193    },
194
195    /// Cell execution started.
196    CellStarted {
197        /// Cell that started executing.
198        cell_id: CellId,
199    },
200
201    /// Cell execution completed successfully.
202    CellCompleted {
203        /// Cell that completed.
204        cell_id: CellId,
205        /// Execution time in milliseconds.
206        duration_ms: u64,
207        /// Cell output (serialized).
208        output: Option<CellOutput>,
209    },
210
211    /// Cell marked as dirty (needs re-execution because upstream changed).
212    CellDirty {
213        /// Cell that is now dirty.
214        cell_id: CellId,
215    },
216
217    /// Cell execution failed.
218    CellError {
219        /// Cell that failed.
220        cell_id: CellId,
221        /// Error message.
222        error: String,
223        /// Source location if available.
224        location: Option<SourceLocation>,
225    },
226
227    /// Compilation error (before execution).
228    CompileError {
229        /// Cell with compilation error.
230        cell_id: CellId,
231        /// Compiler errors.
232        errors: Vec<CompileErrorInfo>,
233    },
234
235    /// Dependency graph updated.
236    GraphUpdated {
237        /// New dependency edges.
238        edges: Vec<DependencyEdge>,
239        /// Parallel execution levels.
240        levels: Vec<Vec<CellId>>,
241    },
242
243    /// Notebook file changed externally.
244    FileChanged {
245        /// Cells that were modified.
246        modified_cells: Vec<CellId>,
247        /// Cells that were added.
248        added_cells: Vec<CellState>,
249        /// Cells that were removed.
250        removed_cells: Vec<CellId>,
251    },
252
253    /// Sync completed.
254    SyncCompleted {
255        /// Path to generated .ipynb file.
256        ipynb_path: String,
257    },
258
259    /// Execution was aborted by user request.
260    ExecutionAborted {
261        /// The cell that was interrupted (if known).
262        cell_id: Option<CellId>,
263    },
264
265    /// Generic error message.
266    Error {
267        /// Error description.
268        message: String,
269    },
270
271    /// Cell insertion result.
272    CellInserted {
273        /// ID of the newly created cell.
274        cell_id: CellId,
275        /// Error message if insertion failed.
276        error: Option<String>,
277    },
278
279    /// Cell deletion result.
280    CellDeleted {
281        /// ID of the deleted cell.
282        cell_id: CellId,
283        /// Error message if deletion failed.
284        error: Option<String>,
285    },
286
287    /// Cell duplication result.
288    CellDuplicated {
289        /// ID of the original cell.
290        original_cell_id: CellId,
291        /// ID of the new duplicated cell.
292        new_cell_id: CellId,
293        /// Error message if duplication failed.
294        error: Option<String>,
295    },
296
297    /// Cell move result.
298    CellMoved {
299        /// ID of the moved cell.
300        cell_id: CellId,
301        /// Error message if move failed.
302        error: Option<String>,
303    },
304
305    /// History entry selected for a cell.
306    HistorySelected {
307        /// Cell whose history was changed.
308        cell_id: CellId,
309        /// New history index.
310        index: usize,
311        /// Total history count.
312        count: usize,
313        /// The output at this history entry.
314        output: Option<CellOutput>,
315        /// Cells that are now dirty (need re-execution).
316        dirty_cells: Vec<CellId>,
317    },
318
319    /// Undo operation result.
320    UndoResult {
321        /// Whether the undo succeeded.
322        success: bool,
323        /// Error message if undo failed.
324        error: Option<String>,
325        /// Description of what was undone (e.g., "Deleted cell 'foo'").
326        description: Option<String>,
327    },
328
329    /// Redo operation result.
330    RedoResult {
331        /// Whether the redo succeeded.
332        success: bool,
333        /// Error message if redo failed.
334        error: Option<String>,
335        /// Description of what was redone.
336        description: Option<String>,
337    },
338
339    /// Current undo/redo state (sent after each operation).
340    UndoRedoState {
341        /// Whether undo is available.
342        can_undo: bool,
343        /// Whether redo is available.
344        can_redo: bool,
345        /// Description of what will be undone (for UI tooltip).
346        undo_description: Option<String>,
347        /// Description of what will be redone (for UI tooltip).
348        redo_description: Option<String>,
349    },
350
351    /// Kernel restart completed.
352    KernelRestarted {
353        /// Error message if restart failed.
354        error: Option<String>,
355    },
356
357    /// All outputs cleared.
358    OutputsCleared {
359        /// Error message if clear failed.
360        error: Option<String>,
361    },
362
363    /// Cell rename result.
364    CellRenamed {
365        /// ID of the renamed cell.
366        cell_id: CellId,
367        /// New display name.
368        new_display_name: String,
369        /// Error message if rename failed.
370        error: Option<String>,
371    },
372
373    /// Markdown cell insertion result.
374    MarkdownCellInserted {
375        /// ID of the newly created markdown cell.
376        cell_id: CellId,
377        /// Error message if insertion failed.
378        error: Option<String>,
379    },
380
381    /// Markdown cell edit result.
382    MarkdownCellEdited {
383        /// ID of the edited markdown cell.
384        cell_id: CellId,
385        /// Error message if edit failed.
386        error: Option<String>,
387    },
388
389    /// Markdown cell deletion result.
390    MarkdownCellDeleted {
391        /// ID of the deleted markdown cell.
392        cell_id: CellId,
393        /// Error message if deletion failed.
394        error: Option<String>,
395    },
396
397    /// Markdown cell move result.
398    MarkdownCellMoved {
399        /// ID of the moved markdown cell.
400        cell_id: CellId,
401        /// Error message if move failed.
402        error: Option<String>,
403    },
404
405    /// Definition cell insertion result.
406    DefinitionCellInserted {
407        /// ID of the newly created definition cell.
408        cell_id: CellId,
409        /// Error message if insertion failed.
410        error: Option<String>,
411    },
412
413    /// Definition cell edit result.
414    DefinitionCellEdited {
415        /// ID of the edited definition cell.
416        cell_id: CellId,
417        /// Error message if edit failed.
418        error: Option<String>,
419        /// Cells that are now dirty (need re-execution) due to definition change.
420        #[serde(default, skip_serializing_if = "Vec::is_empty")]
421        dirty_cells: Vec<CellId>,
422    },
423
424    /// Definition cell deletion result.
425    DefinitionCellDeleted {
426        /// ID of the deleted definition cell.
427        cell_id: CellId,
428        /// Error message if deletion failed.
429        error: Option<String>,
430    },
431
432    /// Definition cell move result.
433    DefinitionCellMoved {
434        /// ID of the moved definition cell.
435        cell_id: CellId,
436        /// Error message if move failed.
437        error: Option<String>,
438    },
439}
440
441/// State of a single cell (code, markdown, or definition).
442#[derive(Debug, Clone, Serialize, Deserialize)]
443#[serde(tag = "cell_type", rename_all = "snake_case")]
444#[allow(clippy::large_enum_variant)]
445pub enum CellState {
446    /// Code cell (executable function).
447    Code {
448        /// Unique cell identifier.
449        id: CellId,
450        /// Cell name (function name).
451        name: String,
452        /// Human-readable display name.
453        display_name: String,
454        /// Cell source code.
455        source: String,
456        /// Doc comment / description.
457        description: Option<String>,
458        /// Return type.
459        return_type: String,
460        /// Dependencies (parameter names).
461        dependencies: Vec<String>,
462        /// Current execution status.
463        status: CellStatus,
464        /// Last output if available.
465        output: Option<CellOutput>,
466        /// Whether the cell needs re-execution.
467        dirty: bool,
468    },
469    /// Markdown cell (non-executable documentation).
470    Markdown {
471        /// Unique cell identifier.
472        id: CellId,
473        /// Markdown content.
474        content: String,
475    },
476    /// Definition cell (types, imports, helper functions - compiled into universe).
477    Definition {
478        /// Unique cell identifier.
479        id: CellId,
480        /// Definition content (source code).
481        content: String,
482        /// Type of definition.
483        definition_type: DefinitionType,
484        /// Attached doc comment.
485        doc_comment: Option<String>,
486    },
487}
488
489impl CellState {
490    /// Get the cell ID.
491    pub fn id(&self) -> CellId {
492        match self {
493            CellState::Code { id, .. }
494            | CellState::Markdown { id, .. }
495            | CellState::Definition { id, .. } => *id,
496        }
497    }
498
499    /// Get the cell name (only for code cells).
500    pub fn name(&self) -> Option<&str> {
501        match self {
502            CellState::Code { name, .. } => Some(name),
503            CellState::Markdown { .. } | CellState::Definition { .. } => None,
504        }
505    }
506
507    /// Check if cell is dirty (only code cells can be dirty).
508    pub fn is_dirty(&self) -> bool {
509        match self {
510            CellState::Code { dirty, .. } => *dirty,
511            CellState::Markdown { .. } | CellState::Definition { .. } => false,
512        }
513    }
514
515    /// Set dirty flag (only for code cells).
516    pub fn set_dirty(&mut self, value: bool) {
517        if let CellState::Code { dirty, .. } = self {
518            *dirty = value;
519        }
520    }
521
522    /// Get status (only for code cells).
523    pub fn status(&self) -> Option<CellStatus> {
524        match self {
525            CellState::Code { status, .. } => Some(*status),
526            CellState::Markdown { .. } | CellState::Definition { .. } => None,
527        }
528    }
529
530    /// Set status (only for code cells).
531    pub fn set_status(&mut self, new_status: CellStatus) {
532        if let CellState::Code { status, .. } = self {
533            *status = new_status;
534        }
535    }
536
537    /// Set output (only for code cells).
538    pub fn set_output(&mut self, new_output: Option<CellOutput>) {
539        if let CellState::Code { output, .. } = self {
540            *output = new_output;
541        }
542    }
543
544    /// Clear output (only for code cells).
545    pub fn clear_output(&mut self) {
546        if let CellState::Code { output, .. } = self {
547            *output = None;
548        }
549    }
550}
551
552/// Cell execution status.
553#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
554#[serde(rename_all = "snake_case")]
555pub enum CellStatus {
556    /// Cell has not been executed.
557    #[default]
558    Idle,
559    /// Cell is currently compiling.
560    Compiling,
561    /// Cell is currently executing.
562    Running,
563    /// Cell completed successfully.
564    Success,
565    /// Cell failed with an error.
566    Error,
567}
568
569/// Cell output representation.
570#[derive(Debug, Clone, Serialize, Deserialize)]
571pub struct CellOutput {
572    /// Plain text representation.
573    pub text: Option<String>,
574    /// HTML representation.
575    pub html: Option<String>,
576    /// Image data (base64 encoded PNG).
577    pub image: Option<String>,
578    /// Structured JSON data.
579    pub json: Option<serde_json::Value>,
580    /// Interactive widgets defined by this cell.
581    #[serde(default, skip_serializing_if = "Vec::is_empty")]
582    pub widgets: Vec<WidgetDef>,
583}
584
585/// Source location for error reporting.
586#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct SourceLocation {
588    /// Line number (1-indexed).
589    pub line: u32,
590    /// Column number (1-indexed).
591    pub column: u32,
592    /// End line (for spans).
593    pub end_line: Option<u32>,
594    /// End column (for spans).
595    pub end_column: Option<u32>,
596}
597
598/// Compiler error information.
599#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct CompileErrorInfo {
601    /// Error message.
602    pub message: String,
603    /// Error code (e.g., "E0308").
604    pub code: Option<String>,
605    /// Source location.
606    pub location: Option<SourceLocation>,
607    /// Rendered error (with colors/formatting removed).
608    pub rendered: Option<String>,
609}
610
611/// Dependency edge in the graph.
612#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct DependencyEdge {
614    /// Source cell (dependency).
615    pub from: CellId,
616    /// Target cell (dependent).
617    pub to: CellId,
618    /// Parameter name used for this dependency.
619    pub param_name: String,
620}
621
622#[cfg(test)]
623mod tests {
624    use super::*;
625
626    #[test]
627    fn test_client_message_serialization() {
628        let msg = ClientMessage::ExecuteCell {
629            cell_id: CellId::new(1),
630        };
631        let json = serde_json::to_string(&msg).unwrap();
632        assert!(json.contains("execute_cell"));
633
634        let parsed: ClientMessage = serde_json::from_str(&json).unwrap();
635        match parsed {
636            ClientMessage::ExecuteCell { cell_id } => {
637                assert_eq!(cell_id, CellId::new(1));
638            }
639            _ => panic!("Wrong message type"),
640        }
641    }
642
643    #[test]
644    fn test_server_message_serialization() {
645        let msg = ServerMessage::CellStarted {
646            cell_id: CellId::new(42),
647        };
648        let json = serde_json::to_string(&msg).unwrap();
649        assert!(json.contains("cell_started"));
650    }
651
652    #[test]
653    fn test_cell_status_default() {
654        assert_eq!(CellStatus::default(), CellStatus::Idle);
655    }
656}