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}