1use crate::time;
51
52pub use crate::{
53 media::{Media, MediaType},
54 ExecutionCount,
55};
56
57use bytes::Bytes;
58use chrono::{DateTime, Utc};
59use serde::{Deserialize, Serialize};
60use serde_json::{json, Value};
61use std::{collections::HashMap, fmt};
62use uuid::Uuid;
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(rename_all = "lowercase")]
90pub enum Channel {
91 Shell,
93 Control,
95 Stdin,
97 IOPub,
99 Heartbeat,
101}
102
103#[derive(Serialize, Deserialize, Debug, Clone)]
104struct UnknownJupyterMessage {
105 pub header: Header,
106 pub parent_header: Option<Header>,
107 pub metadata: Value,
108 pub content: Value,
109 #[serde(skip_serializing, skip_deserializing)]
110 pub buffers: Vec<Bytes>,
111}
112
113#[derive(Serialize, Deserialize, Debug, Clone)]
142pub struct Header {
143 pub msg_id: String,
144 pub username: String,
145 pub session: String,
146 pub date: DateTime<Utc>,
147 pub msg_type: String,
148 pub version: String,
149}
150
151fn serialize_parent_header<S>(
159 parent_header: &Option<Header>,
160 serializer: S,
161) -> Result<S::Ok, S::Error>
162where
163 S: serde::Serializer,
164{
165 match parent_header {
166 Some(parent_header) => parent_header.serialize(serializer),
167 None => serde_json::Map::new().serialize(serializer),
168 }
169}
170
171fn deserialize_parent_header<'de, D>(deserializer: D) -> Result<Option<Header>, D::Error>
177where
178 D: serde::Deserializer<'de>,
179{
180 use serde::de::Error;
181 let value = Value::deserialize(deserializer)?;
182 if value.is_null() {
183 Ok(None)
184 } else if let Some(obj) = value.as_object() {
185 if obj.is_empty() {
186 Ok(None)
187 } else {
188 serde_json::from_value(Value::Object(obj.clone()))
190 .map(Some)
191 .map_err(D::Error::custom)
192 }
193 } else {
194 serde_json::from_value(value)
196 .map(Some)
197 .map_err(D::Error::custom)
198 }
199}
200
201#[derive(Deserialize, Serialize, Clone)]
250pub struct JupyterMessage {
251 #[serde(skip_serializing, skip_deserializing)]
252 pub zmq_identities: Vec<Bytes>,
253 pub header: Header,
254 #[serde(
255 serialize_with = "serialize_parent_header",
256 deserialize_with = "deserialize_parent_header"
257 )]
258 pub parent_header: Option<Header>,
259 pub metadata: Value,
260 pub content: JupyterMessageContent,
261 #[serde(skip_serializing, skip_deserializing)]
262 pub buffers: Vec<Bytes>,
263 pub channel: Option<Channel>,
264}
265
266impl JupyterMessage {
267 pub fn new(
268 content: impl Into<JupyterMessageContent>,
269 parent: Option<&JupyterMessage>,
270 ) -> JupyterMessage {
271 let session = match parent {
275 Some(parent) => parent.header.session.clone(),
276 None => Uuid::new_v4().to_string(),
277 };
278
279 let content = content.into();
280
281 let header = Header {
282 msg_id: Uuid::new_v4().to_string(),
283 username: "runtimelib".to_string(),
284 session,
285 date: time::utc_now(),
286 msg_type: content.message_type().to_owned(),
287 version: "5.3".to_string(),
288 };
289
290 JupyterMessage {
291 zmq_identities: parent.map_or(Vec::new(), |parent| parent.zmq_identities.clone()),
292 header,
293 parent_header: parent.map(|parent| parent.header.clone()),
294 metadata: json!({}),
295 content,
296 buffers: Vec::new(),
297 channel: None,
298 }
299 }
300
301 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
302 self.metadata = metadata;
303 self
304 }
305
306 pub fn with_buffers(mut self, buffers: Vec<Bytes>) -> Self {
307 self.buffers = buffers;
308 self
309 }
310
311 pub fn with_parent(mut self, parent: &JupyterMessage) -> Self {
312 self.header.session.clone_from(&parent.header.session);
313 self.parent_header = Some(parent.header.clone());
314 self.zmq_identities.clone_from(&parent.zmq_identities);
315 self
316 }
317
318 pub fn with_zmq_identities(mut self, zmq_identities: Vec<Bytes>) -> Self {
319 self.zmq_identities = zmq_identities;
320 self
321 }
322
323 pub fn with_session(mut self, session: &str) -> Self {
324 self.header.session = session.to_string();
325 self
326 }
327
328 pub fn message_type(&self) -> &str {
329 self.content.message_type()
330 }
331
332 pub fn from_value(message: Value) -> Result<JupyterMessage, anyhow::Error> {
333 let message = serde_json::from_value::<UnknownJupyterMessage>(message)?;
334
335 let content =
336 JupyterMessageContent::from_type_and_content(&message.header.msg_type, message.content);
337
338 let content = match content {
339 Ok(content) => content,
340 Err(err) => {
341 return Err(anyhow::anyhow!(
342 "Error deserializing content for msg_type `{}`: {}",
343 &message.header.msg_type,
344 err
345 ));
346 }
347 };
348
349 let message = JupyterMessage {
350 zmq_identities: Vec::new(),
351 header: message.header,
352 parent_header: message.parent_header,
353 metadata: message.metadata,
354 content,
355 buffers: message.buffers,
356 channel: None,
357 };
358
359 Ok(message)
360 }
361}
362
363impl fmt::Debug for JupyterMessage {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 writeln!(
366 f,
367 "\nHeader: {}",
368 serde_json::to_string_pretty(&self.header).unwrap()
369 )?;
370 writeln!(
371 f,
372 "Parent header: {}",
373 if let Some(parent_header) = self.parent_header.as_ref() {
374 serde_json::to_string_pretty(parent_header).unwrap()
375 } else {
376 serde_json::to_string_pretty(&serde_json::Map::new()).unwrap()
377 }
378 )?;
379 writeln!(
380 f,
381 "Metadata: {}",
382 serde_json::to_string_pretty(&self.metadata).unwrap()
383 )?;
384 writeln!(
385 f,
386 "Content: {}\n",
387 serde_json::to_string_pretty(&self.content).unwrap()
388 )?;
389 Ok(())
390 }
391}
392
393#[derive(Serialize, Deserialize, Debug, Clone)]
394#[serde(untagged)]
395pub enum JupyterMessageContent {
396 ClearOutput(ClearOutput),
397 CommClose(CommClose),
398 CommInfoReply(CommInfoReply),
399 CommInfoRequest(CommInfoRequest),
400 CommMsg(CommMsg),
401 CommOpen(CommOpen),
402 CompleteReply(CompleteReply),
403 CompleteRequest(CompleteRequest),
404 DebugReply(DebugReply),
405 DebugRequest(DebugRequest),
406 DisplayData(DisplayData),
407 ErrorOutput(ErrorOutput),
408 ExecuteInput(ExecuteInput),
409 ExecuteReply(ExecuteReply),
410 ExecuteRequest(ExecuteRequest),
411 ExecuteResult(ExecuteResult),
412 HistoryReply(HistoryReply),
413 HistoryRequest(HistoryRequest),
414 InputReply(InputReply),
415 InputRequest(InputRequest),
416 InspectReply(InspectReply),
417 InspectRequest(InspectRequest),
418 InterruptReply(InterruptReply),
419 InterruptRequest(InterruptRequest),
420 IsCompleteReply(IsCompleteReply),
421 IsCompleteRequest(IsCompleteRequest),
422 KernelInfoReply(Box<KernelInfoReply>),
425 KernelInfoRequest(KernelInfoRequest),
426 ShutdownReply(ShutdownReply),
427 ShutdownRequest(ShutdownRequest),
428 Status(Status),
429 StreamContent(StreamContent),
430 UnknownMessage(UnknownMessage),
431 UpdateDisplayData(UpdateDisplayData),
432}
433
434impl JupyterMessageContent {
435 pub fn message_type(&self) -> &str {
436 match self {
437 JupyterMessageContent::ClearOutput(_) => "clear_output",
438 JupyterMessageContent::CommClose(_) => "comm_close",
439 JupyterMessageContent::CommInfoReply(_) => "comm_info_reply",
440 JupyterMessageContent::CommInfoRequest(_) => "comm_info_request",
441 JupyterMessageContent::CommMsg(_) => "comm_msg",
442 JupyterMessageContent::CommOpen(_) => "comm_open",
443 JupyterMessageContent::CompleteReply(_) => "complete_reply",
444 JupyterMessageContent::CompleteRequest(_) => "complete_request",
445 JupyterMessageContent::DebugReply(_) => "debug_reply",
446 JupyterMessageContent::DebugRequest(_) => "debug_request",
447 JupyterMessageContent::DisplayData(_) => "display_data",
448 JupyterMessageContent::ErrorOutput(_) => "error",
449 JupyterMessageContent::ExecuteInput(_) => "execute_input",
450 JupyterMessageContent::ExecuteReply(_) => "execute_reply",
451 JupyterMessageContent::ExecuteRequest(_) => "execute_request",
452 JupyterMessageContent::ExecuteResult(_) => "execute_result",
453 JupyterMessageContent::HistoryReply(_) => "history_reply",
454 JupyterMessageContent::HistoryRequest(_) => "history_request",
455 JupyterMessageContent::InputReply(_) => "input_reply",
456 JupyterMessageContent::InputRequest(_) => "input_request",
457 JupyterMessageContent::InspectReply(_) => "inspect_reply",
458 JupyterMessageContent::InspectRequest(_) => "inspect_request",
459 JupyterMessageContent::InterruptReply(_) => "interrupt_reply",
460 JupyterMessageContent::InterruptRequest(_) => "interrupt_request",
461 JupyterMessageContent::IsCompleteReply(_) => "is_complete_reply",
462 JupyterMessageContent::IsCompleteRequest(_) => "is_complete_request",
463 JupyterMessageContent::KernelInfoReply(_) => "kernel_info_reply",
464 JupyterMessageContent::KernelInfoRequest(_) => "kernel_info_request",
465 JupyterMessageContent::ShutdownReply(_) => "shutdown_reply",
466 JupyterMessageContent::ShutdownRequest(_) => "shutdown_request",
467 JupyterMessageContent::Status(_) => "status",
468 JupyterMessageContent::StreamContent(_) => "stream",
469 JupyterMessageContent::UnknownMessage(unk) => unk.msg_type.as_str(),
470 JupyterMessageContent::UpdateDisplayData(_) => "update_display_data",
471 }
472 }
473
474 pub fn from_type_and_content(msg_type: &str, content: Value) -> serde_json::Result<Self> {
475 match msg_type {
476 "clear_output" => Ok(JupyterMessageContent::ClearOutput(serde_json::from_value(
477 content,
478 )?)),
479
480 "comm_close" => Ok(JupyterMessageContent::CommClose(serde_json::from_value(
481 content,
482 )?)),
483
484 "comm_info_reply" => Ok(JupyterMessageContent::CommInfoReply(
485 serde_json::from_value(content)?,
486 )),
487 "comm_info_request" => Ok(JupyterMessageContent::CommInfoRequest(
488 serde_json::from_value(content)?,
489 )),
490
491 "comm_msg" => Ok(JupyterMessageContent::CommMsg(serde_json::from_value(
492 content,
493 )?)),
494 "comm_open" => Ok(JupyterMessageContent::CommOpen(serde_json::from_value(
495 content,
496 )?)),
497
498 "complete_reply" => Ok(JupyterMessageContent::CompleteReply(
499 serde_json::from_value(content)?,
500 )),
501 "complete_request" => Ok(JupyterMessageContent::CompleteRequest(
502 serde_json::from_value(content)?,
503 )),
504
505 "debug_reply" => Ok(JupyterMessageContent::DebugReply(serde_json::from_value(
506 content,
507 )?)),
508 "debug_request" => Ok(JupyterMessageContent::DebugRequest(serde_json::from_value(
509 content,
510 )?)),
511
512 "display_data" => Ok(JupyterMessageContent::DisplayData(serde_json::from_value(
513 content,
514 )?)),
515
516 "error" => Ok(JupyterMessageContent::ErrorOutput(serde_json::from_value(
517 content,
518 )?)),
519
520 "execute_input" => Ok(JupyterMessageContent::ExecuteInput(serde_json::from_value(
521 content,
522 )?)),
523
524 "execute_reply" => Ok(JupyterMessageContent::ExecuteReply(serde_json::from_value(
525 content,
526 )?)),
527 "execute_request" => Ok(JupyterMessageContent::ExecuteRequest(
528 serde_json::from_value(content)?,
529 )),
530
531 "execute_result" => Ok(JupyterMessageContent::ExecuteResult(
532 serde_json::from_value(content)?,
533 )),
534
535 "history_reply" => Ok(JupyterMessageContent::HistoryReply(serde_json::from_value(
536 content,
537 )?)),
538 "history_request" => Ok(JupyterMessageContent::HistoryRequest(
539 serde_json::from_value(content)?,
540 )),
541
542 "input_reply" => Ok(JupyterMessageContent::InputReply(serde_json::from_value(
543 content,
544 )?)),
545 "input_request" => Ok(JupyterMessageContent::InputRequest(serde_json::from_value(
546 content,
547 )?)),
548
549 "inspect_reply" => Ok(JupyterMessageContent::InspectReply(serde_json::from_value(
550 content,
551 )?)),
552 "inspect_request" => Ok(JupyterMessageContent::InspectRequest(
553 serde_json::from_value(content)?,
554 )),
555
556 "interrupt_reply" => Ok(JupyterMessageContent::InterruptReply(
557 serde_json::from_value(content)?,
558 )),
559 "interrupt_request" => Ok(JupyterMessageContent::InterruptRequest(
560 serde_json::from_value(content)?,
561 )),
562
563 "is_complete_reply" => Ok(JupyterMessageContent::IsCompleteReply(
564 serde_json::from_value(content)?,
565 )),
566 "is_complete_request" => Ok(JupyterMessageContent::IsCompleteRequest(
567 serde_json::from_value(content)?,
568 )),
569
570 "kernel_info_reply" => Ok(JupyterMessageContent::KernelInfoReply(
571 serde_json::from_value(content)?,
572 )),
573 "kernel_info_request" => Ok(JupyterMessageContent::KernelInfoRequest(
574 serde_json::from_value(content)?,
575 )),
576
577 "shutdown_reply" => Ok(JupyterMessageContent::ShutdownReply(
578 serde_json::from_value(content)?,
579 )),
580 "shutdown_request" => Ok(JupyterMessageContent::ShutdownRequest(
581 serde_json::from_value(content)?,
582 )),
583
584 "status" => Ok(JupyterMessageContent::Status(serde_json::from_value(
585 content,
586 )?)),
587
588 "stream" => Ok(JupyterMessageContent::StreamContent(
589 serde_json::from_value(content)?,
590 )),
591
592 "update_display_data" => Ok(JupyterMessageContent::UpdateDisplayData(
593 serde_json::from_value(content)?,
594 )),
595
596 _ => Ok(JupyterMessageContent::UnknownMessage(UnknownMessage {
597 msg_type: msg_type.to_string(),
598 content,
599 })),
600 }
601 }
602}
603
604macro_rules! impl_message_traits {
605 ($($name:ident),*) => {
606 $(
607 impl $name {
608 #[doc = concat!("Create a new `JupyterMessage`, assigning the parent for a `", stringify!($name), "` message.\n")]
609 #[doc = concat!("use jupyter_protocol::", stringify!($name), ";\n")]
617 #[doc = concat!("let child_message = ", stringify!($name), "{\n")]
624 #[must_use]
631 pub fn as_child_of(&self, parent: &JupyterMessage) -> JupyterMessage {
632 JupyterMessage::new(self.clone(), Some(parent))
633 }
634 }
635
636 impl From<$name> for JupyterMessage {
637 #[doc(hidden)]
638 #[doc = concat!("Create a new `JupyterMessage` for a `", stringify!($name), "`.\n\n")]
639 #[must_use]
642 fn from(content: $name) -> Self {
643 JupyterMessage::new(content, None)
644 }
645 }
646
647 impl From<$name> for JupyterMessageContent {
648 #[doc = concat!("Create a new `JupyterMessageContent` for a `", stringify!($name), "`.\n\n")]
649 #[must_use]
650 fn from(content: $name) -> Self {
651 JupyterMessageContent::$name(content)
652 }
653 }
654 )*
655 };
656}
657
658impl From<JupyterMessageContent> for JupyterMessage {
659 fn from(content: JupyterMessageContent) -> Self {
660 JupyterMessage::new(content, None)
661 }
662}
663
664impl_message_traits!(
665 ClearOutput,
666 CommClose,
667 CommInfoReply,
668 CommInfoRequest,
669 CommMsg,
670 CommOpen,
671 CompleteReply,
672 CompleteRequest,
673 DebugReply,
674 DebugRequest,
675 DisplayData,
676 ErrorOutput,
677 ExecuteInput,
678 ExecuteReply,
679 ExecuteRequest,
680 ExecuteResult,
681 HistoryReply,
682 InputReply,
684 InputRequest,
685 InspectReply,
686 InspectRequest,
687 InterruptReply,
688 InterruptRequest,
689 IsCompleteReply,
690 IsCompleteRequest,
691 KernelInfoRequest,
693 ShutdownReply,
694 ShutdownRequest,
695 Status,
696 StreamContent,
697 UpdateDisplayData,
698 UnknownMessage
699);
700
701impl KernelInfoReply {
703 pub fn as_child_of(&self, parent: &JupyterMessage) -> JupyterMessage {
704 JupyterMessage::new(
705 JupyterMessageContent::KernelInfoReply(Box::new(self.clone())),
706 Some(parent),
707 )
708 }
709}
710
711impl From<KernelInfoReply> for JupyterMessage {
712 fn from(content: KernelInfoReply) -> Self {
713 JupyterMessage::new(
714 JupyterMessageContent::KernelInfoReply(Box::new(content)),
715 None,
716 )
717 }
718}
719
720impl From<KernelInfoReply> for JupyterMessageContent {
721 fn from(content: KernelInfoReply) -> Self {
722 JupyterMessageContent::KernelInfoReply(Box::new(content))
723 }
724}
725
726impl HistoryRequest {
727 #[must_use]
753 pub fn as_child_of(&self, parent: &JupyterMessage) -> JupyterMessage {
754 JupyterMessage::new(self.clone(), Some(parent))
755 }
756}
757
758impl From<HistoryRequest> for JupyterMessage {
759 #[doc(hidden)]
760 #[must_use]
764 fn from(content: HistoryRequest) -> Self {
765 JupyterMessage::new(content, None)
766 }
767}
768
769impl From<HistoryRequest> for JupyterMessageContent {
770 #[must_use]
772 fn from(content: HistoryRequest) -> Self {
773 JupyterMessageContent::HistoryRequest(content)
774 }
775}
776
777#[derive(Serialize, Deserialize, Debug, Clone)]
792pub struct UnknownMessage {
793 #[serde(skip_serializing, skip_deserializing)]
794 pub msg_type: String,
795 #[serde(flatten)]
796 pub content: Value,
797}
798impl Default for UnknownMessage {
799 fn default() -> Self {
800 Self {
801 msg_type: "unknown".to_string(),
802 content: Value::Null,
803 }
804 }
805}
806
807impl UnknownMessage {
808 pub fn reply(&self, content: serde_json::Value) -> JupyterMessageContent {
812 JupyterMessageContent::UnknownMessage(UnknownMessage {
813 msg_type: self.msg_type.replace("_request", "_reply"),
814 content,
815 })
816 }
817}
818
819#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
821#[serde(rename_all = "lowercase")]
822pub enum ReplyStatus {
823 #[default]
824 Ok,
825 Error,
826 Aborted,
827}
828
829#[derive(Serialize, Deserialize, Debug, Clone, Default)]
830pub struct ReplyError {
831 pub ename: String,
832 pub evalue: String,
833 pub traceback: Vec<String>,
834}
835
836#[derive(Serialize, Deserialize, Debug, Clone, Default)]
838pub struct ClearOutput {
839 pub wait: bool,
843}
844
845#[derive(Serialize, Deserialize, Debug, Clone)]
849pub struct ExecuteRequest {
850 pub code: String,
851 pub silent: bool,
852 pub store_history: bool,
853 #[serde(serialize_with = "serialize_user_expressions")]
854 pub user_expressions: Option<HashMap<String, String>>,
855 #[serde(default = "default_allow_stdin")]
856 pub allow_stdin: bool,
857 #[serde(default = "default_stop_on_error")]
858 pub stop_on_error: bool,
859}
860
861fn serialize_user_expressions<S>(
865 user_expressions: &Option<HashMap<String, String>>,
866 serializer: S,
867) -> Result<S::Ok, S::Error>
868where
869 S: serde::Serializer,
870{
871 match user_expressions {
872 Some(user_expressions) => user_expressions.serialize(serializer),
873 None => serde_json::Map::new().serialize(serializer),
874 }
875}
876
877fn default_allow_stdin() -> bool {
878 false
879}
880
881fn default_stop_on_error() -> bool {
882 true
883}
884
885impl ExecuteRequest {
886 pub fn new(code: String) -> Self {
887 Self {
888 code,
889 ..Default::default()
890 }
891 }
892}
893
894impl Default for ExecuteRequest {
895 fn default() -> Self {
896 Self {
897 code: "".to_string(),
898 silent: false,
899 store_history: true,
900 user_expressions: None,
901 allow_stdin: false,
902 stop_on_error: true,
903 }
904 }
905}
906
907#[derive(Serialize, Deserialize, Debug, Clone)]
915pub struct ExecuteReply {
916 pub status: ReplyStatus,
917 pub execution_count: ExecutionCount,
918
919 #[serde(default)]
920 pub payload: Vec<Payload>,
921 pub user_expressions: Option<HashMap<String, String>>,
922
923 #[serde(flatten, skip_serializing_if = "Option::is_none")]
924 pub error: Option<Box<ReplyError>>,
925}
926impl Default for ExecuteReply {
927 fn default() -> Self {
928 Self {
929 status: ReplyStatus::Ok,
930 execution_count: ExecutionCount::new(0),
931 payload: Vec::new(),
932 user_expressions: None,
933 error: None,
934 }
935 }
936}
937
938#[derive(Serialize, Deserialize, Debug, Clone)]
943#[serde(rename_all = "snake_case")]
944#[serde(tag = "source")]
945pub enum Payload {
946 Page {
947 data: Media,
948 start: usize,
949 },
950 SetNextInput {
951 text: String,
952 replace: bool,
953 },
954 EditMagic {
955 filename: String,
956 line_number: usize,
957 },
958 AskExit {
959 keepkernel: bool,
961 },
962}
963
964#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
968pub struct KernelInfoRequest {}
969
970#[derive(Serialize, Deserialize, Debug, Clone)]
974pub struct KernelInfoReply {
975 pub status: ReplyStatus,
976 pub protocol_version: String,
977 pub implementation: String,
978 pub implementation_version: String,
979 pub language_info: LanguageInfo,
980 pub banner: String,
981 pub help_links: Vec<HelpLink>,
982 #[serde(default = "default_debugger")]
983 pub debugger: bool,
984 #[serde(flatten, skip_serializing_if = "Option::is_none")]
985 pub error: Option<Box<ReplyError>>,
986}
987
988fn default_debugger() -> bool {
989 false
990}
991
992#[derive(Serialize, Deserialize, Debug, Clone)]
993#[serde(untagged)]
994pub enum CodeMirrorMode {
995 Simple(String),
996 CustomMode { name: String, version: usize },
997}
998
999#[derive(Serialize, Deserialize, Debug, Clone)]
1000pub struct CodeMirrorModeObject {
1001 pub name: String,
1002 pub version: usize,
1003}
1004
1005impl CodeMirrorMode {
1006 pub fn typescript() -> Self {
1007 Self::Simple("typescript".to_string())
1008 }
1009
1010 pub fn python() -> Self {
1011 Self::Simple("python".to_string())
1012 }
1013
1014 pub fn ipython_code_mirror_mode() -> Self {
1015 Self::CustomMode {
1016 name: "ipython".to_string(),
1017 version: 3,
1018 }
1019 }
1020}
1021
1022#[derive(Serialize, Deserialize, Debug, Clone)]
1023pub struct LanguageInfo {
1024 pub name: String,
1025 pub version: String,
1026 pub mimetype: String,
1027 pub file_extension: String,
1028 pub pygments_lexer: String,
1029 pub codemirror_mode: CodeMirrorMode,
1030 pub nbconvert_exporter: String,
1031}
1032
1033#[derive(Serialize, Deserialize, Debug, Clone)]
1034pub struct HelpLink {
1035 pub text: String,
1036 pub url: String,
1037}
1038
1039#[derive(Serialize, Deserialize, Debug, Clone)]
1040pub enum Stdio {
1041 #[serde(rename = "stdout")]
1042 Stdout,
1043 #[serde(rename = "stderr")]
1044 Stderr,
1045}
1046
1047#[derive(Serialize, Deserialize, Debug, Clone)]
1089pub struct StreamContent {
1090 pub name: Stdio,
1091 pub text: String,
1092}
1093impl Default for StreamContent {
1094 fn default() -> Self {
1095 Self {
1096 name: Stdio::Stdout,
1097 text: String::new(),
1098 }
1099 }
1100}
1101
1102impl StreamContent {
1103 pub fn stdout(text: &str) -> Self {
1104 Self {
1105 name: Stdio::Stdout,
1106 text: text.to_string(),
1107 }
1108 }
1109
1110 pub fn stderr(text: &str) -> Self {
1111 Self {
1112 name: Stdio::Stderr,
1113 text: text.to_string(),
1114 }
1115 }
1116}
1117
1118#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1120pub struct Transient {
1121 #[serde(skip_serializing_if = "Option::is_none")]
1122 pub display_id: Option<String>,
1123}
1124
1125#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1165pub struct DisplayData {
1166 pub data: Media,
1167 pub metadata: serde_json::Map<String, Value>,
1168 #[serde(default, skip_serializing_if = "Option::is_none")]
1169 pub transient: Option<Transient>,
1170}
1171
1172impl DisplayData {
1173 pub fn new(data: Media) -> Self {
1174 Self {
1175 data,
1176 metadata: Default::default(),
1177 transient: Default::default(),
1178 }
1179 }
1180}
1181
1182impl From<Vec<MediaType>> for DisplayData {
1183 fn from(content: Vec<MediaType>) -> Self {
1184 Self::new(Media::new(content))
1185 }
1186}
1187
1188impl From<MediaType> for DisplayData {
1189 fn from(content: MediaType) -> Self {
1190 Self::new(Media::new(vec![content]))
1191 }
1192}
1193
1194#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1197pub struct UpdateDisplayData {
1198 pub data: Media,
1199 pub metadata: serde_json::Map<String, Value>,
1200 pub transient: Transient,
1201}
1202
1203impl UpdateDisplayData {
1204 pub fn new(data: Media, display_id: &str) -> Self {
1205 Self {
1206 data,
1207 metadata: Default::default(),
1208 transient: Transient {
1209 display_id: Some(display_id.to_string()),
1210 },
1211 }
1212 }
1213}
1214
1215#[derive(Serialize, Deserialize, Debug, Clone)]
1221pub struct ExecuteInput {
1222 pub code: String,
1223 pub execution_count: ExecutionCount,
1224}
1225impl Default for ExecuteInput {
1226 fn default() -> Self {
1227 Self {
1228 code: String::new(),
1229 execution_count: ExecutionCount::new(0),
1230 }
1231 }
1232}
1233
1234#[derive(Serialize, Deserialize, Debug, Clone)]
1258pub struct ExecuteResult {
1259 pub execution_count: ExecutionCount,
1260 pub data: Media,
1261 pub metadata: serde_json::Map<String, Value>,
1262 pub transient: Option<Transient>,
1263}
1264impl Default for ExecuteResult {
1265 fn default() -> Self {
1266 Self {
1267 execution_count: ExecutionCount::new(0),
1268 data: Media::default(),
1269 metadata: serde_json::Map::new(),
1270 transient: None,
1271 }
1272 }
1273}
1274
1275impl ExecuteResult {
1276 pub fn new(execution_count: ExecutionCount, data: Media) -> Self {
1277 Self {
1278 execution_count,
1279 data,
1280 metadata: Default::default(),
1281 transient: None,
1282 }
1283 }
1284}
1285
1286impl From<(ExecutionCount, Vec<MediaType>)> for ExecuteResult {
1287 fn from((execution_count, content): (ExecutionCount, Vec<MediaType>)) -> Self {
1288 Self::new(execution_count, content.into())
1289 }
1290}
1291
1292impl From<(ExecutionCount, MediaType)> for ExecuteResult {
1293 fn from((execution_count, content): (ExecutionCount, MediaType)) -> Self {
1294 Self::new(execution_count, content.into())
1295 }
1296}
1297
1298#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1304pub struct ErrorOutput {
1305 pub ename: String,
1306 pub evalue: String,
1307 pub traceback: Vec<String>,
1308}
1309
1310#[derive(Serialize, Deserialize, Debug, Clone)]
1338pub struct CommOpen {
1339 pub comm_id: CommId,
1340 pub target_name: String,
1341 pub data: serde_json::Map<String, Value>,
1342}
1343impl Default for CommOpen {
1344 fn default() -> Self {
1345 Self {
1346 comm_id: CommId("".to_string()),
1347 target_name: String::new(),
1348 data: serde_json::Map::new(),
1349 }
1350 }
1351}
1352
1353#[derive(Serialize, Deserialize, Debug, Clone)]
1372pub struct CommMsg {
1373 pub comm_id: CommId,
1374 pub data: serde_json::Map<String, Value>,
1375}
1376impl Default for CommMsg {
1377 fn default() -> Self {
1378 Self {
1379 comm_id: CommId("".to_string()),
1380 data: serde_json::Map::new(),
1381 }
1382 }
1383}
1384
1385#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1386pub struct CommInfoRequest {
1387 pub target_name: String,
1388}
1389
1390#[derive(Eq, Hash, PartialEq, Serialize, Deserialize, Debug, Clone)]
1391pub struct CommId(pub String);
1392
1393impl From<CommId> for String {
1394 fn from(comm_id: CommId) -> Self {
1395 comm_id.0
1396 }
1397}
1398
1399impl From<String> for CommId {
1400 fn from(comm_id: String) -> Self {
1401 Self(comm_id)
1402 }
1403}
1404
1405#[derive(Serialize, Deserialize, Debug, Clone)]
1406pub struct CommInfo {
1407 pub target_name: String,
1408}
1409
1410#[derive(Serialize, Deserialize, Debug, Clone)]
1411pub struct CommInfoReply {
1412 pub status: ReplyStatus,
1413 pub comms: HashMap<CommId, CommInfo>,
1414 #[serde(flatten, skip_serializing_if = "Option::is_none")]
1416 pub error: Option<Box<ReplyError>>,
1417}
1418impl Default for CommInfoReply {
1419 fn default() -> Self {
1420 Self {
1421 status: ReplyStatus::Ok,
1422 comms: HashMap::new(),
1423 error: None,
1424 }
1425 }
1426}
1427
1428#[derive(Serialize, Deserialize, Debug, Clone)]
1433pub struct CommClose {
1434 pub comm_id: CommId,
1435 pub data: serde_json::Map<String, Value>,
1436}
1437impl Default for CommClose {
1438 fn default() -> Self {
1439 Self {
1440 comm_id: CommId("".to_string()),
1441 data: serde_json::Map::new(),
1442 }
1443 }
1444}
1445
1446#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1447pub struct ShutdownRequest {
1454 pub restart: bool,
1455}
1456
1457#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1458pub struct InterruptRequest {}
1466
1467#[derive(Serialize, Deserialize, Debug, Clone)]
1468pub struct InterruptReply {
1475 pub status: ReplyStatus,
1476
1477 #[serde(flatten, skip_serializing_if = "Option::is_none")]
1478 pub error: Option<Box<ReplyError>>,
1479}
1480
1481impl Default for InterruptReply {
1482 fn default() -> Self {
1483 Self::new()
1484 }
1485}
1486
1487impl InterruptReply {
1488 pub fn new() -> Self {
1489 Self {
1490 status: ReplyStatus::Ok,
1491 error: None,
1492 }
1493 }
1494}
1495
1496#[derive(Serialize, Deserialize, Debug, Clone)]
1497pub struct ShutdownReply {
1504 pub restart: bool,
1505 pub status: ReplyStatus,
1506
1507 #[serde(flatten, skip_serializing_if = "Option::is_none")]
1508 pub error: Option<Box<ReplyError>>,
1509}
1510impl Default for ShutdownReply {
1511 fn default() -> Self {
1512 Self {
1513 restart: false,
1514 status: ReplyStatus::Ok,
1515 error: None,
1516 }
1517 }
1518}
1519
1520#[derive(Serialize, Deserialize, Debug, Clone)]
1521pub struct InputRequest {
1528 pub prompt: String,
1529 pub password: bool,
1530}
1531impl Default for InputRequest {
1532 fn default() -> Self {
1533 Self {
1534 prompt: "> ".to_string(),
1535 password: false,
1536 }
1537 }
1538}
1539
1540#[derive(Serialize, Deserialize, Debug, Clone)]
1541pub struct InputReply {
1548 pub value: String,
1549
1550 pub status: ReplyStatus,
1551 #[serde(flatten, skip_serializing_if = "Option::is_none")]
1552 pub error: Option<Box<ReplyError>>,
1553}
1554impl Default for InputReply {
1555 fn default() -> Self {
1556 Self {
1557 value: String::new(),
1558 status: ReplyStatus::Ok,
1559 error: None,
1560 }
1561 }
1562}
1563
1564#[derive(Serialize, Deserialize, Debug, Clone)]
1570pub struct InspectRequest {
1571 pub code: String,
1574 pub cursor_pos: usize,
1576 pub detail_level: Option<usize>,
1581}
1582impl Default for InspectRequest {
1583 fn default() -> Self {
1584 Self {
1585 code: String::new(),
1586 cursor_pos: 0,
1587 detail_level: Some(0),
1588 }
1589 }
1590}
1591
1592#[derive(Serialize, Deserialize, Debug, Clone)]
1593pub struct InspectReply {
1594 pub found: bool,
1595 pub data: Media,
1596 pub metadata: serde_json::Map<String, Value>,
1597
1598 pub status: ReplyStatus,
1599 #[serde(flatten, skip_serializing_if = "Option::is_none")]
1600 pub error: Option<Box<ReplyError>>,
1601}
1602impl Default for InspectReply {
1603 fn default() -> Self {
1604 Self {
1605 found: false,
1606 data: Media::default(),
1607 metadata: serde_json::Map::new(),
1608 status: ReplyStatus::Ok,
1609 error: None,
1610 }
1611 }
1612}
1613
1614#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1618pub struct CompleteRequest {
1619 pub code: String,
1620 pub cursor_pos: usize,
1621}
1622
1623#[derive(Serialize, Deserialize, Debug, Clone)]
1627pub struct CompleteReply {
1628 pub matches: Vec<String>,
1629 pub cursor_start: usize,
1630 pub cursor_end: usize,
1631 pub metadata: serde_json::Map<String, Value>,
1632
1633 pub status: ReplyStatus,
1634 #[serde(flatten, skip_serializing_if = "Option::is_none")]
1635 pub error: Option<Box<ReplyError>>,
1636}
1637impl Default for CompleteReply {
1638 fn default() -> Self {
1639 Self {
1640 matches: Vec::new(),
1641 cursor_start: 0,
1642 cursor_end: 0,
1643 metadata: serde_json::Map::new(),
1644 status: ReplyStatus::Ok,
1645 error: None,
1646 }
1647 }
1648}
1649
1650#[derive(Serialize, Deserialize, Debug, Clone)]
1651pub struct DebugRequest {
1652 #[serde(flatten)]
1653 pub content: Value,
1654}
1655impl Default for DebugRequest {
1656 fn default() -> Self {
1657 Self {
1658 content: Value::Null,
1659 }
1660 }
1661}
1662
1663#[derive(Serialize, Deserialize, Debug, Clone)]
1664pub struct DebugReply {
1665 #[serde(flatten)]
1666 pub content: Value,
1667}
1668impl Default for DebugReply {
1669 fn default() -> Self {
1670 Self {
1671 content: Value::Null,
1672 }
1673 }
1674}
1675
1676#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1677#[serde(rename_all = "snake_case")]
1678pub enum IsCompleteReplyStatus {
1679 Incomplete,
1682 Complete,
1684 Invalid,
1686 Unknown,
1691}
1692
1693#[derive(Serialize, Deserialize, Debug, Clone)]
1694pub struct IsCompleteReply {
1695 pub status: IsCompleteReplyStatus,
1698 pub indent: String,
1703}
1704impl Default for IsCompleteReply {
1705 fn default() -> Self {
1706 Self {
1707 status: IsCompleteReplyStatus::Unknown,
1708 indent: String::new(),
1709 }
1710 }
1711}
1712
1713impl IsCompleteReply {
1714 pub fn new(status: IsCompleteReplyStatus, indent: String) -> Self {
1715 Self { status, indent }
1716 }
1717
1718 pub fn incomplete(indent: String) -> Self {
1719 Self::new(IsCompleteReplyStatus::Incomplete, indent)
1720 }
1721
1722 pub fn complete() -> Self {
1723 Self::new(IsCompleteReplyStatus::Complete, String::new())
1724 }
1725
1726 pub fn invalid() -> Self {
1727 Self::new(IsCompleteReplyStatus::Invalid, String::new())
1728 }
1729
1730 pub fn unknown() -> Self {
1731 Self::new(IsCompleteReplyStatus::Unknown, String::new())
1732 }
1733}
1734
1735#[derive(Serialize, Deserialize, Debug, Clone)]
1736#[serde(tag = "hist_access_type")]
1737pub enum HistoryRequest {
1738 #[serde(rename = "range")]
1739 Range {
1740 session: Option<i32>,
1741 start: i32,
1742 stop: i32,
1743 output: bool,
1744 raw: bool,
1745 },
1746 #[serde(rename = "tail")]
1747 Tail { n: i32, output: bool, raw: bool },
1748 #[serde(rename = "search")]
1749 Search {
1750 pattern: String,
1751 unique: bool,
1752 output: bool,
1753 raw: bool,
1754 },
1755}
1756impl Default for HistoryRequest {
1757 fn default() -> Self {
1758 Self::Range {
1759 session: None,
1760 start: 0,
1761 stop: 0,
1762 output: false,
1763 raw: false,
1764 }
1765 }
1766}
1767
1768#[derive(Serialize, Deserialize, Debug, Clone)]
1769#[serde(untagged)]
1770pub enum HistoryEntry {
1771 Input(usize, usize, String),
1774 InputOutput(usize, usize, (String, String)),
1777}
1778
1779#[derive(Serialize, Deserialize, Debug, Clone)]
1783pub struct HistoryReply {
1784 pub history: Vec<HistoryEntry>,
1785
1786 pub status: ReplyStatus,
1787 #[serde(flatten, skip_serializing_if = "Option::is_none")]
1788 pub error: Option<Box<ReplyError>>,
1789}
1790impl Default for HistoryReply {
1791 fn default() -> Self {
1792 Self {
1793 history: Vec::new(),
1794 status: ReplyStatus::Ok,
1795 error: None,
1796 }
1797 }
1798}
1799
1800impl HistoryReply {
1801 pub fn new(history: Vec<HistoryEntry>) -> Self {
1802 Self {
1803 history,
1804 status: ReplyStatus::Ok,
1805 error: None,
1806 }
1807 }
1808}
1809
1810#[derive(Serialize, Deserialize, Debug, Clone, Default)]
1814pub struct IsCompleteRequest {
1815 pub code: String,
1816}
1817
1818#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1819#[serde(rename_all = "lowercase")]
1820pub enum ExecutionState {
1821 Busy,
1822 Idle,
1823}
1824
1825impl ExecutionState {
1826 pub fn as_str(&self) -> &str {
1827 match self {
1828 ExecutionState::Busy => "busy",
1829 ExecutionState::Idle => "idle",
1830 }
1831 }
1832}
1833
1834#[derive(Serialize, Deserialize, Debug, Clone)]
1838pub struct Status {
1839 pub execution_state: ExecutionState,
1840}
1841impl Default for Status {
1842 fn default() -> Self {
1843 Self {
1844 execution_state: ExecutionState::Idle,
1845 }
1846 }
1847}
1848
1849impl Status {
1850 pub fn busy() -> Self {
1851 Self {
1852 execution_state: ExecutionState::Busy,
1853 }
1854 }
1855
1856 pub fn idle() -> Self {
1857 Self {
1858 execution_state: ExecutionState::Idle,
1859 }
1860 }
1861}
1862
1863#[cfg(test)]
1864mod test {
1865 use serde_json::json;
1866
1867 use super::*;
1868
1869 #[test]
1870 fn test_execute_request_serialize() {
1871 let request = ExecuteRequest {
1872 code: "print('Hello, World!')".to_string(),
1873 silent: false,
1874 store_history: true,
1875 user_expressions: Some(HashMap::new()),
1876 allow_stdin: false,
1877 stop_on_error: true,
1878 };
1879 let request_value = serde_json::to_value(request).unwrap();
1880
1881 let expected_request_value = serde_json::json!({
1882 "code": "print('Hello, World!')",
1883 "silent": false,
1884 "store_history": true,
1885 "user_expressions": {},
1886 "allow_stdin": false,
1887 "stop_on_error": true
1888 });
1889
1890 assert_eq!(request_value, expected_request_value);
1891 }
1892
1893 #[test]
1894 fn test_execute_request_user_expressions_serializes_to_empty_dict() {
1895 let request = ExecuteRequest {
1896 code: "print('Hello, World!')".to_string(),
1897 silent: false,
1898 store_history: true,
1899 user_expressions: None,
1900 allow_stdin: false,
1901 stop_on_error: true,
1902 };
1903 let request_value = serde_json::to_value(request).unwrap();
1904
1905 let expected_request_value = serde_json::json!({
1906 "code": "print('Hello, World!')",
1907 "silent": false,
1908 "store_history": true,
1909 "user_expressions": {},
1910 "allow_stdin": false,
1911 "stop_on_error": true
1912 });
1913
1914 assert_eq!(request_value, expected_request_value);
1915 }
1916
1917 #[test]
1918 fn test_into_various() {
1919 let kernel_info_request = KernelInfoRequest {};
1920 let content: JupyterMessageContent = kernel_info_request.clone().into();
1921 let message: JupyterMessage = content.into();
1922 assert!(message.parent_header.is_none());
1923 match message.content {
1924 JupyterMessageContent::KernelInfoRequest(req) => {
1925 assert_eq!(req, kernel_info_request);
1926 }
1927 _ => panic!("Expected KernelInfoRequest"),
1928 }
1929
1930 let kernel_info_request = KernelInfoRequest {};
1931 let message: JupyterMessage = kernel_info_request.clone().into();
1932 assert!(message.parent_header.is_none());
1933 match message.content {
1934 JupyterMessageContent::KernelInfoRequest(req) => {
1935 assert_eq!(req, kernel_info_request);
1936 }
1937 _ => panic!("Expected KernelInfoRequest"),
1938 }
1939 }
1940
1941 #[test]
1942 fn test_default() {
1943 let msg: JupyterMessage = ExecuteRequest {
1944 code: "import this".to_string(),
1945 ..Default::default()
1946 }
1947 .into();
1948
1949 assert_eq!(msg.header.msg_type, "execute_request");
1950 assert_eq!(msg.header.msg_id.len(), 36);
1951
1952 match msg.content {
1953 JupyterMessageContent::ExecuteRequest(req) => {
1954 assert_eq!(req.code, "import this");
1955 assert!(!req.silent);
1956 assert!(req.store_history);
1957 assert_eq!(req.user_expressions, None);
1958 assert!(!req.allow_stdin);
1959 assert!(req.stop_on_error);
1960 }
1961 _ => panic!("Expected ExecuteRequest"),
1962 }
1963 }
1964
1965 #[test]
1966 fn test_deserialize_payload() {
1967 let raw_execute_reply_content = r#"
1968 {
1969 "status": "ok",
1970 "execution_count": 1,
1971 "payload": [{
1972 "source": "page",
1973 "data": {
1974 "text/html": "<h1>Hello</h1>",
1975 "text/plain": "Hello"
1976 },
1977 "start": 0
1978 }],
1979 "user_expressions": {}
1980 }
1981 "#;
1982
1983 let execute_reply: ExecuteReply = serde_json::from_str(raw_execute_reply_content).unwrap();
1984
1985 assert_eq!(execute_reply.status, ReplyStatus::Ok);
1986 assert_eq!(execute_reply.execution_count, ExecutionCount::new(1));
1987
1988 let payload = execute_reply.payload.clone();
1989
1990 assert_eq!(payload.len(), 1);
1991 let payload = payload.first().unwrap();
1992
1993 let media = match payload {
1994 Payload::Page { data, .. } => data,
1995 _ => panic!("Expected Page payload type"),
1996 };
1997
1998 let media = serde_json::to_value(media).unwrap();
1999
2000 let expected_media = serde_json::json!({
2001 "text/html": "<h1>Hello</h1>",
2002 "text/plain": "Hello"
2003 });
2004
2005 assert_eq!(media, expected_media);
2006 }
2007
2008 #[test]
2009 pub fn test_display_data_various_data() {
2010 let display_data = DisplayData {
2011 data: serde_json::from_value(json!({
2012 "text/plain": "Hello, World!",
2013 "text/html": "<h1>Hello, World!</h1>",
2014 "application/json": {
2015 "hello": "world",
2016 "foo": "bar",
2017 "ok": [1, 2, 3],
2018 }
2019 }))
2020 .unwrap(),
2021 ..Default::default()
2022 };
2023
2024 let display_data_value = serde_json::to_value(display_data).unwrap();
2025
2026 let expected_display_data_value = serde_json::json!({
2027 "data": {
2028 "text/plain": "Hello, World!",
2029 "text/html": "<h1>Hello, World!</h1>",
2030 "application/json": {
2031 "hello": "world",
2032 "foo": "bar",
2033 "ok": [1, 2, 3]
2034 }
2035 },
2036 "metadata": {}
2037 });
2038
2039 assert_eq!(display_data_value, expected_display_data_value);
2040 }
2041
2042 use std::mem::size_of;
2043
2044 macro_rules! size_of_variant {
2045 ($variant:ty) => {
2046 let size = size_of::<$variant>();
2047 println!("The size of {} is: {} bytes", stringify!($variant), size);
2048
2049 assert!(size <= 96);
2050 };
2051 }
2052
2053 #[test]
2054 fn test_enum_variant_sizes() {
2055 size_of_variant!(ClearOutput);
2056 size_of_variant!(CommClose);
2057 size_of_variant!(CommInfoReply);
2058 size_of_variant!(CommInfoRequest);
2059 size_of_variant!(CommMsg);
2060 size_of_variant!(CommOpen);
2061 size_of_variant!(CompleteReply);
2062 size_of_variant!(CompleteRequest);
2063 size_of_variant!(DebugReply);
2064 size_of_variant!(DebugRequest);
2065 size_of_variant!(DisplayData);
2066 size_of_variant!(ErrorOutput);
2067 size_of_variant!(ExecuteInput);
2068 size_of_variant!(ExecuteReply);
2069 size_of_variant!(ExecuteRequest);
2070 size_of_variant!(ExecuteResult);
2071 size_of_variant!(HistoryReply);
2072 size_of_variant!(HistoryRequest);
2073 size_of_variant!(InputReply);
2074 size_of_variant!(InputRequest);
2075 size_of_variant!(InspectReply);
2076 size_of_variant!(InspectRequest);
2077 size_of_variant!(InterruptReply);
2078 size_of_variant!(InterruptRequest);
2079 size_of_variant!(IsCompleteReply);
2080 size_of_variant!(IsCompleteRequest);
2081 size_of_variant!(Box<KernelInfoReply>);
2082 size_of_variant!(KernelInfoRequest);
2083 size_of_variant!(ShutdownReply);
2084 size_of_variant!(ShutdownRequest);
2085 size_of_variant!(Status);
2086 size_of_variant!(StreamContent);
2087 size_of_variant!(UnknownMessage);
2088 size_of_variant!(UpdateDisplayData);
2089 }
2090
2091 #[test]
2092 fn test_jupyter_message_content_enum_size() {
2093 let size = size_of::<JupyterMessageContent>();
2094 println!("The size of JupyterMessageContent is: {}", size);
2095 assert!(size > 0);
2096 assert!(size <= 96);
2097 }
2098
2099 #[test]
2100 fn test_jupyter_message_parent_header_serializes_to_empty_dict() {
2101 let request = ExecuteRequest {
2102 code: "1 + 1".to_string(),
2103 ..Default::default()
2104 };
2105 let message = JupyterMessage::from(request);
2106
2107 let serialized_message = serde_json::to_value(message).unwrap();
2108
2109 let parent_header = serialized_message.get("parent_header").unwrap();
2111 assert!(parent_header.is_object());
2112 assert!(parent_header.as_object().unwrap().is_empty());
2113 }
2114
2115 #[test]
2116 fn test_user_expressions_serialization() {
2117 let request = ExecuteRequest {
2118 code: "pass".to_string(),
2119 silent: false,
2120 store_history: true,
2121 user_expressions: Some(HashMap::from([(
2122 String::from("expression"),
2123 String::from("42 + 7"),
2124 )])),
2125 allow_stdin: false,
2126 stop_on_error: true,
2127 };
2128 let request_value = serde_json::to_value(request.clone()).unwrap();
2129
2130 let expected_request_value = serde_json::json!({
2131 "code": "pass",
2132 "silent": false,
2133 "store_history": true,
2134 "user_expressions": {"expression": "42 + 7"},
2135 "allow_stdin": false,
2136 "stop_on_error": true
2137 });
2138
2139 assert_eq!(request_value, expected_request_value);
2140
2141 let deserialized_request: ExecuteRequest = serde_json::from_value(request_value).unwrap();
2142 assert_eq!(
2143 deserialized_request.user_expressions,
2144 request.user_expressions
2145 );
2146 }
2147
2148 #[test]
2149 fn test_jupyter_message_parent_header_deserialize() {
2150 let msg = r#"
2151 {
2152 "buffers": [],
2153 "channel": "shell",
2154 "content": {},
2155 "header": {
2156 "date": "2025-05-14T14:32:23.490Z",
2157 "msg_id": "44bd6b44-78a1-4892-87df-c0861a005d56",
2158 "msg_type": "kernel_info_request",
2159 "session": "b75bddaa-6d69-4340-ba13-81516192370e",
2160 "username": "",
2161 "version": "5.2"
2162 },
2163 "metadata": {},
2164 "parent_header": {
2165 "date": "2025-05-14T14:32:23.490Z",
2166 "msg_id": "2aaf8916-6b83-4f5a-80dd-633e94f5d8e1",
2167 "msg_type": "kernel_info_request",
2168 "session": "e2a3165d-76a8-4fef-850f-712102589660",
2169 "username": "",
2170 "version": "5.2"
2171 }
2172}
2173 "#;
2174
2175 let message: JupyterMessage = serde_json::from_str(msg).unwrap();
2176 assert!(message.parent_header.is_some());
2177 assert_eq!(
2178 message.parent_header.as_ref().unwrap().msg_type,
2179 "kernel_info_request"
2180 );
2181 assert_eq!(
2182 message.parent_header.as_ref().unwrap().msg_id,
2183 "2aaf8916-6b83-4f5a-80dd-633e94f5d8e1"
2184 );
2185 assert_eq!(
2186 message.header.msg_id,
2187 "44bd6b44-78a1-4892-87df-c0861a005d56"
2188 );
2189 }
2190
2191 #[test]
2192 fn test_jupyter_message_empty_parent_header_deserialize() {
2193 let msg = r#"
2194 {
2195 "buffers": [],
2196 "channel": "shell",
2197 "content": {},
2198 "header": {
2199 "date": "2025-05-14T14:32:23.490Z",
2200 "msg_id": "44bd6b44-78a1-4892-87df-c0861a005d56",
2201 "msg_type": "kernel_info_request",
2202 "session": "b75bddaa-6d69-4340-ba13-81516192370e",
2203 "username": "",
2204 "version": "5.2"
2205 },
2206 "metadata": {},
2207 "parent_header": {}
2208}
2209 "#;
2210
2211 let message: JupyterMessage = serde_json::from_str(msg).unwrap();
2212 assert!(message.parent_header.is_none());
2213 assert_eq!(message.header.msg_type, "kernel_info_request");
2214 assert_eq!(
2215 message.header.msg_id,
2216 "44bd6b44-78a1-4892-87df-c0861a005d56"
2217 );
2218 }
2219}