1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
mod from_object;
use indexmap::IndexSet;
use std::collections::HashMap;
pub use from_object::ObjectLineMapping;
#[derive(Debug)]
struct LineEntry {
cpp_line: u32,
cs_line: u32,
cs_file_idx: usize,
}
#[derive(Debug, Default)]
pub struct LineMapping {
cs_files: IndexSet<String>,
cpp_file_map: HashMap<String, Vec<LineEntry>>,
}
impl LineMapping {
pub fn parse(data: &[u8]) -> Option<Self> {
let json: serde_json::Value = serde_json::from_slice(data).ok()?;
let mut result = Self::default();
if let serde_json::Value::Object(object) = json {
for (cpp_file, file_map) in object {
if cpp_file == "__debug-id__" {
continue;
}
let mut lines = Vec::new();
if let serde_json::Value::Object(file_map) = file_map {
for (cs_file, line_map) in file_map {
if let serde_json::Value::Object(line_map) = line_map {
let cs_file_idx = result.cs_files.insert_full(cs_file).0;
for (from, to) in line_map {
let cpp_line = from.parse().ok()?;
let cs_line = to.as_u64().and_then(|n| n.try_into().ok())?;
lines.push(LineEntry {
cpp_line,
cs_line,
cs_file_idx,
});
}
}
}
}
lines.sort_by_key(|entry| entry.cpp_line);
result.cpp_file_map.insert(cpp_file, lines);
}
}
Some(result)
}
pub fn lookup(&self, file: &str, line: u32) -> Option<(&str, u32)> {
let lines = self.cpp_file_map.get(file)?;
let idx = match lines.binary_search_by_key(&line, |entry| entry.cpp_line) {
Ok(idx) => idx,
Err(0) => return None,
Err(idx) => idx - 1,
};
let LineEntry {
cs_line,
cs_file_idx,
cpp_line,
} = lines.get(idx)?;
if line.saturating_sub(*cpp_line) > 5 {
return None;
}
Some((self.cs_files.get_index(*cs_file_idx)?, *cs_line))
}
}
#[cfg(test)]
mod tests {
use super::from_object::SourceInfos;
use super::*;
#[test]
fn test_lookup() {
let cpp_source = b"Lorem ipsum dolor sit amet
//<source_info:main.cs:17>
// some
// comments
some expression // 5
stretching
over
multiple lines
// blank lines
// and stuff
// 13
//<source_info:main.cs:29>
actual source code // 15
";
let line_mappings: HashMap<_, _> = SourceInfos::new(cpp_source)
.map(|si| (si.cpp_line, si.cs_line))
.collect();
let mapping = HashMap::from([("main.cpp", HashMap::from([("main.cs", line_mappings)]))]);
let mapping_json = serde_json::to_string(&mapping).unwrap();
let parsed_mapping = LineMapping::parse(mapping_json.as_bytes()).unwrap();
assert_eq!(parsed_mapping.lookup("main.cpp", 2), None);
assert_eq!(parsed_mapping.lookup("main.cpp", 5), Some(("main.cs", 17)));
assert_eq!(parsed_mapping.lookup("main.cpp", 12), None);
assert_eq!(parsed_mapping.lookup("main.cpp", 15), Some(("main.cs", 29)));
}
}