1use debian_control::lossless::relations::{Entry, Relation, Relations};
3use debian_control::relations::VersionConstraint;
4use debversion::Version;
5
6pub fn is_dep_implied(dep: &Relation, outer: &Relation) -> bool {
10 if dep.name() != outer.name() {
11 return false;
12 }
13
14 let (v1, v2) = match (dep.version(), outer.version()) {
15 (Some(v1), Some(v2)) => (v1, v2),
16 (None, _) => return true,
17 (_, None) => return false,
18 };
19
20 match (v1, v2) {
21 ((VersionConstraint::GreaterThanEqual, v1), (VersionConstraint::GreaterThan, v2)) => {
22 v2 > v1
23 }
24 (
25 (VersionConstraint::GreaterThanEqual, v1),
26 (VersionConstraint::GreaterThanEqual, v2) | (VersionConstraint::Equal, v2),
27 ) => v2 >= v1,
28 (
29 (VersionConstraint::GreaterThanEqual, _v1),
30 (VersionConstraint::LessThanEqual, _v2) | (VersionConstraint::LessThan, _v2),
31 ) => false,
32 ((VersionConstraint::Equal, v1), (VersionConstraint::Equal, v2)) => v2 == v1,
33 ((VersionConstraint::Equal, _), (_, _)) => false,
34 ((VersionConstraint::LessThan, v1), (VersionConstraint::LessThan, v2)) => v2 <= v1,
35 (
36 (VersionConstraint::LessThan, v1),
37 (VersionConstraint::LessThanEqual, v2) | (VersionConstraint::Equal, v2),
38 ) => v2 < v1,
39 (
40 (VersionConstraint::LessThan, _v1),
41 (VersionConstraint::GreaterThanEqual, _v2) | (VersionConstraint::GreaterThan, _v2),
42 ) => false,
43 (
44 (VersionConstraint::LessThanEqual, v1),
45 (VersionConstraint::LessThanEqual, v2)
46 | (VersionConstraint::Equal, v2)
47 | (VersionConstraint::LessThan, v2),
48 ) => v2 <= v1,
49 (
50 (VersionConstraint::LessThanEqual, _v1),
51 (VersionConstraint::GreaterThanEqual, _v2) | (VersionConstraint::GreaterThan, _v2),
52 ) => false,
53 ((VersionConstraint::GreaterThan, v1), (VersionConstraint::GreaterThan, v2)) => v2 >= v1,
54 (
55 (VersionConstraint::GreaterThan, v1),
56 (VersionConstraint::GreaterThanEqual, v2) | (VersionConstraint::Equal, v2),
57 ) => v2 > v1,
58 (
59 (VersionConstraint::GreaterThan, _v1),
60 (VersionConstraint::LessThanEqual, _v2) | (VersionConstraint::LessThan, _v2),
61 ) => false,
62 }
63}
64
65pub fn is_relation_implied(inner: &Entry, outer: &Entry) -> bool {
71 if inner == outer {
72 return true;
73 }
74
75 for inner_dep in inner.relations() {
77 if outer
78 .relations()
79 .any(|outer_dep| is_dep_implied(&inner_dep, &outer_dep))
80 {
81 return true;
82 }
83 }
84
85 false
86}
87
88pub fn ensure_relation(rels: &mut Relations, newrel: Entry) {
94 let mut obsolete = vec![];
95 for (i, relation) in rels.entries().enumerate() {
96 if is_relation_implied(&newrel, &relation) {
97 return;
99 }
100 if is_relation_implied(&relation, &newrel) {
101 obsolete.push(i);
102 }
103 }
104
105 if let Some(pos) = obsolete.pop() {
106 rels.replace(pos, newrel);
107 } else {
108 rels.add_dependency(newrel, None);
109 }
110
111 for i in obsolete.into_iter().rev() {
112 rels.remove_entry(i);
113 }
114}
115
116pub fn ensure_minimum_version(
141 relations: &mut Relations,
142 package: &str,
143 minimum_version: &Version,
144) -> bool {
145 let is_obsolete = |entry: &Entry| -> bool {
146 for r in entry.relations() {
147 if r.name() != package {
148 continue;
149 }
150 if let Some((vc, v)) = r.version().as_ref() {
151 if *vc == VersionConstraint::GreaterThan && v < minimum_version {
152 return true;
153 }
154 if *vc == VersionConstraint::GreaterThanEqual && v <= minimum_version {
155 return true;
156 }
157 }
158 }
159 false
160 };
161
162 let mut found = false;
163 let mut changed = false;
164 let mut obsolete_relations = vec![];
165 let mut relevant_relations = vec![];
166 for (i, entry) in relations.entries().enumerate() {
167 let names = entry
168 .relations()
169 .map(|r| r.name().to_string())
170 .collect::<Vec<_>>();
171 if names.len() > 1 && names.contains(&package.to_string()) && is_obsolete(&entry) {
172 obsolete_relations.push(i);
173 }
174 if names != [package] {
175 continue;
176 }
177 found = true;
178 if entry
179 .relations()
180 .next()
181 .and_then(|r| r.version())
182 .map(|(_vc, v)| &v < minimum_version)
183 .unwrap_or(false)
184 {
185 relevant_relations.push(i);
186 }
187 }
188 if !found {
189 changed = true;
190 relations.add_dependency(
191 Relation::new(
192 package,
193 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
194 )
195 .into(),
196 None,
197 );
198 } else {
199 for i in relevant_relations.into_iter().rev() {
200 relations.replace(
201 i,
202 Relation::new(
203 package,
204 Some((VersionConstraint::GreaterThanEqual, minimum_version.clone())),
205 )
206 .into(),
207 );
208 changed = true;
209 }
210 }
211 for i in obsolete_relations.into_iter().rev() {
212 relations.remove_entry(i);
213 }
214 changed
215}
216
217pub fn ensure_exact_version(
238 relations: &mut Relations,
239 package: &str,
240 version: &Version,
241 position: Option<usize>,
242) -> bool {
243 let mut changed = false;
244 let mut found = vec![];
245
246 for (i, entry) in relations.entries().enumerate() {
247 let names = entry
248 .relations()
249 .map(|r| r.name().to_string())
250 .collect::<Vec<_>>();
251 if names != [package] {
252 continue;
253 }
254 let relation = entry.relations().next().unwrap();
255 if relation
256 .version()
257 .is_none_or(|(vc, v)| vc != VersionConstraint::Equal || &v != version)
258 {
259 found.push(i);
260 }
261 }
262 if found.is_empty() {
263 changed = true;
264 let relation = Relation::new(package, Some((VersionConstraint::Equal, version.clone())));
265 if let Some(position) = position {
266 relations.insert(position, relation.into());
267 } else {
268 relations.add_dependency(relation.into(), None);
269 }
270 } else {
271 for i in found.into_iter().rev() {
272 relations.replace(
273 i,
274 Relation::new(package, Some((VersionConstraint::Equal, version.clone()))).into(),
275 );
276 changed = true;
277 }
278 }
279 changed
280}
281
282pub fn ensure_some_version(relations: &mut Relations, package: &str) -> bool {
306 for entry in relations.entries() {
307 let names = entry
308 .relations()
309 .map(|r| r.name().to_string())
310 .collect::<Vec<_>>();
311 if names == [package] {
312 return false;
313 }
314 }
315 relations.add_dependency(Relation::simple(package).into(), None);
316 true
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use debian_control::lossless::relations::{Relation, Relations};
323
324 mod is_dep_implied {
325 use super::*;
326 fn parse(s: &str) -> Relation {
327 let rs: Relations = s.parse().unwrap();
328 let mut entries = rs.entries();
329 let entry = entries.next().unwrap();
330 assert_eq!(entries.next(), None);
331 let mut relations = entry.relations();
332 let relation = relations.next().unwrap();
333 assert_eq!(relations.next(), None);
334 relation
335 }
336
337 fn is_dep_implied(inner: &str, outer: &str) -> bool {
338 super::is_dep_implied(&parse(inner), &parse(outer))
339 }
340
341 #[test]
342 fn test_no_version() {
343 assert!(is_dep_implied("bzr", "bzr"));
344 assert!(is_dep_implied("bzr", "bzr (>= 3)"));
345 assert!(is_dep_implied("bzr", "bzr (<< 3)"));
346 }
347
348 #[test]
349 fn test_wrong_package() {
350 assert!(!is_dep_implied("bzr", "foo (<< 3)"));
351 }
352
353 #[test]
354 fn test_version() {
355 assert!(!is_dep_implied("bzr (>= 3)", "bzr (<< 3)"));
356 assert!(is_dep_implied("bzr (>= 3)", "bzr (= 3)"));
357 assert!(!is_dep_implied("bzr (= 3)", "bzr (>= 3)"));
358 assert!(!is_dep_implied("bzr (>= 3)", "bzr (>> 3)"));
359 assert!(!is_dep_implied("bzr (= 3)", "bzr (= 4)"));
360 assert!(!is_dep_implied("bzr (>= 3)", "bzr (>= 2)"));
361 assert!(is_dep_implied("bzr (>= 3)", "bzr (>= 3)"));
362 assert!(is_dep_implied("bzr", "bzr (<< 3)"));
363 assert!(is_dep_implied("bzr (<< 3)", "bzr (<< 3)"));
364 assert!(is_dep_implied("bzr (<= 3)", "bzr (<< 3)"));
365 assert!(!is_dep_implied("bzr (>= 2)", "bzr (<< 3)"));
366 assert!(!is_dep_implied("bzr (<< 2)", "bzr (<< 3)"));
367 assert!(!is_dep_implied("bzr (<= 2)", "bzr (<< 3)"));
368 assert!(is_dep_implied("bzr (<= 5)", "bzr (<< 3)"));
369 assert!(is_dep_implied("bzr (<= 5)", "bzr (= 3)"));
370 assert!(!is_dep_implied("bzr (<= 5)", "bzr (>= 3)"));
371 assert!(is_dep_implied("bzr (>> 5)", "bzr (>> 6)"));
372 assert!(is_dep_implied("bzr (>> 5)", "bzr (>> 5)"));
373 assert!(!is_dep_implied("bzr (>> 5)", "bzr (>> 4)"));
374 assert!(is_dep_implied("bzr (>> 5)", "bzr (= 6)"));
375 assert!(!is_dep_implied("bzr (>> 5)", "bzr (= 5)"));
376 assert!(is_dep_implied("bzr:any (>> 5)", "bzr:any (= 6)"));
377 }
378 }
379
380 mod is_relation_implied {
381 use debian_control::lossless::relations::Relations;
382
383 fn parse(s: &str) -> super::Entry {
384 let r: Relations = s.parse().unwrap();
385 let mut entries = r.entries();
386 let entry = entries.next().unwrap();
387 assert_eq!(entries.next(), None);
388 entry
389 }
390
391 fn is_relation_implied(inner: &str, outer: &str) -> bool {
392 super::is_relation_implied(&parse(inner), &parse(outer))
393 }
394
395 #[test]
396 fn test_unrelated() {
397 assert!(!is_relation_implied("bzr", "bar"));
398 assert!(!is_relation_implied("bzr (= 3)", "bar"));
399 assert!(!is_relation_implied("bzr (= 3) | foo", "bar"));
400 }
401
402 #[test]
403 fn test_too_old() {
404 assert!(!is_relation_implied("bzr (= 3)", "bzr"));
405 assert!(!is_relation_implied("bzr (= 3)", "bzr (= 2)"));
406 assert!(!is_relation_implied("bzr (= 3)", "bzr (>= 2)"));
407 }
408
409 #[test]
410 fn test_ors() {
411 assert!(!is_relation_implied("bzr (= 3)", "bzr | foo"));
412 assert!(is_relation_implied("bzr", "bzr | foo"));
413 assert!(is_relation_implied("bzr | foo", "bzr | foo"));
414 }
415
416 #[test]
417 fn test_implied() {
418 assert!(is_relation_implied("bzr (= 3)", "bzr (= 3)"));
419 assert!(is_relation_implied("bzr (>= 3)", "bzr (>= 4)"));
420 assert!(is_relation_implied("bzr (>= 4)", "bzr (>= 4)"));
421 assert!(is_relation_implied("bzr", "bzr"));
422 assert!(is_relation_implied("bzr | foo", "bzr"));
423 assert!(!is_relation_implied("bzr (= 3)", "bzr (>= 3)"));
424 assert!(is_relation_implied(
425 "python3:any | dh-sequence-python3",
426 "python3:any"
427 ));
428 assert!(is_relation_implied(
429 "python3:any | python3-dev:any | dh-sequence-python3",
430 "python3:any | python3-dev:any"
431 ));
432 }
433 }
434
435 #[test]
436 fn test_ensure_relation() {
437 let mut rels: Relations = "".parse().unwrap();
438 let rel = Relation::simple("foo");
439 ensure_relation(&mut rels, rel.into());
440 assert_eq!("foo", rels.to_string());
441 }
442
443 #[test]
444 fn test_ensure_relation_upgrade() {
445 let mut rels = "foo".parse().unwrap();
446 let newrel: Entry = Relation::new(
447 "foo",
448 Some((VersionConstraint::Equal, "1.0".parse().unwrap())),
449 )
450 .into();
451 ensure_relation(&mut rels, newrel);
452 assert_eq!("foo (= 1.0)", rels.to_string());
453 }
454
455 #[test]
456 fn test_ensure_relation_new() {
457 let mut rels = "bar (= 1.0)".parse().unwrap();
458 let newrel: Entry = Relation::new(
459 "foo",
460 Some((VersionConstraint::Equal, "2.0".parse().unwrap())),
461 )
462 .into();
463 ensure_relation(&mut rels, newrel);
464 assert_eq!("bar (= 1.0), foo (= 2.0)", rels.to_string());
465 }
466
467 #[test]
468 fn test_drops_obsolete() {
469 let mut rels = "bar (= 1.0), foo (>= 2.0), foo (>= 1.0)".parse().unwrap();
470 let newrel: Entry = Relation::new(
471 "foo",
472 Some((VersionConstraint::GreaterThanEqual, "3.0".parse().unwrap())),
473 )
474 .into();
475 ensure_relation(&mut rels, newrel);
476 assert_eq!("bar (= 1.0), foo (>= 3.0)", rels.to_string());
477 }
478
479 #[test]
480 fn test_ensure_relation_with_error() {
481 let mut rels = Relations::parse_relaxed("@cdbs@, debhelper (>= 9)", false).0;
482 let newrel: Entry = Relation::new("foo", None).into();
483
484 ensure_relation(&mut rels, newrel);
485 assert_eq!("@cdbs@, debhelper (>= 9), foo", rels.to_string());
486 }
487
488 #[test]
489 fn test_ensure_minimum_version() {
490 let mut rels = "".parse().unwrap();
491 ensure_minimum_version(&mut rels, "foo", &"1.0".parse().unwrap());
492 assert_eq!("foo (>= 1.0)", rels.to_string());
493 }
494
495 #[test]
496 fn test_ensure_minimum_version_upgrade() {
497 let mut rels = "foo (>= 1.0)".parse().unwrap();
498 ensure_minimum_version(&mut rels, "foo", &"2.0".parse().unwrap());
499 assert_eq!("foo (>= 2.0)", rels.to_string());
500 }
501
502 #[test]
503 fn test_ensure_minimum_version_upgrade_with_or() {
504 let mut rels = "foo (>= 1.0) | bar".parse().unwrap();
505 ensure_minimum_version(&mut rels, "foo", &"2.0".parse().unwrap());
506 assert_eq!("foo (>= 2.0)", rels.to_string());
507 }
508
509 #[test]
510 fn test_ensure_exact_version() {
511 let mut rels = "".parse().unwrap();
512 ensure_exact_version(&mut rels, "foo", &"1.0".parse().unwrap(), None);
513 assert_eq!("foo (= 1.0)", rels.to_string());
514 }
515
516 #[test]
517 fn test_ensure_exact_version_upgrade() {
518 let mut rels = "foo (= 1.0)".parse().unwrap();
519 ensure_exact_version(&mut rels, "foo", &"2.0".parse().unwrap(), None);
520 assert_eq!("foo (= 2.0)", rels.to_string());
521 }
522
523 #[test]
524 fn test_ensure_exact_version_upgrade_with_position() {
525 let mut rels = "foo (= 1.0)".parse().unwrap();
526 ensure_exact_version(&mut rels, "foo", &"2.0".parse().unwrap(), Some(0));
527 assert_eq!("foo (= 2.0)", rels.to_string());
528 }
529}