1use alloc::{
8 collections::{BTreeMap, BTreeSet},
9 format,
10 string::String,
11 vec,
12 vec::Vec,
13};
14
15use super::{Dependency, PackageId, PackageMetadata, Version};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum ResolverError {
24 PackageNotFound { name: String },
26 NoSatisfyingVersion {
28 package: String,
29 requirement: String,
30 },
31 VersionConflict {
33 package: String,
34 required: String,
35 existing: String,
36 },
37 MissingMetadata { package: String, version: String },
39 Unsatisfiable,
41 Conflict {
43 package: String,
44 conflicting: String,
45 },
46 NoProvider { virtual_package: String },
48}
49
50impl core::fmt::Display for ResolverError {
51 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52 match self {
53 Self::PackageNotFound { name } => write!(f, "Package not found: {}", name),
54 Self::NoSatisfyingVersion {
55 package,
56 requirement,
57 } => {
58 write!(
59 f,
60 "No suitable version found for {} (requirement: {})",
61 package, requirement
62 )
63 }
64 Self::VersionConflict {
65 package,
66 required,
67 existing,
68 } => {
69 write!(
70 f,
71 "Version conflict for {}: need {}, have {}",
72 package, required, existing
73 )
74 }
75 Self::MissingMetadata { package, version } => {
76 write!(f, "Missing metadata for {} {}", package, version)
77 }
78 Self::Unsatisfiable => {
79 write!(f, "No satisfying assignment found for dependencies")
80 }
81 Self::Conflict {
82 package,
83 conflicting,
84 } => {
85 write!(f, "Conflict: {} conflicts with {}", package, conflicting)
86 }
87 Self::NoProvider { virtual_package } => {
88 write!(f, "No provider for virtual package: {}", virtual_package)
89 }
90 }
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
100pub enum VersionReq {
101 Exact(Version),
103 AtLeast(Version),
105 AtMost(Version),
107 Range(Version, Version),
109 Caret(Version),
111 Tilde(Version),
113 Any,
115 Compound(Vec<VersionReq>),
117}
118
119impl VersionReq {
120 pub fn parse(s: &str) -> Self {
131 let trimmed = s.trim();
132 if trimmed == "*" || trimmed.is_empty() {
133 return VersionReq::Any;
134 }
135
136 if trimmed.contains(',') {
138 let parts: Vec<VersionReq> = trimmed
139 .split(',')
140 .map(|part| Self::parse_single(part.trim()))
141 .collect();
142 if parts.len() == 1 {
143 return parts.into_iter().next().unwrap_or(VersionReq::Any);
144 }
145 return VersionReq::Compound(parts);
146 }
147
148 Self::parse_single(trimmed)
149 }
150
151 fn parse_single(s: &str) -> Self {
153 let trimmed = s.trim();
154
155 if let Some(rest) = trimmed.strip_prefix('^') {
156 if let Some(v) = Self::parse_version_flexible(rest) {
157 return VersionReq::Caret(v);
158 }
159 }
160
161 if let Some(rest) = trimmed.strip_prefix('~') {
162 if let Some(v) = Self::parse_version_flexible(rest) {
163 return VersionReq::Tilde(v);
164 }
165 }
166
167 if let Some(rest) = trimmed.strip_prefix(">=") {
168 if let Some(v) = Self::parse_version(rest) {
169 return VersionReq::AtLeast(v);
170 }
171 }
172
173 if let Some(rest) = trimmed.strip_prefix("<=") {
174 if let Some(v) = Self::parse_version(rest) {
175 return VersionReq::AtMost(v);
176 }
177 }
178
179 if let Some(rest) = trimmed.strip_prefix('<') {
180 if let Some(v) = Self::parse_version(rest) {
182 return VersionReq::Range(Version::new(0, 0, 0), v);
183 }
184 }
185
186 if let Some(rest) = trimmed.strip_prefix('>') {
187 if let Some(v) = Self::parse_version(rest) {
189 let next = Version::new(v.major, v.minor, v.patch + 1);
190 return VersionReq::AtLeast(next);
191 }
192 }
193
194 if let Some(v) = Self::parse_version(trimmed) {
195 return VersionReq::Exact(v);
196 }
197
198 VersionReq::Any
199 }
200
201 fn parse_version(s: &str) -> Option<Version> {
203 let parts: Vec<&str> = s.trim().split('.').collect();
204 if parts.len() != 3 {
205 return None;
206 }
207
208 let major = parts[0].parse().ok()?;
209 let minor = parts[1].parse().ok()?;
210 let patch = parts[2].parse().ok()?;
211
212 Some(Version::new(major, minor, patch))
213 }
214
215 fn parse_version_flexible(s: &str) -> Option<Version> {
218 let parts: Vec<&str> = s.trim().split('.').collect();
219 if parts.is_empty() || parts.len() > 3 {
220 return None;
221 }
222
223 let major: u32 = parts[0].parse().ok()?;
224 let minor: u32 = parts.get(1).and_then(|p| p.parse().ok()).unwrap_or(0);
225 let patch: u32 = parts.get(2).and_then(|p| p.parse().ok()).unwrap_or(0);
226
227 Some(Version::new(major, minor, patch))
228 }
229
230 pub fn satisfies(&self, version: &Version) -> bool {
232 match self {
233 VersionReq::Exact(v) => version == v,
234 VersionReq::AtLeast(v) => version >= v,
235 VersionReq::AtMost(v) => version <= v,
236 VersionReq::Range(min, max) => version >= min && version < max,
237 VersionReq::Caret(v) => satisfies_caret(version, v),
238 VersionReq::Tilde(v) => satisfies_tilde(version, v),
239 VersionReq::Any => true,
240 VersionReq::Compound(reqs) => reqs.iter().all(|r| r.satisfies(version)),
241 }
242 }
243}
244
245fn satisfies_caret(version: &Version, base: &Version) -> bool {
251 if version < base {
252 return false;
253 }
254 if base.major > 0 {
255 version.major == base.major
256 } else if base.minor > 0 {
257 version.major == 0 && version.minor == base.minor
258 } else {
259 version.major == 0 && version.minor == 0 && version.patch == base.patch
260 }
261}
262
263fn satisfies_tilde(version: &Version, base: &Version) -> bool {
267 version >= base && version.major == base.major && version.minor == base.minor
268}
269
270pub fn satisfies_range(version: &Version, range_expr: &str) -> bool {
274 VersionReq::parse(range_expr).satisfies(version)
275}
276
277#[derive(Debug, Clone, PartialEq, Eq)]
284struct Literal {
285 var_index: usize,
287 positive: bool,
289}
290
291impl Literal {
292 fn new(var_index: usize, positive: bool) -> Self {
293 Self {
294 var_index,
295 positive,
296 }
297 }
298}
299
300#[derive(Debug, Clone)]
302struct SatClause {
303 literals: Vec<Literal>,
304}
305
306impl SatClause {
307 fn new(literals: Vec<Literal>) -> Self {
308 Self { literals }
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq)]
314enum Assignment {
315 True,
317 False,
319 Unassigned,
321}
322
323struct SatSolver {
325 num_vars: usize,
327 clauses: Vec<SatClause>,
329 assignments: Vec<Assignment>,
331}
332
333impl SatSolver {
334 fn new(num_vars: usize) -> Self {
335 Self {
336 num_vars,
337 clauses: Vec::new(),
338 assignments: vec![Assignment::Unassigned; num_vars + 1],
339 }
340 }
341
342 fn add_clause(&mut self, clause: SatClause) {
343 self.clauses.push(clause);
344 }
345
346 fn solve(&mut self) -> Option<Vec<bool>> {
350 self.assignments = vec![Assignment::Unassigned; self.num_vars + 1];
352 if self.dpll() {
353 let result = (1..=self.num_vars)
354 .map(|i| self.assignments[i] == Assignment::True)
355 .collect();
356 Some(result)
357 } else {
358 None
359 }
360 }
361
362 fn dpll(&mut self) -> bool {
364 if !self.unit_propagate() {
366 return false;
367 }
368
369 self.pure_literal_eliminate();
371
372 if self.all_clauses_satisfied() {
374 return true;
375 }
376
377 if self.has_empty_clause() {
379 return false;
380 }
381
382 let var = match self.choose_variable() {
384 Some(v) => v,
385 None => return self.all_clauses_satisfied(),
386 };
387
388 let saved = self.assignments.clone();
390 self.assignments[var] = Assignment::True;
391 if self.dpll() {
392 return true;
393 }
394
395 self.assignments = saved;
397 self.assignments[var] = Assignment::False;
398 self.dpll()
399 }
400
401 fn unit_propagate(&mut self) -> bool {
407 let mut changed = true;
408 while changed {
409 changed = false;
410 for clause_idx in 0..self.clauses.len() {
411 let mut unassigned_lit: Option<Literal> = None;
412 let mut unassigned_count = 0;
413 let mut clause_satisfied = false;
414
415 for lit in &self.clauses[clause_idx].literals {
416 match self.assignments[lit.var_index] {
417 Assignment::True => {
418 if lit.positive {
419 clause_satisfied = true;
420 break;
421 }
422 }
423 Assignment::False => {
424 if !lit.positive {
425 clause_satisfied = true;
426 break;
427 }
428 }
429 Assignment::Unassigned => {
430 unassigned_count += 1;
431 unassigned_lit = Some(lit.clone());
432 }
433 }
434 }
435
436 if clause_satisfied {
437 continue;
438 }
439
440 if unassigned_count == 0 {
441 return false;
443 }
444
445 if unassigned_count == 1 {
446 if let Some(lit) = unassigned_lit {
448 self.assignments[lit.var_index] = if lit.positive {
449 Assignment::True
450 } else {
451 Assignment::False
452 };
453 changed = true;
454 }
455 }
456 }
457 }
458 true
459 }
460
461 fn pure_literal_eliminate(&mut self) {
464 let mut appears_pos = vec![false; self.num_vars + 1];
465 let mut appears_neg = vec![false; self.num_vars + 1];
466
467 for clause in &self.clauses {
468 if self.clause_satisfied(clause) {
470 continue;
471 }
472 for lit in &clause.literals {
473 if self.assignments[lit.var_index] == Assignment::Unassigned {
474 if lit.positive {
475 appears_pos[lit.var_index] = true;
476 } else {
477 appears_neg[lit.var_index] = true;
478 }
479 }
480 }
481 }
482
483 for var in 1..=self.num_vars {
484 if self.assignments[var] != Assignment::Unassigned {
485 continue;
486 }
487 if appears_pos[var] && !appears_neg[var] {
488 self.assignments[var] = Assignment::True;
489 } else if !appears_pos[var] && appears_neg[var] {
490 self.assignments[var] = Assignment::False;
491 }
492 }
493 }
494
495 fn clause_satisfied(&self, clause: &SatClause) -> bool {
497 clause
498 .literals
499 .iter()
500 .any(|lit| match self.assignments[lit.var_index] {
501 Assignment::True => lit.positive,
502 Assignment::False => !lit.positive,
503 Assignment::Unassigned => false,
504 })
505 }
506
507 fn all_clauses_satisfied(&self) -> bool {
509 self.clauses.iter().all(|c| self.clause_satisfied(c))
510 }
511
512 fn has_empty_clause(&self) -> bool {
514 for clause in &self.clauses {
515 let all_falsified =
516 clause
517 .literals
518 .iter()
519 .all(|lit| match self.assignments[lit.var_index] {
520 Assignment::True => !lit.positive,
521 Assignment::False => lit.positive,
522 Assignment::Unassigned => false,
523 });
524 if all_falsified && !clause.literals.is_empty() {
525 return true;
526 }
527 }
528 false
529 }
530
531 fn choose_variable(&self) -> Option<usize> {
533 (1..=self.num_vars).find(|&i| self.assignments[i] == Assignment::Unassigned)
534 }
535}
536
537#[derive(Debug, Clone)]
547#[allow(dead_code)] struct Candidate {
549 package_id: PackageId,
550 version: Version,
551 dependencies: Vec<Dependency>,
552 conflicts: Vec<PackageId>,
553 provides: Vec<PackageId>,
555}
556
557pub struct DependencyResolver {
569 available: BTreeMap<PackageId, Vec<Version>>,
571 metadata: BTreeMap<(PackageId, Version), Candidate>,
573 virtual_providers: BTreeMap<PackageId, Vec<(PackageId, Version)>>,
575}
576
577impl DependencyResolver {
578 pub fn new() -> Self {
579 Self {
580 available: BTreeMap::new(),
581 metadata: BTreeMap::new(),
582 virtual_providers: BTreeMap::new(),
583 }
584 }
585
586 pub fn register_package(
588 &mut self,
589 package_id: PackageId,
590 version: Version,
591 dependencies: Vec<Dependency>,
592 conflicts: Vec<PackageId>,
593 ) {
594 self.register_package_full(package_id, version, dependencies, conflicts, Vec::new());
595 }
596
597 pub fn register_package_full(
599 &mut self,
600 package_id: PackageId,
601 version: Version,
602 dependencies: Vec<Dependency>,
603 conflicts: Vec<PackageId>,
604 provides: Vec<PackageId>,
605 ) {
606 self.available
608 .entry(package_id.clone())
609 .or_insert_with(Vec::new)
610 .push(version.clone());
611
612 if let Some(versions) = self.available.get_mut(&package_id) {
614 versions.sort_by(|a, b| b.cmp(a));
615 }
616
617 for virt in &provides {
619 self.virtual_providers
620 .entry(virt.clone())
621 .or_insert_with(Vec::new)
622 .push((package_id.clone(), version.clone()));
623 }
624
625 self.metadata.insert(
627 (package_id.clone(), version.clone()),
628 Candidate {
629 package_id,
630 version,
631 dependencies,
632 conflicts,
633 provides,
634 },
635 );
636 }
637
638 pub fn resolve(
643 &self,
644 dependencies: &[Dependency],
645 ) -> Result<Vec<(PackageId, Version)>, ResolverError> {
646 match self.resolve_greedy(dependencies) {
648 Ok(solution) => Ok(solution),
649 Err(_) => {
650 self.resolve_sat(dependencies)
652 }
653 }
654 }
655
656 fn resolve_greedy(
658 &self,
659 dependencies: &[Dependency],
660 ) -> Result<Vec<(PackageId, Version)>, ResolverError> {
661 let mut solution = BTreeMap::new();
662 let mut visited = BTreeSet::new();
663
664 for dep in dependencies {
665 self.resolve_dependency(dep, &mut solution, &mut visited)?;
666 }
667
668 self.check_conflicts(&solution)?;
669
670 let mut result: Vec<(PackageId, Version)> = solution.into_iter().collect();
671 result.reverse();
672
673 Ok(result)
674 }
675
676 fn resolve_sat(
681 &self,
682 dependencies: &[Dependency],
683 ) -> Result<Vec<(PackageId, Version)>, ResolverError> {
684 let mut relevant = BTreeSet::new();
686 self.collect_relevant_packages(dependencies, &mut relevant)?;
687
688 if relevant.is_empty() {
689 return Ok(Vec::new());
690 }
691
692 let mut var_map: BTreeMap<(PackageId, Version), usize> = BTreeMap::new();
694 let mut var_list: Vec<(PackageId, Version)> = Vec::new();
695 let mut idx = 1usize;
696 for (pkg, ver) in &relevant {
697 var_map.insert((pkg.clone(), ver.clone()), idx);
698 var_list.push((pkg.clone(), ver.clone()));
699 idx += 1;
700 }
701 let num_vars = var_list.len();
702
703 let mut solver = SatSolver::new(num_vars);
704
705 self.encode_dependencies(dependencies, &var_map, &relevant, &mut solver)?;
707 self.encode_at_most_one_version(&var_map, &mut solver);
708 self.encode_conflicts(&var_map, &mut solver);
709
710 let solution = solver.solve().ok_or(ResolverError::Unsatisfiable)?;
712
713 self.decode_solution(&solution, &var_list)
715 }
716
717 fn collect_relevant_packages(
719 &self,
720 dependencies: &[Dependency],
721 relevant: &mut BTreeSet<(PackageId, Version)>,
722 ) -> Result<(), ResolverError> {
723 let mut work_queue: Vec<PackageId> = dependencies.iter().map(|d| d.name.clone()).collect();
724 let mut visited_pkgs = BTreeSet::new();
725
726 while let Some(pkg) = work_queue.pop() {
727 if visited_pkgs.contains(&pkg) {
728 continue;
729 }
730 visited_pkgs.insert(pkg.clone());
731
732 let real_pkg = if self.available.contains_key(&pkg) {
734 pkg.clone()
735 } else if let Some(providers) = self.virtual_providers.get(&pkg) {
736 if let Some((real, _)) = providers.first() {
737 real.clone()
738 } else {
739 return Err(ResolverError::NoProvider {
740 virtual_package: pkg,
741 });
742 }
743 } else {
744 return Err(ResolverError::PackageNotFound { name: pkg });
745 };
746
747 if let Some(versions) = self.available.get(&real_pkg) {
748 for ver in versions {
749 relevant.insert((real_pkg.clone(), ver.clone()));
750 if let Some(candidate) = self.metadata.get(&(real_pkg.clone(), ver.clone())) {
752 for dep in &candidate.dependencies {
753 work_queue.push(dep.name.clone());
754 }
755 }
756 }
757 }
758 }
759
760 Ok(())
761 }
762
763 fn encode_dependencies(
768 &self,
769 dependencies: &[Dependency],
770 var_map: &BTreeMap<(PackageId, Version), usize>,
771 relevant: &BTreeSet<(PackageId, Version)>,
772 solver: &mut SatSolver,
773 ) -> Result<(), ResolverError> {
774 for dep in dependencies {
776 let clause = self.build_dependency_clause(dep, var_map)?;
777 if clause.literals.is_empty() {
778 return Err(ResolverError::NoSatisfyingVersion {
779 package: dep.name.clone(),
780 requirement: dep.version_req.clone(),
781 });
782 }
783 solver.add_clause(clause);
784 }
785
786 for (pkg, ver) in relevant {
790 if let Some(&var_idx) = var_map.get(&(pkg.clone(), ver.clone())) {
791 if let Some(candidate) = self.metadata.get(&(pkg.clone(), ver.clone())) {
792 for dep in &candidate.dependencies {
793 let mut lits = vec![Literal::new(var_idx, false)];
794
795 let dep_pkg = self.resolve_virtual(&dep.name);
796 let req = VersionReq::parse(&dep.version_req);
797
798 if let Some(versions) = self.available.get(&dep_pkg) {
799 for dep_ver in versions {
800 if req.satisfies(dep_ver) {
801 if let Some(&dep_var) =
802 var_map.get(&(dep_pkg.clone(), dep_ver.clone()))
803 {
804 lits.push(Literal::new(dep_var, true));
805 }
806 }
807 }
808 }
809
810 solver.add_clause(SatClause::new(lits));
813 }
814 }
815 }
816 }
817
818 Ok(())
819 }
820
821 fn encode_at_most_one_version(
826 &self,
827 var_map: &BTreeMap<(PackageId, Version), usize>,
828 solver: &mut SatSolver,
829 ) {
830 for (pkg, versions) in &self.available {
831 let vars: Vec<usize> = versions
832 .iter()
833 .filter_map(|v| var_map.get(&(pkg.clone(), v.clone())).copied())
834 .collect();
835
836 for i in 0..vars.len() {
838 for j in (i + 1)..vars.len() {
839 solver.add_clause(SatClause::new(vec![
840 Literal::new(vars[i], false),
841 Literal::new(vars[j], false),
842 ]));
843 }
844 }
845 }
846 }
847
848 fn encode_conflicts(
853 &self,
854 var_map: &BTreeMap<(PackageId, Version), usize>,
855 solver: &mut SatSolver,
856 ) {
857 for ((pkg, ver), candidate) in &self.metadata {
858 if let Some(&pkg_var) = var_map.get(&(pkg.clone(), ver.clone())) {
859 for conflict in &candidate.conflicts {
860 let conflict_pkg = self.resolve_virtual(conflict);
861 if let Some(conflict_versions) = self.available.get(&conflict_pkg) {
862 for cv in conflict_versions {
863 if let Some(&cv_var) = var_map.get(&(conflict_pkg.clone(), cv.clone()))
864 {
865 solver.add_clause(SatClause::new(vec![
866 Literal::new(pkg_var, false),
867 Literal::new(cv_var, false),
868 ]));
869 }
870 }
871 }
872 }
873 }
874 }
875 }
876
877 fn build_dependency_clause(
879 &self,
880 dep: &Dependency,
881 var_map: &BTreeMap<(PackageId, Version), usize>,
882 ) -> Result<SatClause, ResolverError> {
883 let mut lits = Vec::new();
884 let req = VersionReq::parse(&dep.version_req);
885
886 let pkg_name = self.resolve_virtual(&dep.name);
887 if let Some(versions) = self.available.get(&pkg_name) {
888 for ver in versions {
889 if req.satisfies(ver) {
890 if let Some(&var_idx) = var_map.get(&(pkg_name.clone(), ver.clone())) {
891 lits.push(Literal::new(var_idx, true));
892 }
893 }
894 }
895 }
896
897 Ok(SatClause::new(lits))
898 }
899
900 fn decode_solution(
902 &self,
903 solution: &[bool],
904 var_list: &[(PackageId, Version)],
905 ) -> Result<Vec<(PackageId, Version)>, ResolverError> {
906 let mut result = Vec::new();
907
908 for (i, selected) in solution.iter().enumerate() {
909 if *selected {
910 result.push(var_list[i].clone());
911 }
912 }
913
914 let sorted = self.topological_sort(&result)?;
916 Ok(sorted)
917 }
918
919 fn topological_sort(
921 &self,
922 packages: &[(PackageId, Version)],
923 ) -> Result<Vec<(PackageId, Version)>, ResolverError> {
924 let pkg_set: BTreeSet<PackageId> = packages.iter().map(|(p, _)| p.clone()).collect();
925
926 let mut in_degree: BTreeMap<PackageId, usize> = BTreeMap::new();
928 let mut adj: BTreeMap<PackageId, Vec<PackageId>> = BTreeMap::new();
929
930 for (pkg, ver) in packages {
931 in_degree.entry(pkg.clone()).or_insert(0);
932 adj.entry(pkg.clone()).or_insert_with(Vec::new);
933
934 if let Some(candidate) = self.metadata.get(&(pkg.clone(), ver.clone())) {
935 for dep in &candidate.dependencies {
936 let dep_pkg = self.resolve_virtual(&dep.name);
937 if pkg_set.contains(&dep_pkg) {
938 adj.entry(dep_pkg)
939 .or_insert_with(Vec::new)
940 .push(pkg.clone());
941 *in_degree.entry(pkg.clone()).or_insert(0) += 1;
942 }
943 }
944 }
945 }
946
947 let mut queue: Vec<PackageId> = in_degree
949 .iter()
950 .filter(|(_, °)| deg == 0)
951 .map(|(pkg, _)| pkg.clone())
952 .collect();
953 queue.sort();
954
955 let mut order = Vec::new();
956 while let Some(pkg) = queue.pop() {
957 order.push(pkg.clone());
958 if let Some(dependents) = adj.get(&pkg) {
959 for dep in dependents {
960 if let Some(deg) = in_degree.get_mut(dep) {
961 *deg = deg.saturating_sub(1);
962 if *deg == 0 {
963 queue.push(dep.clone());
964 queue.sort();
965 }
966 }
967 }
968 }
969 }
970
971 let pkg_ver_map: BTreeMap<PackageId, Version> = packages
973 .iter()
974 .map(|(p, v)| (p.clone(), v.clone()))
975 .collect();
976 let result: Vec<(PackageId, Version)> = order
977 .into_iter()
978 .filter_map(|p| pkg_ver_map.get(&p).map(|v| (p, v.clone())))
979 .collect();
980
981 Ok(result)
982 }
983
984 fn resolve_virtual(&self, name: &PackageId) -> PackageId {
986 if self.available.contains_key(name) {
987 return name.clone();
988 }
989 if let Some(providers) = self.virtual_providers.get(name) {
990 if let Some((real, _)) = providers.first() {
991 return real.clone();
992 }
993 }
994 name.clone()
995 }
996
997 pub fn resolve_upgrade(
1006 &self,
1007 installed: &BTreeMap<PackageId, Version>,
1008 upgrade_targets: &[PackageId],
1009 ) -> Result<Vec<(PackageId, Version)>, ResolverError> {
1010 let targets: Vec<PackageId> = if upgrade_targets.is_empty() {
1012 installed.keys().cloned().collect()
1013 } else {
1014 upgrade_targets.to_vec()
1015 };
1016
1017 let mut deps = Vec::new();
1020
1021 for target in &targets {
1022 deps.push(Dependency {
1023 name: target.clone(),
1024 version_req: String::from("*"),
1025 });
1026 }
1027
1028 for (pkg, ver) in installed {
1030 if !targets.contains(pkg) {
1031 deps.push(Dependency {
1032 name: pkg.clone(),
1033 version_req: Self::version_to_string(ver),
1034 });
1035 }
1036 }
1037
1038 let solution = self.resolve(&deps)?;
1040
1041 let mut upgrades = Vec::new();
1043 for (pkg, new_ver) in &solution {
1044 match installed.get(pkg) {
1045 Some(old_ver) if new_ver > old_ver => {
1046 upgrades.push((pkg.clone(), new_ver.clone()));
1047 }
1048 None => {
1049 upgrades.push((pkg.clone(), new_ver.clone()));
1051 }
1052 _ => {} }
1054 }
1055
1056 Ok(upgrades)
1057 }
1058
1059 fn resolve_dependency(
1061 &self,
1062 dep: &Dependency,
1063 solution: &mut BTreeMap<PackageId, Version>,
1064 visited: &mut BTreeSet<PackageId>,
1065 ) -> Result<(), ResolverError> {
1066 if visited.contains(&dep.name) {
1068 return Ok(()); }
1070
1071 let real_name = self.resolve_virtual(&dep.name);
1073
1074 if solution.contains_key(&real_name) {
1076 let existing_version = &solution[&real_name];
1077 let req = VersionReq::parse(&dep.version_req);
1078
1079 if !req.satisfies(existing_version) {
1080 return Err(ResolverError::VersionConflict {
1081 package: dep.name.clone(),
1082 required: dep.version_req.clone(),
1083 existing: Self::version_to_string(existing_version),
1084 });
1085 }
1086 return Ok(());
1087 }
1088
1089 visited.insert(real_name.clone());
1090
1091 let version = self.find_suitable_version(&real_name, &dep.version_req)?;
1093
1094 let candidate = self
1096 .metadata
1097 .get(&(real_name.clone(), version.clone()))
1098 .ok_or_else(|| ResolverError::MissingMetadata {
1099 package: real_name.clone(),
1100 version: Self::version_to_string(&version),
1101 })?;
1102
1103 for trans_dep in &candidate.dependencies {
1105 self.resolve_dependency(trans_dep, solution, visited)?;
1106 }
1107
1108 solution.insert(real_name.clone(), version);
1110
1111 visited.remove(&real_name);
1112
1113 Ok(())
1114 }
1115
1116 fn find_suitable_version(
1118 &self,
1119 package_id: &PackageId,
1120 version_req_str: &str,
1121 ) -> Result<Version, ResolverError> {
1122 let versions =
1123 self.available
1124 .get(package_id)
1125 .ok_or_else(|| ResolverError::PackageNotFound {
1126 name: package_id.clone(),
1127 })?;
1128
1129 let req = VersionReq::parse(version_req_str);
1130
1131 for version in versions {
1133 if req.satisfies(version) {
1134 return Ok(version.clone());
1135 }
1136 }
1137
1138 Err(ResolverError::NoSatisfyingVersion {
1139 package: package_id.clone(),
1140 requirement: String::from(version_req_str),
1141 })
1142 }
1143
1144 fn check_conflicts(
1146 &self,
1147 solution: &BTreeMap<PackageId, Version>,
1148 ) -> Result<(), ResolverError> {
1149 for (pkg_id, version) in solution {
1150 if let Some(candidate) = self.metadata.get(&(pkg_id.clone(), version.clone())) {
1151 for conflict in &candidate.conflicts {
1152 let resolved = self.resolve_virtual(conflict);
1153 if solution.contains_key(&resolved) {
1154 return Err(ResolverError::Conflict {
1155 package: format!("{} {}", pkg_id, Self::version_to_string(version)),
1156 conflicting: conflict.clone(),
1157 });
1158 }
1159 }
1160 }
1161 }
1162 Ok(())
1163 }
1164
1165 fn version_to_string(version: &Version) -> String {
1167 alloc::format!("{}.{}.{}", version.major, version.minor, version.patch)
1168 }
1169
1170 pub fn latest_version(&self, package_id: &str) -> Option<Version> {
1172 self.available
1173 .get(package_id)
1174 .and_then(|versions| versions.first().cloned())
1175 }
1176
1177 pub fn search(&self, query: &str) -> Vec<(PackageId, Version)> {
1181 let query_lower = query.to_lowercase();
1182 self.available
1183 .iter()
1184 .filter(|(name, _)| name.to_lowercase().contains(&query_lower))
1185 .filter_map(|(name, versions)| versions.first().map(|v| (name.clone(), v.clone())))
1186 .collect()
1187 }
1188
1189 pub fn get_package_metadata(&self, package_id: &str) -> Option<PackageMetadata> {
1191 let versions = self.available.get(package_id)?;
1192 let latest = versions.first()?;
1193 let candidate = self
1194 .metadata
1195 .get(&(String::from(package_id), latest.clone()))?;
1196
1197 Some(PackageMetadata {
1198 name: candidate.package_id.clone(),
1199 version: candidate.version.clone(),
1200 author: String::from("unknown"),
1201 description: String::new(),
1202 license: String::new(),
1203 dependencies: candidate.dependencies.clone(),
1204 conflicts: candidate.conflicts.clone(),
1205 })
1206 }
1207}
1208
1209impl Default for DependencyResolver {
1210 fn default() -> Self {
1211 Self::new()
1212 }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217 use super::*;
1218
1219 #[test]
1220 fn test_version_req_parsing() {
1221 let req = VersionReq::parse("1.2.3");
1222 assert!(matches!(req, VersionReq::Exact(_)));
1223
1224 let req = VersionReq::parse(">=1.0.0");
1225 assert!(matches!(req, VersionReq::AtLeast(_)));
1226
1227 let req = VersionReq::parse("*");
1228 assert!(matches!(req, VersionReq::Any));
1229 }
1230
1231 #[test]
1232 fn test_version_satisfies() {
1233 let v123 = Version::new(1, 2, 3);
1234 let v100 = Version::new(1, 0, 0);
1235
1236 let req = VersionReq::Exact(v123.clone());
1237 assert!(req.satisfies(&v123));
1238 assert!(!req.satisfies(&v100));
1239
1240 let req = VersionReq::AtLeast(v100.clone());
1241 assert!(req.satisfies(&v123));
1242 assert!(req.satisfies(&v100));
1243 }
1244
1245 #[test]
1246 fn test_simple_resolution() {
1247 let mut resolver = DependencyResolver::new();
1248
1249 resolver.register_package(String::from("pkg-a"), Version::new(1, 0, 0), vec![], vec![]);
1250
1251 let deps = vec![Dependency {
1252 name: String::from("pkg-a"),
1253 version_req: String::from("1.0.0"),
1254 }];
1255
1256 let result = resolver.resolve(&deps);
1257 assert!(result.is_ok());
1258 let packages = result.unwrap();
1259 assert_eq!(packages.len(), 1);
1260 }
1261
1262 #[test]
1263 fn test_caret_version_req() {
1264 let req = VersionReq::parse("^1.2.3");
1265 assert!(req.satisfies(&Version::new(1, 2, 3)));
1266 assert!(req.satisfies(&Version::new(1, 9, 0)));
1267 assert!(!req.satisfies(&Version::new(2, 0, 0)));
1268 assert!(!req.satisfies(&Version::new(1, 2, 2)));
1269 }
1270
1271 #[test]
1272 fn test_caret_zero_major() {
1273 let req = VersionReq::parse("^0.2.3");
1274 assert!(req.satisfies(&Version::new(0, 2, 3)));
1275 assert!(req.satisfies(&Version::new(0, 2, 9)));
1276 assert!(!req.satisfies(&Version::new(0, 3, 0)));
1277 assert!(!req.satisfies(&Version::new(1, 0, 0)));
1278 }
1279
1280 #[test]
1281 fn test_tilde_version_req() {
1282 let req = VersionReq::parse("~1.2.3");
1283 assert!(req.satisfies(&Version::new(1, 2, 3)));
1284 assert!(req.satisfies(&Version::new(1, 2, 9)));
1285 assert!(!req.satisfies(&Version::new(1, 3, 0)));
1286 assert!(!req.satisfies(&Version::new(2, 0, 0)));
1287 }
1288
1289 #[test]
1290 fn test_compound_version_req() {
1291 let req = VersionReq::parse(">=1.2.0, <2.0.0");
1292 assert!(req.satisfies(&Version::new(1, 2, 0)));
1293 assert!(req.satisfies(&Version::new(1, 9, 9)));
1294 assert!(!req.satisfies(&Version::new(2, 0, 0)));
1295 assert!(!req.satisfies(&Version::new(1, 1, 9)));
1296 }
1297
1298 #[test]
1299 fn test_satisfies_range_function() {
1300 assert!(satisfies_range(&Version::new(1, 5, 0), "^1.2"));
1301 assert!(!satisfies_range(&Version::new(2, 0, 0), "^1.2"));
1302 assert!(satisfies_range(&Version::new(1, 2, 5), "~1.2.3"));
1303 }
1304
1305 #[test]
1306 fn test_conflict_detection() {
1307 let mut resolver = DependencyResolver::new();
1308
1309 resolver.register_package(
1310 String::from("pkg-a"),
1311 Version::new(1, 0, 0),
1312 vec![],
1313 vec![String::from("pkg-b")],
1314 );
1315 resolver.register_package(String::from("pkg-b"), Version::new(1, 0, 0), vec![], vec![]);
1316
1317 let deps = vec![
1318 Dependency {
1319 name: String::from("pkg-a"),
1320 version_req: String::from("*"),
1321 },
1322 Dependency {
1323 name: String::from("pkg-b"),
1324 version_req: String::from("*"),
1325 },
1326 ];
1327
1328 let result = resolver.resolve(&deps);
1329 assert!(result.is_err());
1330 }
1331
1332 #[test]
1333 fn test_transitive_dependencies() {
1334 let mut resolver = DependencyResolver::new();
1335
1336 resolver.register_package(
1337 String::from("app"),
1338 Version::new(1, 0, 0),
1339 vec![Dependency {
1340 name: String::from("lib-a"),
1341 version_req: String::from(">=1.0.0"),
1342 }],
1343 vec![],
1344 );
1345 resolver.register_package(
1346 String::from("lib-a"),
1347 Version::new(1, 2, 0),
1348 vec![Dependency {
1349 name: String::from("lib-b"),
1350 version_req: String::from("^1.0"),
1351 }],
1352 vec![],
1353 );
1354 resolver.register_package(String::from("lib-b"), Version::new(1, 1, 0), vec![], vec![]);
1355
1356 let deps = vec![Dependency {
1357 name: String::from("app"),
1358 version_req: String::from("*"),
1359 }];
1360
1361 let result = resolver.resolve(&deps);
1362 assert!(result.is_ok());
1363 let packages = result.unwrap();
1364 assert_eq!(packages.len(), 3);
1365 }
1366
1367 #[test]
1368 fn test_virtual_package_provides() {
1369 let mut resolver = DependencyResolver::new();
1370
1371 resolver.register_package_full(
1373 String::from("postfix"),
1374 Version::new(3, 5, 0),
1375 vec![],
1376 vec![],
1377 vec![String::from("mail-server")],
1378 );
1379
1380 resolver.register_package(
1382 String::from("webapp"),
1383 Version::new(1, 0, 0),
1384 vec![Dependency {
1385 name: String::from("mail-server"),
1386 version_req: String::from("*"),
1387 }],
1388 vec![],
1389 );
1390
1391 let deps = vec![Dependency {
1392 name: String::from("webapp"),
1393 version_req: String::from("*"),
1394 }];
1395
1396 let result = resolver.resolve(&deps);
1397 assert!(result.is_ok());
1398 let packages = result.unwrap();
1399 assert_eq!(packages.len(), 2);
1401 assert!(packages.iter().any(|(p, _)| p == "postfix"));
1402 }
1403
1404 #[test]
1405 fn test_resolve_upgrade() {
1406 let mut resolver = DependencyResolver::new();
1407
1408 resolver.register_package(String::from("pkg-a"), Version::new(1, 0, 0), vec![], vec![]);
1409 resolver.register_package(String::from("pkg-a"), Version::new(2, 0, 0), vec![], vec![]);
1410 resolver.register_package(String::from("pkg-b"), Version::new(1, 0, 0), vec![], vec![]);
1411
1412 let mut installed = BTreeMap::new();
1413 installed.insert(String::from("pkg-a"), Version::new(1, 0, 0));
1414 installed.insert(String::from("pkg-b"), Version::new(1, 0, 0));
1415
1416 let upgrades = resolver
1417 .resolve_upgrade(&installed, &[String::from("pkg-a")])
1418 .unwrap();
1419
1420 assert_eq!(upgrades.len(), 1);
1421 assert_eq!(upgrades[0].0, "pkg-a");
1422 assert_eq!(upgrades[0].1, Version::new(2, 0, 0));
1423 }
1424
1425 #[test]
1426 fn test_sat_conflict_resolution() {
1427 let mut resolver = DependencyResolver::new();
1428
1429 resolver.register_package(
1431 String::from("pkg-a"),
1432 Version::new(1, 0, 0),
1433 vec![Dependency {
1434 name: String::from("lib"),
1435 version_req: String::from("1.0.0"),
1436 }],
1437 vec![],
1438 );
1439 resolver.register_package(
1441 String::from("pkg-a"),
1442 Version::new(2, 0, 0),
1443 vec![Dependency {
1444 name: String::from("lib"),
1445 version_req: String::from("2.0.0"),
1446 }],
1447 vec![],
1448 );
1449 resolver.register_package(String::from("lib"), Version::new(1, 0, 0), vec![], vec![]);
1450 resolver.register_package(String::from("lib"), Version::new(2, 0, 0), vec![], vec![]);
1451
1452 let deps = vec![Dependency {
1455 name: String::from("pkg-a"),
1456 version_req: String::from("*"),
1457 }];
1458
1459 let result = resolver.resolve(&deps);
1460 assert!(result.is_ok());
1461 let packages = result.unwrap();
1462 assert_eq!(packages.len(), 2);
1463
1464 let pkg_a_ver = packages.iter().find(|(p, _)| p == "pkg-a").unwrap();
1466 let lib_ver = packages.iter().find(|(p, _)| p == "lib").unwrap();
1467 assert_eq!(pkg_a_ver.1.major, lib_ver.1.major);
1468 }
1469
1470 #[test]
1471 fn test_flexible_version_parsing() {
1472 let req = VersionReq::parse("^1");
1474 assert!(req.satisfies(&Version::new(1, 5, 0)));
1475 assert!(!req.satisfies(&Version::new(2, 0, 0)));
1476
1477 let req = VersionReq::parse("~1.2");
1479 assert!(req.satisfies(&Version::new(1, 2, 5)));
1480 assert!(!req.satisfies(&Version::new(1, 3, 0)));
1481 }
1482}