jj_lib/
conflict_labels.rs1use std::fmt;
18
19use crate::merge::Merge;
20
21#[derive(PartialEq, Eq, Clone)]
24pub struct ConflictLabels {
25 labels: Merge<String>,
27}
28
29impl ConflictLabels {
30 pub const fn unlabeled() -> Self {
32 Self {
33 labels: Merge::resolved(String::new()),
34 }
35 }
36
37 pub fn from_merge(labels: Merge<String>) -> Self {
41 if labels.is_resolved() || labels.iter().all(|label| label.is_empty()) {
42 Self::unlabeled()
43 } else {
44 Self { labels }
45 }
46 }
47
48 pub fn from_vec(labels: Vec<String>) -> Self {
51 if labels.is_empty() {
52 Self::unlabeled()
53 } else {
54 Self::from_merge(Merge::from_vec(labels))
55 }
56 }
57
58 pub fn has_labels(&self) -> bool {
60 !self.labels.is_resolved()
61 }
62
63 pub fn num_sides(&self) -> Option<usize> {
66 self.has_labels().then_some(self.labels.num_sides())
67 }
68
69 pub fn as_merge(&self) -> &Merge<String> {
71 &self.labels
72 }
73
74 pub fn into_merge(self) -> Merge<String> {
76 self.labels
77 }
78
79 pub fn as_slice(&self) -> &[String] {
82 if self.has_labels() {
83 self.labels.as_slice()
84 } else {
85 &[]
86 }
87 }
88
89 pub fn get_add(&self, add_index: usize) -> Option<&str> {
91 self.labels
92 .get_add(add_index)
93 .filter(|label| !label.is_empty())
94 .map(String::as_str)
95 }
96
97 pub fn get_remove(&self, remove_index: usize) -> Option<&str> {
99 self.labels
100 .get_remove(remove_index)
101 .filter(|label| !label.is_empty())
102 .map(String::as_str)
103 }
104
105 pub fn simplify_with<T: PartialEq + Clone>(&self, merge: &Merge<T>) -> (Self, Merge<T>) {
108 if self.has_labels() {
109 let (labels, simplified) = self
110 .labels
111 .as_ref()
112 .zip(merge.as_ref())
113 .simplify_by(|&(_label, item)| item)
114 .unzip();
115 (Self::from_merge(labels.cloned()), simplified.cloned())
116 } else {
117 let simplified = merge.simplify();
118 (Self::unlabeled(), simplified)
119 }
120 }
121}
122
123impl fmt::Debug for ConflictLabels {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 if self.has_labels() {
126 f.debug_tuple("Labeled")
127 .field(&self.labels.as_slice())
128 .finish()
129 } else {
130 write!(f, "Unlabeled")
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_conflict_labels_from_vec() {
141 assert_eq!(
143 ConflictLabels::from_vec(vec![]),
144 ConflictLabels::unlabeled()
145 );
146 assert_eq!(
148 ConflictLabels::from_vec(vec![
149 String::from("left"),
150 String::from("base"),
151 String::from("right")
152 ]),
153 ConflictLabels::from_merge(Merge::from_vec(vec![
154 String::from("left"),
155 String::from("base"),
156 String::from("right")
157 ]))
158 );
159 }
160
161 #[test]
162 fn test_conflict_labels_as_slice() {
163 let empty: &[String] = &[];
165 assert_eq!(ConflictLabels::unlabeled().as_slice(), empty);
166 assert_eq!(
168 ConflictLabels::from_merge(Merge::from_vec(vec![
169 String::from("left"),
170 String::from("base"),
171 String::from("right")
172 ]))
173 .as_slice(),
174 &[
175 String::from("left"),
176 String::from("base"),
177 String::from("right")
178 ]
179 );
180 }
181}