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