1use serde::{Deserialize, Serialize};
2use sha2::{Digest, Sha256};
3
4use crate::{ModuleRef, ProcessRef};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub(crate) struct LashlangExecutionContext {
8 pub(crate) module_ref: ModuleRef,
9 pub(crate) entry: LashlangExecutionEntry,
10}
11
12impl LashlangExecutionContext {
13 pub(crate) fn main(module_ref: ModuleRef) -> Self {
14 Self {
15 module_ref,
16 entry: LashlangExecutionEntry::Main,
17 }
18 }
19
20 pub(crate) fn process(
21 module_ref: ModuleRef,
22 process_ref: ProcessRef,
23 process_name: impl Into<String>,
24 ) -> Self {
25 Self {
26 module_ref,
27 entry: LashlangExecutionEntry::Process {
28 process_ref,
29 process_name: process_name.into(),
30 },
31 }
32 }
33
34 pub(crate) fn builder(&self) -> LashlangExecutionSiteBuilder<'_> {
35 LashlangExecutionSiteBuilder { context: self }
36 }
37}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub(crate) enum LashlangExecutionEntry {
41 Main,
42 Process {
43 process_ref: ProcessRef,
44 process_name: String,
45 },
46}
47
48impl LashlangExecutionEntry {
49 fn stable_key(&self) -> String {
50 match self {
51 Self::Main => "main".to_string(),
52 Self::Process { process_ref, .. } => process_ref_key(process_ref),
53 }
54 }
55}
56
57#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub(crate) struct LashlangAstPath(Vec<u32>);
59
60impl LashlangAstPath {
61 pub(crate) fn root() -> Self {
62 Self(Vec::new())
63 }
64
65 pub(crate) fn from_indices(indices: &[u32]) -> Self {
66 Self(indices.to_vec())
67 }
68
69 pub(crate) fn child(&self, index: usize) -> Self {
70 let mut path = self.0.clone();
71 path.push(index as u32);
72 Self(path)
73 }
74
75 fn stable_text(&self) -> String {
76 if self.0.is_empty() {
77 return "root".to_string();
78 }
79 self.0
80 .iter()
81 .map(u32::to_string)
82 .collect::<Vec<_>>()
83 .join(".")
84 }
85}
86
87#[derive(Clone, Debug)]
88pub(crate) struct LashlangExecutionSiteBuilder<'context> {
89 context: &'context LashlangExecutionContext,
90}
91
92impl LashlangExecutionSiteBuilder<'_> {
93 pub(crate) fn process_node_id(&self) -> String {
94 self.node_id(&LashlangAstPath::root(), "process")
95 }
96
97 pub(crate) fn main_node_id(&self) -> String {
98 self.node_id(&LashlangAstPath::root(), "main")
99 }
100
101 pub(crate) fn node_site(
102 &self,
103 path: &LashlangAstPath,
104 kind: impl Into<String>,
105 label: impl Into<String>,
106 ) -> LashlangExecutionSite {
107 let kind = kind.into();
108 LashlangExecutionSite {
109 node_id: self.node_id(path, &kind),
110 node_kind: kind,
111 label: label.into(),
112 branch: None,
113 }
114 }
115
116 pub(crate) fn branch_site(&self, path: &LashlangAstPath) -> LashlangExecutionSite {
117 LashlangExecutionSite {
118 node_id: self.node_id(path, "branch"),
119 node_kind: "branch".to_string(),
120 label: "if".to_string(),
121 branch: Some(LashlangBranchSite {
122 then_edge_id: self.branch_edge_id(path, ProcessBranchSelection::Then),
123 else_edge_id: self.branch_edge_id(path, ProcessBranchSelection::Else),
124 }),
125 }
126 }
127
128 pub(crate) fn branch_arm_node_id(
129 &self,
130 path: &LashlangAstPath,
131 selection: ProcessBranchSelection,
132 ) -> String {
133 let kind = match selection {
134 ProcessBranchSelection::Then => "branch_arm_then",
135 ProcessBranchSelection::Else => "branch_arm_else",
136 };
137 self.node_id(path, kind)
138 }
139
140 pub(crate) fn edge_id(
141 &self,
142 path: &LashlangAstPath,
143 from: &str,
144 to: &str,
145 label: &str,
146 ) -> String {
147 stable_id(
148 "edge",
149 &[
150 self.context.module_ref.to_string(),
151 self.context.entry.stable_key(),
152 path.stable_text(),
153 from.to_string(),
154 to.to_string(),
155 label.to_string(),
156 ],
157 )
158 }
159
160 pub(crate) fn branch_edge_id(
161 &self,
162 path: &LashlangAstPath,
163 selection: ProcessBranchSelection,
164 ) -> String {
165 let label = match selection {
166 ProcessBranchSelection::Then => "then",
167 ProcessBranchSelection::Else => "else",
168 };
169 stable_id(
170 "edge",
171 &[
172 self.context.module_ref.to_string(),
173 self.context.entry.stable_key(),
174 path.stable_text(),
175 "branch".to_string(),
176 label.to_string(),
177 ],
178 )
179 }
180
181 fn node_id(&self, path: &LashlangAstPath, kind: impl AsRef<str>) -> String {
182 stable_id(
183 kind.as_ref(),
184 &[
185 self.context.module_ref.to_string(),
186 self.context.entry.stable_key(),
187 path.stable_text(),
188 kind.as_ref().to_string(),
189 ],
190 )
191 }
192}
193
194fn stable_id(prefix: &str, parts: &[String]) -> String {
195 let mut hasher = Sha256::new();
196 for part in parts {
197 hasher.update(part.as_bytes());
198 hasher.update([0]);
199 }
200 let hash = format!("{:x}", hasher.finalize());
201 format!("{prefix}:{}", &hash[..24])
202}
203
204pub fn process_ref_key(process_ref: &ProcessRef) -> String {
205 format!("{}:{}", process_ref.component, process_ref.pos)
206}
207
208#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
209pub struct LashlangExecutionSite {
210 pub node_id: String,
211 pub node_kind: String,
212 pub label: String,
213 #[serde(default, skip_serializing_if = "Option::is_none")]
214 pub branch: Option<LashlangBranchSite>,
215}
216
217#[derive(Clone, Debug, PartialEq, Eq)]
218pub struct LashlangExecutionCallSite {
219 pub site: LashlangExecutionSite,
220 pub occurrence: u64,
221}
222
223#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
224pub struct LashlangBranchSite {
225 pub then_edge_id: String,
226 pub else_edge_id: String,
227}
228
229#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
230#[serde(rename_all = "snake_case")]
231pub enum ProcessBranchSelection {
232 Then,
233 Else,
234}
235
236#[derive(Clone, Debug, PartialEq, Eq)]
237pub struct LashlangExecutionChild {
238 pub process_id: String,
239 pub module_ref: ModuleRef,
240 pub process_ref: ProcessRef,
241 pub process_name: String,
242}
243
244#[derive(Clone, Debug, PartialEq, Eq)]
245pub enum LashlangExecutionObservation {
246 NodeStarted {
247 site: LashlangExecutionSite,
248 occurrence: u64,
249 },
250 NodeCompleted {
251 site: LashlangExecutionSite,
252 occurrence: u64,
253 },
254 NodeFailed {
255 site: LashlangExecutionSite,
256 occurrence: u64,
257 error: String,
258 },
259 BranchSelected {
260 site: LashlangExecutionSite,
261 occurrence: u64,
262 edge_id: String,
263 selected: ProcessBranchSelection,
264 },
265 ChildStarted {
266 site: LashlangExecutionSite,
267 occurrence: u64,
268 child: LashlangExecutionChild,
269 },
270}