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}