1use std::env;
8use std::fs;
9use std::path::{Path, PathBuf};
10use std::process;
11
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
16pub struct Node {
17 pub id: String,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub function_id: Option<String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub call_id: Option<u64>,
25 pub label: String,
27 pub kind: NodeKind,
29 pub module_path: String,
31 pub file: String,
33 pub line: u32,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub source: Option<String>,
38}
39
40#[derive(Clone, Debug, Serialize, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum NodeKind {
44 Function,
46 Type,
48 Test,
50}
51
52#[derive(Clone, Debug, Serialize, Deserialize)]
54pub struct Edge {
55 pub from: String,
57 pub to: String,
59 #[serde(default)]
61 pub kind: EdgeKind,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub label: Option<String>,
65}
66
67#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
69#[serde(rename_all = "snake_case")]
70pub enum EdgeKind {
71 #[default]
73 ControlFlow,
74 DataFlow,
76 TestLink,
78}
79
80#[derive(Clone, Debug, Serialize, Deserialize)]
82pub struct ValueSlot {
83 pub name: String,
85 pub preview: String,
87}
88
89#[derive(Clone, Debug, Serialize, Deserialize)]
91pub struct Event {
92 pub seq: u64,
94 pub call_id: Option<u64>,
96 pub parent_call_id: Option<u64>,
98 pub node_id: String,
100 pub kind: EventKind,
102 pub title: String,
104 pub values: Vec<ValueSlot>,
106}
107
108#[derive(Clone, Debug, Serialize, Deserialize)]
110#[serde(rename_all = "snake_case")]
111pub enum EventKind {
112 FunctionEnter,
114 FunctionExit,
116 ValueSnapshot,
118 TestStarted,
120 TestPassed,
122 TestFailed,
124}
125
126#[derive(Clone, Debug, Serialize, Deserialize)]
128pub struct Session {
129 pub title: String,
131 pub nodes: Vec<Node>,
133 pub edges: Vec<Edge>,
135 pub events: Vec<Event>,
137}
138
139impl Session {
140 pub fn new(title: impl Into<String>) -> Self {
142 Self {
143 title: title.into(),
144 nodes: Vec::new(),
145 edges: Vec::new(),
146 events: Vec::new(),
147 }
148 }
149}
150
151#[derive(Clone, Copy, Debug)]
153pub struct FunctionMeta {
154 pub id: &'static str,
156 pub label: &'static str,
158 pub module_path: &'static str,
160 pub file: &'static str,
162 pub line: u32,
164 pub source: &'static str,
166}
167
168#[derive(Clone, Copy, Debug)]
170pub struct TypeMeta {
171 pub id: &'static str,
173 pub label: &'static str,
175 pub module_path: &'static str,
177 pub file: &'static str,
179 pub line: u32,
181 pub source: &'static str,
183}
184
185pub fn write_session_json(session: &Session, path: impl AsRef<Path>) -> std::io::Result<()> {
191 let json = serde_json::to_string_pretty(session).map_err(std::io::Error::other)?;
192
193 if let Some(parent) = path.as_ref().parent() {
194 fs::create_dir_all(parent)?;
195 }
196
197 fs::write(path, json)
198}
199
200pub fn read_session_json(path: impl AsRef<Path>) -> std::io::Result<Session> {
202 let content = fs::read_to_string(path)?;
203 serde_json::from_str(&content).map_err(std::io::Error::other)
204}
205
206fn sanitize_filename(label: &str) -> String {
208 let sanitized: String = label
209 .chars()
210 .map(|ch| match ch {
211 'a'..='z' | 'A'..='Z' | '0'..='9' => ch,
212 _ => '-',
213 })
214 .collect();
215
216 sanitized.trim_matches('-').to_owned()
217}
218
219pub fn write_session_snapshot_in_dir(
221 session: &Session,
222 dir: impl AsRef<Path>,
223 label: impl AsRef<str>,
224) -> std::io::Result<PathBuf> {
225 fs::create_dir_all(&dir)?;
226
227 let file_name = format!(
228 "{}-{}.json",
229 sanitize_filename(label.as_ref()),
230 process::id()
231 );
232 let path = dir.as_ref().join(file_name);
233 write_session_json(session, &path)?;
234 Ok(path)
235}
236
237pub fn write_session_snapshot_from_env(
241 session: &Session,
242 label: impl AsRef<str>,
243) -> std::io::Result<Option<PathBuf>> {
244 match env::var("DBG_SESSION_DIR") {
245 Ok(dir) => write_session_snapshot_in_dir(session, dir, label).map(Some),
246 Err(env::VarError::NotPresent) => Ok(None),
247 Err(error) => Err(std::io::Error::other(error)),
248 }
249}