1#![warn(
2 clippy::disallowed_methods,
3 reason = "Prefer System trait methods over std methods in ty crates"
4)]
5
6use ordermap::OrderMap;
7use ruff_db::system::SystemPathBuf;
8use ruff_python_ast::PythonVersion;
9use ruff_ranged_value::RangedValue;
10use std::{collections::HashMap, hash::BuildHasher};
11
12pub trait Combine {
57 #[must_use]
58 fn combine(mut self, other: Self) -> Self
59 where
60 Self: Sized,
61 {
62 self.combine_with(other);
63 self
64 }
65
66 fn combine_with(&mut self, other: Self);
67}
68
69impl<T> Combine for RangedValue<T>
70where
71 T: Combine,
72{
73 fn combine_with(&mut self, other: Self) {
74 (**self).combine_with(other.into_inner());
75 }
76}
77
78impl<T> Combine for Option<T>
79where
80 T: Combine,
81{
82 fn combine(self, other: Self) -> Self
83 where
84 Self: Sized,
85 {
86 match (self, other) {
87 (Some(a), Some(b)) => Some(a.combine(b)),
88 (None, Some(b)) => Some(b),
89 (a, _) => a,
90 }
91 }
92
93 fn combine_with(&mut self, other: Self) {
94 match (self, other) {
95 (Some(a), Some(b)) => {
96 a.combine_with(b);
97 }
98 (a @ None, Some(b)) => {
99 *a = Some(b);
100 }
101 _ => {}
102 }
103 }
104}
105
106impl<T> Combine for Vec<T> {
107 fn combine_with(&mut self, mut other: Self) {
108 std::mem::swap(self, &mut other);
111 self.extend(other);
112 }
113}
114
115impl<K, V, S> Combine for HashMap<K, V, S>
116where
117 K: Eq + std::hash::Hash,
118 S: BuildHasher,
119{
120 fn combine_with(&mut self, mut other: Self) {
121 std::mem::swap(self, &mut other);
124 self.extend(other);
125 }
126}
127
128impl<K, V, S> Combine for OrderMap<K, V, S>
129where
130 K: Eq + std::hash::Hash,
131 S: BuildHasher,
132{
133 fn combine_with(&mut self, mut other: Self) {
134 std::mem::swap(self, &mut other);
137
138 for (k, v) in other {
139 self.remove(&k);
142
143 self.insert(k, v);
145 }
146 }
147}
148
149macro_rules! impl_noop_combine {
151 ($name:ident) => {
152 impl Combine for $name {
153 #[inline(always)]
154 fn combine_with(&mut self, _other: Self) {}
155
156 #[inline(always)]
157 fn combine(self, _other: Self) -> Self {
158 self
159 }
160 }
161 };
162}
163
164impl_noop_combine!(SystemPathBuf);
165impl_noop_combine!(PythonVersion);
166
167impl_noop_combine!(bool);
169impl_noop_combine!(usize);
170impl_noop_combine!(u8);
171impl_noop_combine!(u16);
172impl_noop_combine!(u32);
173impl_noop_combine!(u64);
174impl_noop_combine!(u128);
175impl_noop_combine!(isize);
176impl_noop_combine!(i8);
177impl_noop_combine!(i16);
178impl_noop_combine!(i32);
179impl_noop_combine!(i64);
180impl_noop_combine!(i128);
181impl_noop_combine!(String);
182
183#[cfg(test)]
184mod tests {
185 use ordermap::OrderMap;
186 use std::collections::HashMap;
187
188 use super::Combine;
189
190 #[test]
191 fn combine_option() {
192 assert_eq!(Some(1).combine(Some(2)), Some(1));
193 assert_eq!(None.combine(Some(2)), Some(2));
194 assert_eq!(Some(1).combine(None), Some(1));
195 }
196
197 #[test]
198 fn combine_vec() {
199 assert_eq!(None.combine(Some(vec![1, 2, 3])), Some(vec![1, 2, 3]));
200 assert_eq!(Some(vec![1, 2, 3]).combine(None), Some(vec![1, 2, 3]));
201 assert_eq!(
202 Some(vec![1, 2, 3]).combine(Some(vec![4, 5, 6])),
203 Some(vec![4, 5, 6, 1, 2, 3])
204 );
205 }
206
207 #[test]
208 fn combine_map() {
209 let a: HashMap<u32, _> = HashMap::from_iter([(1, "a"), (2, "a"), (3, "a")]);
210 let b: HashMap<u32, _> = HashMap::from_iter([(0, "b"), (2, "b"), (5, "b")]);
211
212 assert_eq!(None.combine(Some(b.clone())), Some(b.clone()));
213 assert_eq!(Some(a.clone()).combine(None), Some(a.clone()));
214 assert_eq!(
215 Some(a).combine(Some(b)),
216 Some(HashMap::from_iter([
217 (0, "b"),
218 (1, "a"),
220 (2, "a"),
221 (3, "a"),
222 (5, "b")
223 ]))
224 );
225 }
226
227 #[test]
228 fn combine_order_map() {
229 let a: OrderMap<_, _> = OrderMap::from_iter([(1, "a"), (2, "a"), (3, "a")]);
230 let b: OrderMap<_, _> = OrderMap::from_iter([(0, "b"), (2, "b"), (5, "b")]);
231
232 assert_eq!(None.combine(Some(b.clone())), Some(b.clone()));
233 assert_eq!(Some(a.clone()).combine(None), Some(a.clone()));
234 assert_eq!(
235 a.combine(b),
236 OrderMap::<_, _>::from_iter([(0, "b"), (5, "b"), (1, "a"), (2, "a"), (3, "a"),])
238 );
239 }
240}