1use naga::Module;
2use std::fs::File;
3use std::io::{BufRead, BufReader};
4use vyre_emit_naga::BindResultEntry;
5
6#[derive(Debug)]
8pub enum BindResultLogError {
9 Open(std::io::Error),
11 Read(std::io::Error),
13 Parse(usize, serde_json::Error),
16}
17
18pub struct FailureTrace {
19 pub text: String,
20}
21
22pub fn failure_trace(module: &Module, error: &naga::valid::ValidationError) -> FailureTrace {
23 let text = format!(
24 "FAILURE: {:#?}\nentry_points={}\nfunctions={}\nglobals={}",
25 error,
26 module.entry_points.len(),
27 module.functions.len(),
28 module.global_variables.len()
29 );
30 FailureTrace { text }
31}
32
33pub fn failure_trace_wgsl(
34 module: &Module,
35 info: &naga::valid::ModuleInfo,
36 err: &naga::back::wgsl::Error,
37) -> FailureTrace {
38 let text = format!(
39 "FAILURE: {:#?}\nentry_points={}\nfunctions={}\nglobals={}\nmodule_info={:#?}",
40 err,
41 module.entry_points.len(),
42 module.functions.len(),
43 module.global_variables.len(),
44 info
45 );
46 FailureTrace { text }
47}
48
49pub fn load_bind_result_log(
55 path: &str,
56) -> Result<Vec<BindResultEntry>, BindResultLogError> {
57 let file = File::open(path).map_err(BindResultLogError::Open)?;
58 let reader = BufReader::new(file);
59 let mut entries = Vec::new();
60 for (line_no, raw) in reader.lines().enumerate() {
61 let line = raw.map_err(BindResultLogError::Read)?;
62 let entry: BindResultEntry = serde_json::from_str(&line)
63 .map_err(|e| BindResultLogError::Parse(line_no + 1, e))?;
64 entries.push(entry);
65 }
66 Ok(entries)
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn load_bind_result_log_missing_file_returns_open_error() {
75 let r = load_bind_result_log("/nonexistent/path/bind_log.jsonl");
76 assert!(
77 matches!(r, Err(BindResultLogError::Open(_))),
78 "expected Err(Open(_)) for missing file, got {:?}",
79 r.err().map(|e| format!("{e:?}"))
80 );
81 }
82
83 #[test]
84 fn load_bind_result_log_valid_entries_parses_all() {
85 let entry = BindResultEntry {
87 vyre_op_id: 7,
88 op_kind: "Literal".to_string(),
89 init_handle: 42,
90 init_scalar_kind: Some("Uint".to_string()),
91 child_body_depth: 0,
92 value_types_at_call: None,
93 publish_path: "root/op7".to_string(),
94 local_allocated_ty: None,
95 };
96 let line = serde_json::to_string(&entry).unwrap();
97 let dir = tempfile::tempdir().unwrap();
98 let path = dir.path().join("bind.jsonl");
99 std::fs::write(&path, format!("{line}\n")).unwrap();
100
101 let result = load_bind_result_log(path.to_str().unwrap()).unwrap();
102 assert_eq!(result.len(), 1, "expected exactly 1 entry");
103 assert_eq!(result[0].vyre_op_id, 7);
104 assert_eq!(result[0].op_kind, "Literal");
105 assert_eq!(result[0].init_handle, 42);
106 assert_eq!(result[0].publish_path, "root/op7");
107 }
108
109 #[test]
110 fn load_bind_result_log_malformed_json_line_returns_parse_error() {
111 let dir = tempfile::tempdir().unwrap();
112 let path = dir.path().join("bad.jsonl");
113 let entry = BindResultEntry {
115 vyre_op_id: 1,
116 op_kind: "Load".to_string(),
117 init_handle: 0,
118 init_scalar_kind: None,
119 child_body_depth: 0,
120 value_types_at_call: None,
121 publish_path: "p".to_string(),
122 local_allocated_ty: None,
123 };
124 let valid_line = serde_json::to_string(&entry).unwrap();
125 std::fs::write(&path, format!("{valid_line}\nnot valid json\n")).unwrap();
126
127 let r = load_bind_result_log(path.to_str().unwrap());
128 assert!(
129 matches!(r, Err(BindResultLogError::Parse(2, _))),
130 "expected Err(Parse(2, _)) for malformed line 2, got {:?}",
131 r.err().map(|e| format!("{e:?}"))
132 );
133 }
134}