Skip to main content

rez_next_solver/
solver.rs

1//! Core solver implementation
2
3use crate::dependency_resolver::DependencyResolver;
4use crate::resolution::ResolutionResult;
5use rez_next_common::RezCoreError;
6use rez_next_package::{Package, PackageRequirement, Requirement};
7use rez_next_repository::simple_repository::RepositoryManager;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::sync::Arc;
11
12/// Solver configuration
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct SolverConfig {
15    /// Maximum number of resolution attempts
16    pub max_attempts: usize,
17    /// Maximum resolution time in seconds
18    pub max_time_seconds: u64,
19    /// Enable parallel resolution
20    pub enable_parallel: bool,
21    /// Maximum number of parallel workers
22    pub max_workers: usize,
23    /// Enable solver caching
24    pub enable_caching: bool,
25    /// Cache TTL in seconds
26    pub cache_ttl_seconds: u64,
27    /// Prefer latest versions
28    pub prefer_latest: bool,
29    /// Allow pre-release versions
30    pub allow_prerelease: bool,
31    /// Conflict resolution strategy
32    pub conflict_strategy: ConflictStrategy,
33    /// Strict mode: return Err if any requirement cannot be satisfied.
34    /// When false (lenient/default), unsatisfied requirements are recorded in
35    /// `DetailedResolutionResult::failed_requirements` and resolution continues.
36    pub strict_mode: bool,
37}
38
39impl Default for SolverConfig {
40    fn default() -> Self {
41        Self {
42            max_attempts: 1000,
43            max_time_seconds: 300, // 5 minutes
44            enable_parallel: true,
45            max_workers: 4,
46            enable_caching: true,
47            cache_ttl_seconds: 3600, // 1 hour
48            prefer_latest: true,
49            allow_prerelease: false,
50            conflict_strategy: ConflictStrategy::LatestWins,
51            strict_mode: false,
52        }
53    }
54}
55
56/// Conflict resolution strategy
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
58pub enum ConflictStrategy {
59    /// Latest version wins
60    LatestWins,
61    /// Earliest version wins
62    EarliestWins,
63    /// Fail on conflict
64    FailOnConflict,
65    /// Try to find compatible version
66    FindCompatible,
67}
68
69/// Solver request
70#[derive(Debug, Clone)]
71pub struct SolverRequest {
72    /// Root requirements to resolve
73    pub requirements: Vec<PackageRequirement>,
74    /// Additional constraints
75    pub constraints: Vec<PackageRequirement>,
76    /// Packages to exclude
77    pub excludes: Vec<String>,
78    /// Platform constraints
79    pub platform: Option<String>,
80    /// Architecture constraints
81    pub arch: Option<String>,
82    /// Request metadata
83    pub metadata: HashMap<String, String>,
84}
85
86impl SolverRequest {
87    /// Create a new solver request
88    pub fn new(requirements: Vec<PackageRequirement>) -> Self {
89        Self {
90            requirements,
91            constraints: Vec::new(),
92            excludes: Vec::new(),
93            platform: None,
94            arch: None,
95            metadata: HashMap::new(),
96        }
97    }
98
99    /// Add a constraint
100    pub fn with_constraint(mut self, constraint: PackageRequirement) -> Self {
101        self.constraints.push(constraint);
102        self
103    }
104
105    /// Add an exclusion
106    pub fn with_exclude(mut self, package_name: String) -> Self {
107        self.excludes.push(package_name);
108        self
109    }
110
111    /// Set platform constraint
112    pub fn with_platform(mut self, platform: String) -> Self {
113        self.platform = Some(platform);
114        self
115    }
116
117    /// Set architecture constraint
118    pub fn with_arch(mut self, arch: String) -> Self {
119        self.arch = Some(arch);
120        self
121    }
122}
123
124/// High-performance dependency solver
125pub struct DependencySolver {
126    /// Solver configuration
127    config: SolverConfig,
128    /// Repository manager for package discovery
129    repository_manager: Option<Arc<RepositoryManager>>,
130}
131
132impl std::fmt::Debug for DependencySolver {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        f.debug_struct("DependencySolver")
135            .field("config", &self.config)
136            .field("has_repository", &self.repository_manager.is_some())
137            .finish()
138    }
139}
140
141/// Solver statistics
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct SolverStats {
144    /// Total number of resolutions
145    pub total_resolutions: u64,
146    /// Successful resolutions
147    pub successful_resolutions: u64,
148    /// Failed resolutions
149    pub failed_resolutions: u64,
150    /// Cache hits
151    pub cache_hits: u64,
152    /// Cache misses
153    pub cache_misses: u64,
154    /// Average resolution time in milliseconds
155    pub avg_resolution_time_ms: f64,
156    /// Total resolution time in milliseconds
157    pub total_resolution_time_ms: u64,
158}
159
160impl Default for SolverStats {
161    fn default() -> Self {
162        Self {
163            total_resolutions: 0,
164            successful_resolutions: 0,
165            failed_resolutions: 0,
166            cache_hits: 0,
167            cache_misses: 0,
168            avg_resolution_time_ms: 0.0,
169            total_resolution_time_ms: 0,
170        }
171    }
172}
173
174impl DependencySolver {
175    /// Create a new solver with default configuration
176    pub fn new() -> Self {
177        let config = SolverConfig::default();
178        Self {
179            config,
180            repository_manager: None,
181        }
182    }
183
184    /// Create a new solver with custom configuration
185    pub fn with_config(config: SolverConfig) -> Self {
186        Self {
187            config,
188            repository_manager: None,
189        }
190    }
191
192    /// Set repository manager for package discovery
193    pub fn with_repository_manager(mut self, manager: Arc<RepositoryManager>) -> Self {
194        self.repository_manager = Some(manager);
195        self
196    }
197
198    /// Set repository manager in place
199    pub fn set_repository_manager(&mut self, manager: Arc<RepositoryManager>) {
200        self.repository_manager = Some(manager);
201    }
202
203    /// Resolve dependencies for a given request using DependencyResolver when possible
204    pub fn resolve(&self, request: SolverRequest) -> Result<ResolutionResult, RezCoreError> {
205        if let Some(ref repo_manager) = self.repository_manager {
206            // Use the real DependencyResolver backed by repositories
207            let rt = tokio::runtime::Runtime::new()
208                .map_err(|e| RezCoreError::Solver(format!("Failed to create runtime: {}", e)))?;
209
210            // Convert PackageRequirement -> Requirement via string parsing
211            let requirements: Vec<Requirement> = request
212                .requirements
213                .into_iter()
214                .map(|pr| {
215                    let req_str = pr.to_string();
216                    req_str
217                        .parse::<Requirement>()
218                        .unwrap_or_else(|_| Requirement::new(pr.name.clone()))
219                })
220                .collect();
221
222            let mut resolver =
223                DependencyResolver::new(Arc::clone(repo_manager), self.config.clone());
224
225            let result = rt.block_on(resolver.resolve(requirements))?;
226
227            let packages: Vec<Package> = result
228                .resolved_packages
229                .into_iter()
230                .map(|info| (*info.package).clone())
231                .collect();
232
233            Ok(ResolutionResult {
234                packages,
235                conflicts_resolved: !result.conflicts.is_empty(),
236                resolution_time_ms: result.stats.resolution_time_ms,
237                metadata: HashMap::new(),
238            })
239        } else {
240            // No repository configured: return empty result (packages must be resolved externally)
241            Ok(ResolutionResult {
242                packages: Vec::new(),
243                conflicts_resolved: false,
244                resolution_time_ms: 0,
245                metadata: HashMap::new(),
246            })
247        }
248    }
249}
250
251impl Default for DependencySolver {
252    fn default() -> Self {
253        Self::new()
254    }
255}