1use crate::{
10 constraint::{Constraint, MultiConstraint, Operator, SingleConstraint},
11 interval::{BranchConstraint, Interval, IntervalResult},
12 version::version_compare,
13};
14use std::{cmp::Ordering, collections::HashMap};
15
16pub struct Intervals {
18 cache: HashMap<String, IntervalResult>,
19}
20
21impl Default for Intervals {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl Intervals {
28 #[must_use]
30 pub fn new() -> Self {
31 Self {
32 cache: HashMap::new(),
33 }
34 }
35
36 pub fn clear(&mut self) {
38 self.cache.clear();
39 }
40
41 pub fn is_subset_of(
43 &mut self,
44 candidate: &dyn Constraint,
45 constraint: &dyn Constraint,
46 ) -> bool {
47 if constraint.is_match_all() {
49 return true;
50 }
51
52 if candidate.is_match_none() || constraint.is_match_none() {
54 return false;
55 }
56
57 let intersection = self.get_intersection(candidate, constraint);
59 let candidate_intervals = self.get(candidate);
60
61 if intersection.numeric.len() != candidate_intervals.numeric.len() {
63 return false;
64 }
65
66 for (i, interval) in intersection.numeric.iter().enumerate() {
67 if i >= candidate_intervals.numeric.len() {
68 return false;
69 }
70
71 let cand_interval = &candidate_intervals.numeric[i];
72
73 if interval.start().to_string() != cand_interval.start().to_string() {
75 return false;
76 }
77
78 if interval.end().to_string() != cand_interval.end().to_string() {
80 return false;
81 }
82 }
83
84 if intersection.branches.exclude != candidate_intervals.branches.exclude {
86 return false;
87 }
88
89 if intersection.branches.names.len() != candidate_intervals.branches.names.len() {
90 return false;
91 }
92
93 for (i, name) in intersection.branches.names.iter().enumerate() {
94 if i >= candidate_intervals.branches.names.len()
95 || name != &candidate_intervals.branches.names[i]
96 {
97 return false;
98 }
99 }
100
101 true
102 }
103
104 pub fn have_intersections(&mut self, a: &dyn Constraint, b: &dyn Constraint) -> bool {
106 if a.is_match_all() || b.is_match_all() {
107 return true;
108 }
109
110 if a.is_match_none() || b.is_match_none() {
111 return false;
112 }
113
114 let intersection = self.get_intersection_early_exit(a, b);
115 intersection.matches_something()
116 }
117
118 pub fn get(&mut self, constraint: &dyn Constraint) -> IntervalResult {
120 let key = constraint.to_string();
121
122 if let Some(cached) = self.cache.get(&key) {
123 return cached.clone();
124 }
125
126 let result = self.generate_intervals(constraint, false);
127 self.cache.insert(key, result.clone());
128 result
129 }
130
131 fn get_intersection(&mut self, a: &dyn Constraint, b: &dyn Constraint) -> IntervalResult {
133 let a_intervals = self.get(a);
135 let b_intervals = self.get(b);
136 self.intersect_intervals(&a_intervals, &b_intervals)
137 }
138
139 fn get_intersection_early_exit(
141 &mut self,
142 a: &dyn Constraint,
143 b: &dyn Constraint,
144 ) -> IntervalResult {
145 let a_intervals = self.get(a);
146 let b_intervals = self.get(b);
147 self.intersect_intervals_early_exit(&a_intervals, &b_intervals)
148 }
149
150 fn generate_intervals(
152 &mut self,
153 constraint: &dyn Constraint,
154 stop_on_first: bool,
155 ) -> IntervalResult {
156 if constraint.is_match_all() {
157 return IntervalResult::any();
158 }
159
160 if constraint.is_match_none() {
161 return IntervalResult::none();
162 }
163
164 if let Some(single) = constraint.as_single() {
165 return self.generate_single_intervals(single);
166 }
167
168 if let Some(multi) = constraint.as_multi() {
169 return self.generate_multi_intervals(multi, stop_on_first);
170 }
171
172 IntervalResult::none()
173 }
174
175 #[allow(clippy::unused_self)]
177 fn generate_single_intervals(&self, constraint: &SingleConstraint) -> IntervalResult {
178 let op = constraint.operator();
179 let version = constraint.version();
180
181 if version.starts_with("dev-") {
183 let mut intervals = Vec::new();
184 let mut branches = BranchConstraint::no_dev();
185
186 match op {
187 Operator::Ne => {
188 intervals.push(Interval::any());
190 branches = BranchConstraint::new(vec![version.to_string()], true);
191 },
192 Operator::Eq => {
193 branches.names.push(version.to_string());
194 },
195 _ => {
196 },
198 }
199
200 return IntervalResult::new(intervals, branches);
201 }
202
203 match op {
205 Operator::Gt | Operator::Ge => IntervalResult::new(
206 vec![Interval::new(
207 SingleConstraint::new(op, version),
208 Interval::until_positive_infinity(),
209 )],
210 BranchConstraint::no_dev(),
211 ),
212 Operator::Lt | Operator::Le => IntervalResult::new(
213 vec![Interval::new(
214 Interval::from_zero(),
215 SingleConstraint::new(op, version),
216 )],
217 BranchConstraint::no_dev(),
218 ),
219 Operator::Ne => {
220 IntervalResult::new(
222 vec![
223 Interval::new(
224 Interval::from_zero(),
225 SingleConstraint::new(Operator::Lt, version),
226 ),
227 Interval::new(
228 SingleConstraint::new(Operator::Gt, version),
229 Interval::until_positive_infinity(),
230 ),
231 ],
232 BranchConstraint::any_dev(),
233 )
234 },
235 Operator::Eq => {
236 IntervalResult::new(
238 vec![Interval::new(
239 SingleConstraint::new(Operator::Ge, version),
240 SingleConstraint::new(Operator::Le, version),
241 )],
242 BranchConstraint::no_dev(),
243 )
244 },
245 }
246 }
247
248 fn generate_multi_intervals(
250 &mut self,
251 multi: &MultiConstraint,
252 stop_on_first: bool,
253 ) -> IntervalResult {
254 let constraints = multi.constraints();
255
256 let mut numeric_groups: Vec<Vec<Interval>> = Vec::new();
257 let mut constraint_branches: Vec<BranchConstraint> = Vec::new();
258
259 for c in constraints {
260 let res = self.get(c.as_ref());
261 numeric_groups.push(res.numeric);
262 constraint_branches.push(res.branches);
263 }
264
265 let branches = if multi.is_disjunctive() {
267 self.merge_branches_disjunctive(&constraint_branches)
268 } else {
269 self.merge_branches_conjunctive(&constraint_branches)
270 };
271
272 if numeric_groups.len() == 1 {
274 return IntervalResult::new(numeric_groups.into_iter().next().unwrap(), branches);
275 }
276
277 let numeric = if multi.is_disjunctive() {
279 self.merge_intervals_disjunctive(&numeric_groups)
280 } else {
281 self.merge_intervals_conjunctive(&numeric_groups, stop_on_first)
282 };
283
284 IntervalResult::new(numeric, branches)
285 }
286
287 #[allow(clippy::unused_self)]
289 fn merge_branches_disjunctive(&self, branches: &[BranchConstraint]) -> BranchConstraint {
290 let mut result = BranchConstraint::no_dev();
291
292 for b in branches {
293 if b.exclude {
294 if result.exclude {
295 result.names.retain(|n| b.names.contains(n));
297 } else {
298 result.exclude = true;
300 result.names = b
301 .names
302 .iter()
303 .filter(|n| !result.names.contains(*n))
304 .cloned()
305 .collect();
306 }
307 } else if result.exclude {
308 result.names.retain(|n| !b.names.contains(n));
310 } else {
311 result.names.extend(b.names.iter().cloned());
313 }
314 }
315
316 result.names.sort();
317 result.names.dedup();
318 result
319 }
320
321 #[allow(clippy::unused_self)]
323 fn merge_branches_conjunctive(&self, branches: &[BranchConstraint]) -> BranchConstraint {
324 let mut result = BranchConstraint::any_dev();
325
326 for b in branches {
327 if b.exclude {
328 if result.exclude {
329 result.names.extend(b.names.iter().cloned());
331 } else {
332 result.names.retain(|n| !b.names.contains(n));
334 }
335 } else if result.exclude {
336 let new_names: Vec<String> = b
338 .names
339 .iter()
340 .filter(|n| !result.names.contains(*n))
341 .cloned()
342 .collect();
343 result.exclude = false;
344 result.names = new_names;
345 } else {
346 result.names.retain(|n| b.names.contains(n));
348 }
349 }
350
351 result.names.sort();
352 result.names.dedup();
353 result
354 }
355
356 #[allow(clippy::unused_self)]
358 fn merge_intervals_disjunctive(&self, groups: &[Vec<Interval>]) -> Vec<Interval> {
359 let mut borders: Vec<Border> = Vec::new();
361
362 for group in groups {
363 for interval in group {
364 borders.push(Border {
365 version: interval.start().version().to_string(),
366 operator: interval.start().operator(),
367 is_start: true,
368 });
369 borders.push(Border {
370 version: interval.end().version().to_string(),
371 operator: interval.end().operator(),
372 is_start: false,
373 });
374 }
375 }
376
377 borders.sort_by(|a, b| {
379 let cmp = version_compare(&a.version, &b.version);
380 if cmp == Ordering::Equal {
381 a.sort_order().cmp(&b.sort_order())
382 } else {
383 cmp
384 }
385 });
386
387 let mut active_intervals = 0;
389 let mut intervals = Vec::new();
390 let mut start: Option<SingleConstraint> = None;
391
392 for border in borders {
393 if border.is_start {
394 if active_intervals == 0 {
395 start = Some(SingleConstraint::new(border.operator, &border.version));
396 }
397 active_intervals += 1;
398 } else {
399 active_intervals -= 1;
400 if active_intervals == 0 {
401 if let Some(s) = start.take() {
402 if !is_invalid_interval(&s, &border) {
404 intervals.push(Interval::new(
405 s,
406 SingleConstraint::new(border.operator, &border.version),
407 ));
408 }
409 }
410 }
411 }
412 }
413
414 intervals
415 }
416
417 #[allow(clippy::unused_self)]
419 fn merge_intervals_conjunctive(
420 &self,
421 groups: &[Vec<Interval>],
422 stop_on_first: bool,
423 ) -> Vec<Interval> {
424 let mut borders: Vec<Border> = Vec::new();
426
427 for group in groups {
428 for interval in group {
429 borders.push(Border {
430 version: interval.start().version().to_string(),
431 operator: interval.start().operator(),
432 is_start: true,
433 });
434 borders.push(Border {
435 version: interval.end().version().to_string(),
436 operator: interval.end().operator(),
437 is_start: false,
438 });
439 }
440 }
441
442 borders.sort_by(|a, b| {
444 let cmp = version_compare(&a.version, &b.version);
445 if cmp == Ordering::Equal {
446 a.sort_order().cmp(&b.sort_order())
447 } else {
448 cmp
449 }
450 });
451
452 let mut active_intervals = 0;
454 let activation_threshold = groups.len();
455 let mut intervals = Vec::new();
456 let mut start: Option<SingleConstraint> = None;
457
458 for border in borders {
459 if border.is_start {
460 active_intervals += 1;
461 } else {
462 active_intervals -= 1;
463 }
464
465 if start.is_none() && active_intervals >= activation_threshold {
466 start = Some(SingleConstraint::new(border.operator, &border.version));
467 } else if start.is_some() && active_intervals < activation_threshold {
468 if let Some(s) = start.take() {
469 if !is_invalid_interval(&s, &border) {
471 intervals.push(Interval::new(
472 s,
473 SingleConstraint::new(border.operator, &border.version),
474 ));
475
476 if stop_on_first {
477 break;
478 }
479 }
480 }
481 }
482 }
483
484 intervals
485 }
486
487 fn intersect_intervals(&self, a: &IntervalResult, b: &IntervalResult) -> IntervalResult {
489 let groups = vec![a.numeric.clone(), b.numeric.clone()];
490 let numeric = self.merge_intervals_conjunctive(&groups, false);
491 let branches = self.merge_branches_conjunctive(&[a.branches.clone(), b.branches.clone()]);
492 IntervalResult::new(numeric, branches)
493 }
494
495 fn intersect_intervals_early_exit(
497 &self,
498 a: &IntervalResult,
499 b: &IntervalResult,
500 ) -> IntervalResult {
501 let groups = vec![a.numeric.clone(), b.numeric.clone()];
502 let numeric = self.merge_intervals_conjunctive(&groups, true);
503 let branches = self.merge_branches_conjunctive(&[a.branches.clone(), b.branches.clone()]);
504 IntervalResult::new(numeric, branches)
505 }
506}
507
508#[derive(Debug)]
510struct Border {
511 version: String,
512 operator: Operator,
513 is_start: bool,
514}
515
516impl Border {
517 const fn sort_order(&self) -> i32 {
518 match self.operator {
519 Operator::Ge => -3,
520 Operator::Lt => -2,
521 Operator::Gt => 2,
522 Operator::Le => 3,
523 Operator::Eq | Operator::Ne => 0,
524 }
525 }
526}
527
528fn is_invalid_interval(start: &SingleConstraint, border: &Border) -> bool {
530 if version_compare(start.version(), &border.version) != Ordering::Equal {
531 return false;
532 }
533
534 (start.operator() == Operator::Gt && border.operator == Operator::Le)
536 || (start.operator() == Operator::Ge && border.operator == Operator::Lt)
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542
543 #[test]
544 fn test_single_constraint_intervals() {
545 let mut intervals = Intervals::new();
546
547 let c = SingleConstraint::new(Operator::Ge, "1.0.0.0-dev");
548 let result = intervals.get(&c);
549 assert_eq!(result.numeric.len(), 1);
550 assert_eq!(result.numeric[0].start().operator(), Operator::Ge);
551 }
552
553 #[test]
554 fn test_have_intersections() {
555 let mut intervals = Intervals::new();
556
557 let c1 = SingleConstraint::new(Operator::Ge, "1.0.0.0");
558 let c2 = SingleConstraint::new(Operator::Lt, "2.0.0.0");
559 assert!(intervals.have_intersections(&c1, &c2));
560
561 let c3 = SingleConstraint::new(Operator::Gt, "3.0.0.0");
562 let c4 = SingleConstraint::new(Operator::Lt, "2.0.0.0");
563 assert!(!intervals.have_intersections(&c3, &c4));
564 }
565}