everruns_core/
execution_context.rs1use everruns_provider::typed_id::{ExecId, MessageId, SessionId, TurnId, WorkspaceId};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ExecutionContext {
9 pub session_id: SessionId,
11 pub turn_id: TurnId,
13 pub input_message_id: MessageId,
15 pub exec_id: ExecId,
17 #[serde(default)]
19 pub workspace_id: Option<WorkspaceId>,
20}
21
22impl ExecutionContext {
23 pub fn new(session_id: SessionId, turn_id: TurnId, input_message_id: MessageId) -> Self {
25 Self {
26 session_id,
27 turn_id,
28 input_message_id,
29 exec_id: ExecId::new(),
30 workspace_id: None,
31 }
32 }
33
34 pub fn with_workspace_id(mut self, workspace_id: WorkspaceId) -> Self {
36 self.workspace_id = Some(workspace_id);
37 self
38 }
39
40 pub fn next_exec(&self) -> Self {
42 Self {
43 session_id: self.session_id,
44 turn_id: self.turn_id,
45 input_message_id: self.input_message_id,
46 exec_id: ExecId::new(),
47 workspace_id: self.workspace_id,
48 }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn next_exec_preserves_lineage_and_workspace_and_changes_execution_id() {
58 for workspace in [None, Some(WorkspaceId::from_seed(5))] {
59 let mut context = ExecutionContext::new(
60 SessionId::from_seed(1),
61 TurnId::from_seed(2),
62 MessageId::from_seed(3),
63 );
64 assert_eq!(context.workspace_id, None);
65 if let Some(workspace) = workspace {
66 context = context.with_workspace_id(workspace);
67 }
68 let next = context.next_exec();
69 assert_eq!(next.session_id, SessionId::from_seed(1));
70 assert_eq!(next.turn_id, TurnId::from_seed(2));
71 assert_eq!(next.input_message_id, MessageId::from_seed(3));
72 assert_eq!(next.workspace_id, workspace);
73 assert_ne!(next.exec_id, context.exec_id);
74 assert_ne!(next.next_exec().exec_id, next.exec_id);
75 }
76 }
77
78 #[test]
79 fn wire_shape_preserves_literal_identity_and_missing_workspace_default() {
80 let wire = serde_json::json!({"session_id":"session_00000000000000000000000000000001","turn_id":"turn_00000000000000000000000000000002","input_message_id":"message_00000000000000000000000000000003","exec_id":"exec_00000000000000000000000000000004"});
81 let parsed: ExecutionContext = serde_json::from_value(wire.clone()).unwrap();
82 assert_eq!(parsed.workspace_id, None);
83 assert_eq!(parsed.session_id, SessionId::from_seed(1));
84 assert_eq!(parsed.turn_id, TurnId::from_seed(2));
85 assert_eq!(parsed.input_message_id, MessageId::from_seed(3));
86 assert_eq!(parsed.exec_id, ExecId::from_seed(4));
87 let mut expected = wire;
88 expected["workspace_id"] = serde_json::Value::Null;
89 assert_eq!(serde_json::to_value(&parsed).unwrap(), expected);
90 let attached = parsed
91 .with_workspace_id(WorkspaceId::from_seed(5))
92 .with_workspace_id(WorkspaceId::from_seed(6));
93 expected["workspace_id"] = serde_json::json!("wsp_00000000000000000000000000000006");
94 assert_eq!(serde_json::to_value(&attached).unwrap(), expected);
95 assert_eq!(
96 serde_json::from_value::<ExecutionContext>(expected)
97 .unwrap()
98 .workspace_id,
99 Some(WorkspaceId::from_seed(6))
100 );
101 }
102}