#[derive(Debug, Clone)]
pub struct FileBoundaryMap {
boundaries: Vec<FileBoundary>,
}
#[derive(Debug, Clone)]
struct FileBoundary {
file_path: String,
start_line: usize,
line_count: usize,
}
impl FileBoundaryMap {
pub fn new() -> Self {
Self { boundaries: Vec::new() }
}
pub fn add_file(&mut self, file_path: String, line_count: usize) {
let start_line = if let Some(last) = self.boundaries.last() {
let effective_line_count = last.line_count.max(1);
last.start_line + effective_line_count + 1
} else {
1
};
self.boundaries.push(FileBoundary {
file_path,
start_line,
line_count,
});
}
pub fn map_line(&self, combined_line: usize) -> (String, usize) {
for boundary in &self.boundaries {
let end_line = boundary.start_line + boundary.line_count;
if combined_line >= boundary.start_line && combined_line < end_line {
let original_line = combined_line - boundary.start_line + 1;
return (boundary.file_path.clone(), original_line);
}
}
("unknown".to_string(), combined_line)
}
pub fn file_count(&self) -> usize {
self.boundaries.len()
}
}
impl Default for FileBoundaryMap {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_boundary_map() {
let map = FileBoundaryMap::new();
assert_eq!(map.file_count(), 0);
let (file, line) = map.map_line(1);
assert_eq!(file, "unknown");
assert_eq!(line, 1);
}
#[test]
fn test_single_file() {
let mut map = FileBoundaryMap::new();
map.add_file("test.tx".to_string(), 5);
assert_eq!(map.file_count(), 1);
let (file, line) = map.map_line(1);
assert_eq!(file, "test.tx");
assert_eq!(line, 1);
let (file, line) = map.map_line(5);
assert_eq!(file, "test.tx");
assert_eq!(line, 5);
let (file, line) = map.map_line(6);
assert_eq!(file, "unknown");
assert_eq!(line, 6);
}
#[test]
fn test_multiple_files() {
let mut map = FileBoundaryMap::new();
map.add_file("flows.tx".to_string(), 3);
map.add_file("deploy.tx".to_string(), 5);
assert_eq!(map.file_count(), 2);
let (file, line) = map.map_line(1);
assert_eq!(file, "flows.tx");
assert_eq!(line, 1);
let (file, line) = map.map_line(3);
assert_eq!(file, "flows.tx");
assert_eq!(line, 3);
let (file, line) = map.map_line(4);
assert_eq!(file, "unknown");
let (file, line) = map.map_line(5);
assert_eq!(file, "deploy.tx");
assert_eq!(line, 1);
let (file, line) = map.map_line(9);
assert_eq!(file, "deploy.tx");
assert_eq!(line, 5);
}
#[test]
fn test_three_files() {
let mut map = FileBoundaryMap::new();
map.add_file("flows.tx".to_string(), 3);
map.add_file("variables.tx".to_string(), 2);
map.add_file("deploy.tx".to_string(), 4);
let (file, line) = map.map_line(2);
assert_eq!(file, "flows.tx");
assert_eq!(line, 2);
let (file, line) = map.map_line(6);
assert_eq!(file, "variables.tx");
assert_eq!(line, 2);
let (file, line) = map.map_line(10);
assert_eq!(file, "deploy.tx");
assert_eq!(line, 3);
}
#[test]
fn test_empty_file_in_sequence() {
let mut map = FileBoundaryMap::new();
map.add_file("first.tx".to_string(), 2);
map.add_file("empty.tx".to_string(), 0);
map.add_file("third.tx".to_string(), 3);
let (file, line) = map.map_line(2);
assert_eq!(file, "first.tx");
assert_eq!(line, 2);
let (file, _) = map.map_line(4);
assert_eq!(file, "unknown");
let (file, line) = map.map_line(6);
assert_eq!(file, "third.tx");
assert_eq!(line, 1);
}
}