1#![deny(unsafe_code)]
12#![warn(missing_docs)]
13
14mod capabilities;
15mod deterministic;
16mod frame;
17mod safepoint;
18mod serde_bin;
19mod state;
20mod version;
21
22pub use capabilities::{
23 continuation_matrix, ContinuationCapabilityMatrix, ContinuationCapabilityRow,
24};
25pub use deterministic::{
26 DeterministicContext, ReplayEngineStatus, ReplayJournal, ReplayJournalEntry,
27};
28pub use frame::{FrameId, SourceLocation, UniversalFrame};
29pub use safepoint::{
30 SafepointKind, SafepointReport, SemanticLossNote, CAPTURE_YES, MIGRATE_NO, MIGRATE_PARTIAL,
31 REPLAY_UNSUPPORTED, SNAPSHOT_YES,
32};
33pub use serde_bin::{from_binary, to_binary, UES_MAGIC};
34pub use state::{
35 AsyncState, CapabilityState, ControlState, ExternalResource, HeapState, MigrationMetadata,
36 ModuleState, UniversalExecutionState,
37};
38pub use version::{check_ues_format, UES_FORMAT_VERSION};
39
40use parallax_core::{ErrorCode, ParallaxError};
41
42pub type Result<T> = parallax_core::Result<T>;
44
45pub fn to_json_bytes(ues: &UniversalExecutionState) -> Result<Vec<u8>> {
47 serde_json::to_vec_pretty(ues).map_err(|e| {
48 ParallaxError::new(ErrorCode::SerializationFailure, e.to_string())
49 .with_source("parallax-ues")
50 .with_operation("to_json_bytes")
51 })
52}
53
54pub fn to_json_bytes_compact(ues: &UniversalExecutionState) -> Result<Vec<u8>> {
56 serde_json::to_vec(ues).map_err(|e| {
57 ParallaxError::new(ErrorCode::SerializationFailure, e.to_string())
58 .with_source("parallax-ues")
59 .with_operation("to_json_bytes_compact")
60 })
61}
62
63pub fn from_json_bytes(bytes: &[u8]) -> Result<UniversalExecutionState> {
65 let ues: UniversalExecutionState = serde_json::from_slice(bytes).map_err(|e| {
66 ParallaxError::new(ErrorCode::SerializationFailure, e.to_string())
67 .with_source("parallax-ues")
68 .with_operation("from_json_bytes")
69 })?;
70 ues.validate()?;
71 Ok(ues)
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use parallax_core::RuntimeKind;
78 use parallax_pcir::PcirProgram;
79
80 #[test]
81 fn json_round_trip_and_version() {
82 let mut ues =
83 UniversalExecutionState::checkpoint_shell(RuntimeKind::Python, "demo.py", "cp1");
84 ues.pcir = Some(PcirProgram::checkpoint_stub("cp1"));
85 let bytes = to_json_bytes(&ues).unwrap();
86 let back = from_json_bytes(&bytes).unwrap();
87 assert_eq!(back.format_version, UES_FORMAT_VERSION);
88 assert_eq!(back.control_state.safepoint_label.as_deref(), Some("cp1"));
89 }
90
91 #[test]
92 fn binary_round_trip() {
93 let ues =
94 UniversalExecutionState::checkpoint_shell(RuntimeKind::JavaScript, "demo.js", "mark");
95 let bin = to_binary(&ues).unwrap();
96 assert!(bin.starts_with(UES_MAGIC));
97 let back = from_binary(&bin).unwrap();
98 assert_eq!(back.source_runtime, RuntimeKind::JavaScript);
99 }
100
101 #[test]
102 fn rejects_future_ues_version() {
103 let mut ues = UniversalExecutionState::checkpoint_shell(RuntimeKind::Python, "x.py", "a");
104 ues.format_version = UES_FORMAT_VERSION + 9;
105 let bytes = to_json_bytes_compact(&ues).unwrap();
106 let err = from_json_bytes(&bytes).unwrap_err();
107 assert!(err.message.contains("unsupported UES format"));
108 }
109
110 #[test]
111 fn continuation_matrix_is_honest() {
112 let m = continuation_matrix(RuntimeKind::Python);
113 assert!(m
114 .rows
115 .iter()
116 .any(|r| r.name == "explicit_checkpoint_capture"));
117 let wasm = continuation_matrix(RuntimeKind::Wasm);
118 let cross = wasm
119 .rows
120 .iter()
121 .find(|r| r.name == "cross_runtime_resume")
122 .unwrap();
123 assert_eq!(cross.level, parallax_core::CapabilityLevel::No);
124 }
125}