1use std::collections::HashMap;
2
3#[derive(Default, Clone, Debug, Hash, Eq, PartialEq)]
7pub enum MergeLocation {
8 Start,
9 Line(usize),
10 End,
11 #[default]
12 Unknown,
13}
14
15#[derive(Default)]
17pub struct LineMerger<T> {
18 lines: Vec<T>,
20}
21
22impl<T: Clone> LineMerger<T> {
23 pub fn new(lines: impl IntoIterator<Item = T>) -> Self {
24 Self {
25 lines: lines.into_iter().collect::<Vec<_>>(),
26 }
27 }
28
29 pub fn merge(
30 &self,
31 merge: HashMap<MergeLocation, Vec<Vec<T>>>,
32 start: Option<usize>,
33 ) -> Vec<T> {
34 let mut merged = vec![];
35 for (idx, line) in self.lines.iter().enumerate() {
36 let location = if idx == 0 {
37 MergeLocation::Start
38 } else if idx == self.lines.len().saturating_sub(1) {
39 MergeLocation::End
40 } else {
41 let idx = idx
42 .saturating_add(start.unwrap_or_default())
43 .saturating_sub(1);
44 MergeLocation::Line(idx)
45 };
46
47 if location != MergeLocation::Start {
48 merged.push(line.clone());
49 }
50
51 merged.extend(
52 merge
53 .get(&location)
54 .into_iter()
55 .flatten()
56 .flat_map(|merge| merge.iter())
57 .cloned(),
58 );
59
60 if location == MergeLocation::Start {
61 merged.push(line.clone());
62 }
63 }
64
65 merged
66 }
67}
68
69#[cfg(test)]
70mod test {
71 use std::collections::HashMap;
72
73 use pretty_assertions::assert_eq;
74
75 use crate::ui::utils::{LineMerger, MergeLocation};
76
77 #[test]
78 fn lines_should_be_merged_correctly() -> anyhow::Result<()> {
79 let diff = r#"
80fn main() {
81 println!("Hello, world!");
82
83 another_function();
84}
85
86fn another_function() {
87 println!("Another function.");
88}"#;
89
90 let comment = r#"──────────────────────────────────────
91Is this needed?
92──────────────────────────────────────"#
93 .to_string();
94 let comment = comment.lines().collect::<Vec<_>>();
95
96 let merged = LineMerger::new(diff.lines()).merge(
97 HashMap::from([(MergeLocation::Line(2), vec![comment])]),
98 Some(1),
99 );
100 let actual = merged.join("\n");
101
102 let expected = r#"
103fn main() {
104 println!("Hello, world!");
105──────────────────────────────────────
106Is this needed?
107──────────────────────────────────────
108
109 another_function();
110}
111
112fn another_function() {
113 println!("Another function.");
114}"#;
115
116 let expected = expected.to_string();
117
118 assert_eq!(expected, actual);
119
120 Ok(())
121 }
122
123 #[test]
124 fn lines_with_start_should_be_merged_correctly() -> anyhow::Result<()> {
125 let diff = r#"
126fn main() {
127 println!("Hello, world!");
128
129 another_function();
130}
131
132fn another_function() {
133 println!("Another function.");
134}"#;
135
136 let comment = r#"──────────────────────────────────────
137Is this needed?
138──────────────────────────────────────"#
139 .to_string();
140 let comment = comment.lines().collect::<Vec<_>>();
141
142 let merged = LineMerger::new(diff.lines()).merge(
143 HashMap::from([(MergeLocation::Line(103), vec![comment])]),
144 Some(100),
145 );
146 let actual = merged.join("\n");
147
148 let expected = r#"
149fn main() {
150 println!("Hello, world!");
151
152 another_function();
153──────────────────────────────────────
154Is this needed?
155──────────────────────────────────────
156}
157
158fn another_function() {
159 println!("Another function.");
160}"#;
161
162 let expected = expected.to_string();
163
164 assert_eq!(expected, actual);
165
166 Ok(())
167 }
168}