1use crate::{SolverConfig, SolverStats};
4use rez_next_common::RezCoreError;
5use rez_next_package::{Package, Requirement, VersionConstraint};
6use rez_next_repository::simple_repository::{PackageRepository, RepositoryManager};
7use rez_next_version::Version;
8use std::collections::{HashMap, HashSet, VecDeque};
9use std::sync::Arc;
10
11pub struct DependencyResolver {
13 repository_manager: Arc<RepositoryManager>,
15
16 config: SolverConfig,
18
19 stats: SolverStats,
21
22 package_cache: HashMap<String, Vec<Arc<Package>>>,
24}
25
26#[derive(Debug, Clone)]
28pub struct ResolutionResult {
29 pub resolved_packages: Vec<ResolvedPackageInfo>,
31
32 pub failed_requirements: Vec<Requirement>,
34
35 pub conflicts: Vec<ResolutionConflict>,
37
38 pub stats: ResolutionStats,
40}
41
42#[derive(Debug, Clone)]
44pub struct ResolvedPackageInfo {
45 pub package: Arc<Package>,
47
48 pub variant_index: Option<usize>,
50
51 pub requested: bool,
53
54 pub required_by: Vec<String>,
56
57 pub satisfying_requirement: Option<Requirement>,
59}
60
61#[derive(Debug, Clone, serde::Serialize)]
63pub struct ResolutionConflict {
64 pub package_name: String,
66
67 pub conflicting_requirements: Vec<Requirement>,
69
70 pub source_packages: Vec<String>,
72}
73
74#[derive(Debug, Clone, serde::Serialize)]
76pub struct ResolutionStats {
77 pub packages_considered: usize,
79
80 pub variants_evaluated: usize,
82
83 pub resolution_time_ms: u64,
85
86 pub conflicts_encountered: usize,
88
89 pub backtrack_steps: usize,
91}
92
93impl DependencyResolver {
94 pub fn new(repository_manager: Arc<RepositoryManager>, config: SolverConfig) -> Self {
96 Self {
97 repository_manager,
98 config,
99 stats: SolverStats::default(),
100 package_cache: HashMap::new(),
101 }
102 }
103
104 pub async fn resolve(
106 &mut self,
107 requirements: Vec<Requirement>,
108 ) -> Result<ResolutionResult, RezCoreError> {
109 let start_time = std::time::Instant::now();
110
111 let mut resolution_state = ResolutionState::new(requirements.clone());
113
114 let result = self.resolve_recursive(&mut resolution_state).await?;
116
117 let resolution_time = start_time.elapsed().as_millis() as u64;
119 let stats = ResolutionStats {
120 packages_considered: resolution_state.packages_considered,
121 variants_evaluated: resolution_state.variants_evaluated,
122 resolution_time_ms: resolution_time,
123 conflicts_encountered: resolution_state.conflicts.len(),
124 backtrack_steps: resolution_state.backtrack_steps,
125 };
126
127 Ok(ResolutionResult {
128 resolved_packages: result,
129 failed_requirements: resolution_state.failed_requirements,
130 conflicts: resolution_state.conflicts,
131 stats,
132 })
133 }
134
135 async fn resolve_recursive(
137 &mut self,
138 state: &mut ResolutionState,
139 ) -> Result<Vec<ResolvedPackageInfo>, RezCoreError> {
140 while let Some(requirement) = state.get_next_requirement() {
142 if let Some(existing) = state.find_satisfying_package(&requirement) {
144 state.mark_requirement_satisfied(&requirement, existing.package.name.clone());
146 continue;
147 }
148
149 let candidates = self.find_candidate_packages(&requirement).await?;
151 state.packages_considered += candidates.len();
152
153 if candidates.is_empty() {
154 state.failed_requirements.push(requirement.clone());
155 continue;
156 }
157
158 let mut resolved = false;
160 for candidate in candidates {
161 if let Some(conflict) = state.check_conflicts(&candidate, &requirement) {
163 state.conflicts.push(conflict);
164 continue;
165 }
166
167 if let Ok(resolved_info) = self
169 .try_resolve_with_candidate(state, &candidate, &requirement)
170 .await
171 {
172 state.add_resolved_package(resolved_info);
173 resolved = true;
174 break;
175 }
176 }
177
178 if !resolved {
179 state.failed_requirements.push(requirement);
180 }
181 }
182
183 Ok(state.resolved_packages.clone())
184 }
185
186 async fn find_candidate_packages(
188 &mut self,
189 requirement: &Requirement,
190 ) -> Result<Vec<Arc<Package>>, RezCoreError> {
191 if let Some(cached) = self.package_cache.get(&requirement.name) {
193 return Ok(self.filter_candidates(cached, requirement));
194 }
195
196 let packages = self
198 .repository_manager
199 .find_packages(&requirement.name)
200 .await?;
201
202 self.package_cache
204 .insert(requirement.name.clone(), packages.clone());
205
206 Ok(self.filter_candidates(&packages, requirement))
207 }
208
209 fn filter_candidates(
211 &self,
212 packages: &[Arc<Package>],
213 requirement: &Requirement,
214 ) -> Vec<Arc<Package>> {
215 packages
216 .iter()
217 .filter(|pkg| {
218 if let Some(ref version) = pkg.version {
219 requirement.is_satisfied_by(version)
220 } else {
221 true
223 }
224 })
225 .cloned()
226 .collect()
227 }
228
229 async fn try_resolve_with_candidate(
231 &mut self,
232 state: &mut ResolutionState,
233 candidate: &Arc<Package>,
234 requirement: &Requirement,
235 ) -> Result<ResolvedPackageInfo, RezCoreError> {
236 let resolved_info = ResolvedPackageInfo {
238 package: candidate.clone(),
239 variant_index: None, requested: state.is_original_requirement(requirement),
241 required_by: vec![requirement.name.clone()],
242 satisfying_requirement: Some(requirement.clone()),
243 };
244
245 for dep_req_str in &candidate.requires {
247 let dep_requirement: Requirement = dep_req_str.parse().map_err(|e| {
248 RezCoreError::RequirementParse(format!(
249 "Invalid requirement '{}': {}",
250 dep_req_str, e
251 ))
252 })?;
253 state.add_requirement(dep_requirement);
254 }
255
256 Ok(resolved_info)
257 }
258}
259
260#[derive(Debug)]
262struct ResolutionState {
263 original_requirements: Vec<Requirement>,
265
266 requirement_queue: VecDeque<Requirement>,
268
269 resolved_packages: Vec<ResolvedPackageInfo>,
271
272 failed_requirements: Vec<Requirement>,
274
275 conflicts: Vec<ResolutionConflict>,
277
278 satisfied_requirements: HashSet<String>,
280
281 packages_considered: usize,
283 variants_evaluated: usize,
284 backtrack_steps: usize,
285}
286
287impl ResolutionState {
288 fn new(requirements: Vec<Requirement>) -> Self {
289 let mut queue = VecDeque::new();
290 for req in &requirements {
291 queue.push_back(req.clone());
292 }
293
294 Self {
295 original_requirements: requirements,
296 requirement_queue: queue,
297 resolved_packages: Vec::new(),
298 failed_requirements: Vec::new(),
299 conflicts: Vec::new(),
300 satisfied_requirements: HashSet::new(),
301 packages_considered: 0,
302 variants_evaluated: 0,
303 backtrack_steps: 0,
304 }
305 }
306
307 fn get_next_requirement(&mut self) -> Option<Requirement> {
308 self.requirement_queue.pop_front()
309 }
310
311 fn add_requirement(&mut self, requirement: Requirement) {
312 let req_key = format!(
313 "{}:{}",
314 requirement.name,
315 requirement
316 .version_constraint
317 .as_ref()
318 .map(|v| format!("{:?}", v))
319 .unwrap_or_default()
320 );
321 if !self.satisfied_requirements.contains(&req_key) {
322 self.requirement_queue.push_back(requirement);
323 }
324 }
325
326 fn mark_requirement_satisfied(&mut self, requirement: &Requirement, package_name: String) {
327 let req_key = format!(
328 "{}:{}",
329 requirement.name,
330 requirement
331 .version_constraint
332 .as_ref()
333 .map(|v| format!("{:?}", v))
334 .unwrap_or_default()
335 );
336 self.satisfied_requirements.insert(req_key);
337 }
338
339 fn find_satisfying_package(&self, requirement: &Requirement) -> Option<&ResolvedPackageInfo> {
340 self.resolved_packages.iter().find(|pkg| {
341 pkg.package.name == requirement.name
342 && pkg
343 .package
344 .version
345 .as_ref()
346 .map_or(true, |v| requirement.is_satisfied_by(v))
347 })
348 }
349
350 fn check_conflicts(
351 &self,
352 candidate: &Arc<Package>,
353 requirement: &Requirement,
354 ) -> Option<ResolutionConflict> {
355 for existing in &self.resolved_packages {
357 if existing.package.name == candidate.name {
358 if let (Some(existing_version), Some(candidate_version)) =
359 (&existing.package.version, &candidate.version)
360 {
361 if existing_version != candidate_version {
362 return Some(ResolutionConflict {
363 package_name: candidate.name.clone(),
364 conflicting_requirements: vec![requirement.clone()],
365 source_packages: vec![existing.package.name.clone()],
366 });
367 }
368 }
369 }
370 }
371
372 None
373 }
374
375 fn add_resolved_package(&mut self, package: ResolvedPackageInfo) {
376 self.resolved_packages.push(package);
377 }
378
379 fn is_original_requirement(&self, requirement: &Requirement) -> bool {
380 self.original_requirements
381 .iter()
382 .any(|orig| orig.name == requirement.name)
383 }
384}
385
386impl Default for ResolutionStats {
387 fn default() -> Self {
388 Self {
389 packages_considered: 0,
390 variants_evaluated: 0,
391 resolution_time_ms: 0,
392 conflicts_encountered: 0,
393 backtrack_steps: 0,
394 }
395 }
396}