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