objects/blame/
finalize.rs1use crate::object::{ContentHash, FileProvenance, Origin, OriginSet};
5
6use super::types::{BlameSliceError, OriginRange};
7
8pub fn finalize_file_provenance(
13 file_blob: ContentHash,
14 line_count: u32,
15 ranges: impl IntoIterator<Item = OriginRange>,
16) -> Result<FileProvenance, BlameSliceError> {
17 let mut ranges: Vec<OriginRange> = ranges.into_iter().collect();
18 ranges.sort_by_key(|range| range.target_start);
19
20 if line_count == 0 {
21 if ranges
22 .iter()
23 .any(|range| range.target_start != 0 || range.len != 0)
24 {
25 return Err(BlameSliceError::InvalidCoverage);
26 }
27 let origins = ranges
28 .first()
29 .map(|range| vec![range.origin.clone()])
30 .unwrap_or_default();
31 let origin_sets = if origins.is_empty() {
32 Vec::new()
33 } else {
34 vec![OriginSet {
35 origin_indexes: vec![0],
36 }]
37 };
38 let provenance = FileProvenance::new(file_blob, 0, Vec::new(), origins, origin_sets);
39 provenance
40 .validate()
41 .map_err(|_| BlameSliceError::InvalidCoverage)?;
42 return Ok(provenance);
43 }
44
45 let coalesced = coalesce(ranges);
46 let mut next_line = 0u32;
47 let mut origins: Vec<Origin> = Vec::new();
48 let mut origin_sets = Vec::new();
49 let mut spans = Vec::new();
50
51 for range in coalesced {
52 if range.target_start != next_line || range.len == 0 {
53 return Err(BlameSliceError::InvalidCoverage);
54 }
55 let origin_index = origin_index(&mut origins, range.origin);
56 let origin_set_index = origin_set_index(&mut origin_sets, origin_index);
57 spans.push(crate::object::LineSpan {
58 start_line: range.target_start,
59 line_len: range.len,
60 origin_set_index,
61 });
62 next_line = next_line.saturating_add(range.len);
63 }
64 if next_line != line_count {
65 return Err(BlameSliceError::InvalidCoverage);
66 }
67
68 let provenance = FileProvenance::new(file_blob, line_count, spans, origins, origin_sets);
69 provenance
70 .validate()
71 .map_err(|_| BlameSliceError::InvalidCoverage)?;
72 Ok(provenance)
73}
74
75fn coalesce(ranges: Vec<OriginRange>) -> Vec<OriginRange> {
76 let mut out: Vec<OriginRange> = Vec::new();
77 for range in ranges {
78 match out.last_mut() {
79 Some(last)
80 if last.target_start + last.len == range.target_start
81 && last.origin == range.origin =>
82 {
83 last.len += range.len;
84 }
85 _ => out.push(range),
86 }
87 }
88 out
89}
90
91fn origin_index(origins: &mut Vec<Origin>, origin: Origin) -> u32 {
92 if let Some((index, _)) = origins
93 .iter()
94 .enumerate()
95 .find(|(_, existing)| **existing == origin)
96 {
97 return index as u32;
98 }
99 let next = origins.len() as u32;
100 origins.push(origin);
101 next
102}
103
104fn origin_set_index(origin_sets: &mut Vec<OriginSet>, origin_index: u32) -> u32 {
105 let indexes = vec![origin_index];
106 if let Some((index, _)) = origin_sets
107 .iter()
108 .enumerate()
109 .find(|(_, set)| set.origin_indexes == indexes)
110 {
111 return index as u32;
112 }
113 let next = origin_sets.len() as u32;
114 origin_sets.push(OriginSet {
115 origin_indexes: indexes,
116 });
117 next
118}