1use rez_next_common::RezCoreError;
4use rez_next_package::{Package, PackageRequirement};
5use rez_next_version::Version;
6use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, HashSet, VecDeque};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct GraphNode {
12 pub package: Package,
14 pub dependencies: HashSet<String>,
16 pub dependents: HashSet<String>,
18 pub metadata: HashMap<String, String>,
20}
21
22impl GraphNode {
23 pub fn new(package: Package) -> Self {
25 Self {
26 package,
27 dependencies: HashSet::new(),
28 dependents: HashSet::new(),
29 metadata: HashMap::new(),
30 }
31 }
32
33 pub fn key(&self) -> String {
35 match &self.package.version {
36 Some(version) => format!("{}-{}", self.package.name, version.as_str()),
37 None => self.package.name.clone(),
38 }
39 }
40
41 pub fn add_dependency(&mut self, dependency_key: String) {
43 self.dependencies.insert(dependency_key);
44 }
45
46 pub fn add_dependent(&mut self, dependent_key: String) {
48 self.dependents.insert(dependent_key);
49 }
50
51 pub fn remove_dependency(&mut self, dependency_key: &str) {
53 self.dependencies.remove(dependency_key);
54 }
55
56 pub fn remove_dependent(&mut self, dependent_key: &str) {
58 self.dependents.remove(dependent_key);
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct DependencyConflict {
65 pub package_name: String,
67 pub conflicting_requirements: Vec<PackageRequirement>,
69 pub source_packages: Vec<String>,
71 pub severity: ConflictSeverity,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
77pub enum ConflictSeverity {
78 Minor,
80 Major,
82 Incompatible,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct ConflictResolution {
89 pub package_name: String,
91 pub selected_version: Option<Version>,
93 pub strategy: String,
95 pub modified_packages: Vec<String>,
97}
98
99#[derive(Debug, Clone)]
101pub struct DependencyGraph {
102 nodes: HashMap<String, GraphNode>,
104 requirements: HashMap<String, Vec<PackageRequirement>>,
106 constraints: Vec<PackageRequirement>,
108 exclusions: HashSet<String>,
110 metadata: HashMap<String, String>,
112}
113
114impl DependencyGraph {
115 pub fn new() -> Self {
117 Self {
118 nodes: HashMap::new(),
119 requirements: HashMap::new(),
120 constraints: Vec::new(),
121 exclusions: HashSet::new(),
122 metadata: HashMap::new(),
123 }
124 }
125
126 pub fn clear(&mut self) {
128 self.nodes.clear();
129 self.requirements.clear();
130 self.constraints.clear();
131 self.exclusions.clear();
132 self.metadata.clear();
133 }
134
135 pub fn add_package(&mut self, package: Package) -> Result<(), RezCoreError> {
137 let key = match &package.version {
138 Some(version) => format!("{}-{}", package.name, version.as_str()),
139 None => package.name.clone(),
140 };
141
142 if self.exclusions.contains(&package.name) {
144 return Err(RezCoreError::Solver(format!(
145 "Package {} is excluded",
146 package.name
147 )));
148 }
149
150 let node = GraphNode::new(package);
151 self.nodes.insert(key, node);
152
153 Ok(())
154 }
155
156 pub fn add_requirement(&mut self, requirement: PackageRequirement) -> Result<(), RezCoreError> {
158 self.requirements
159 .entry(requirement.name.clone())
160 .or_insert_with(Vec::new)
161 .push(requirement);
162
163 Ok(())
164 }
165
166 pub fn add_constraint(&mut self, constraint: PackageRequirement) -> Result<(), RezCoreError> {
168 self.constraints.push(constraint);
169 Ok(())
170 }
171
172 pub fn add_exclusion(&mut self, package_name: String) -> Result<(), RezCoreError> {
174 self.exclusions.insert(package_name);
175 Ok(())
176 }
177
178 pub fn add_dependency_edge(
180 &mut self,
181 from_key: &str,
182 to_key: &str,
183 ) -> Result<(), RezCoreError> {
184 if let Some(from_node) = self.nodes.get_mut(from_key) {
186 from_node.add_dependency(to_key.to_string());
187 } else {
188 return Err(RezCoreError::Solver(format!(
189 "Package {} not found in graph",
190 from_key
191 )));
192 }
193
194 if let Some(to_node) = self.nodes.get_mut(to_key) {
196 to_node.add_dependent(from_key.to_string());
197 } else {
198 return Err(RezCoreError::Solver(format!(
199 "Package {} not found in graph",
200 to_key
201 )));
202 }
203
204 Ok(())
205 }
206
207 pub fn remove_package(&mut self, package_key: &str) -> Result<(), RezCoreError> {
209 if let Some(node) = self.nodes.remove(package_key) {
210 for dep_key in &node.dependencies {
212 if let Some(dep_node) = self.nodes.get_mut(dep_key) {
213 dep_node.remove_dependent(package_key);
214 }
215 }
216
217 for dependent_key in &node.dependents {
218 if let Some(dependent_node) = self.nodes.get_mut(dependent_key) {
219 dependent_node.remove_dependency(package_key);
220 }
221 }
222 }
223
224 Ok(())
225 }
226
227 pub fn detect_conflicts(&self) -> Vec<DependencyConflict> {
229 let mut conflicts = Vec::new();
230
231 for (package_name, requirements) in &self.requirements {
233 if requirements.len() > 1 {
234 let mut incompatible_groups = Vec::new();
236
237 for (i, req1) in requirements.iter().enumerate() {
238 for req2 in requirements.iter().skip(i + 1) {
239 }
244 }
245
246 if !incompatible_groups.is_empty() {
247 let severity = self.determine_conflict_severity(&incompatible_groups);
248 let source_packages = self.find_requirement_sources(package_name);
249
250 conflicts.push(DependencyConflict {
251 package_name: package_name.clone(),
252 conflicting_requirements: requirements.clone(),
253 source_packages,
254 severity,
255 });
256 }
257 }
258 }
259
260 conflicts
261 }
262
263 fn determine_conflict_severity(
265 &self,
266 incompatible_groups: &[(PackageRequirement, PackageRequirement)],
267 ) -> ConflictSeverity {
268 ConflictSeverity::Major }
283
284 fn find_requirement_sources(&self, package_name: &str) -> Vec<String> {
286 let mut sources = Vec::new();
287
288 for node in self.nodes.values() {
289 for req_str in &node.package.requires {
290 if let Ok(req) = PackageRequirement::parse(req_str) {
291 if req.name == package_name {
292 sources.push(node.key());
293 break;
294 }
295 }
296 }
297 }
298
299 sources
300 }
301
302 pub fn apply_conflict_resolution(
304 &mut self,
305 resolution: ConflictResolution,
306 ) -> Result<(), RezCoreError> {
307 for package_key in &resolution.modified_packages {
309 self.remove_package(package_key)?;
310 }
311
312 if let Some(requirements) = self.requirements.get_mut(&resolution.package_name) {
314 if let Some(ref version) = resolution.selected_version {
316 let new_requirement = PackageRequirement::with_version(
318 resolution.package_name.clone(),
319 version.as_str().to_string(),
320 );
321 requirements.clear();
322 requirements.push(new_requirement);
323 }
324 }
325
326 Ok(())
327 }
328
329 pub fn get_resolved_packages(&self) -> Result<Vec<Package>, RezCoreError> {
331 let sorted_keys = self.topological_sort()?;
332 let mut packages = Vec::new();
333
334 for key in sorted_keys {
335 if let Some(node) = self.nodes.get(&key) {
336 packages.push(node.package.clone());
337 }
338 }
339
340 Ok(packages)
341 }
342
343 fn topological_sort(&self) -> Result<Vec<String>, RezCoreError> {
345 let mut in_degree: HashMap<String, usize> = HashMap::new();
346 let mut result = Vec::new();
347 let mut queue = VecDeque::new();
348
349 for (key, node) in &self.nodes {
351 in_degree.insert(key.clone(), node.dependents.len());
352 }
353
354 for (key, °ree) in &in_degree {
356 if degree == 0 {
357 queue.push_back(key.clone());
358 }
359 }
360
361 while let Some(current_key) = queue.pop_front() {
363 result.push(current_key.clone());
364
365 if let Some(current_node) = self.nodes.get(¤t_key) {
366 for dep_key in ¤t_node.dependencies {
368 if let Some(degree) = in_degree.get_mut(dep_key) {
369 *degree -= 1;
370 if *degree == 0 {
371 queue.push_back(dep_key.clone());
372 }
373 }
374 }
375 }
376 }
377
378 if result.len() != self.nodes.len() {
380 return Err(RezCoreError::Solver(
381 "Circular dependency detected in package graph".to_string(),
382 ));
383 }
384
385 Ok(result)
386 }
387
388 pub fn get_stats(&self) -> GraphStats {
390 let mut total_dependencies = 0;
391 let mut max_depth = 0;
392
393 for node in self.nodes.values() {
394 total_dependencies += node.dependencies.len();
395 let depth = self.calculate_node_depth(&node.key());
396 max_depth = max_depth.max(depth);
397 }
398
399 GraphStats {
400 node_count: self.nodes.len(),
401 edge_count: total_dependencies,
402 max_depth,
403 conflict_count: self.detect_conflicts().len(),
404 constraint_count: self.constraints.len(),
405 exclusion_count: self.exclusions.len(),
406 }
407 }
408
409 fn calculate_node_depth(&self, node_key: &str) -> usize {
411 let mut visited = HashSet::new();
412 self.calculate_depth_recursive(node_key, &mut visited)
413 }
414
415 fn calculate_depth_recursive(&self, node_key: &str, visited: &mut HashSet<String>) -> usize {
417 if visited.contains(node_key) {
418 return 0; }
420
421 visited.insert(node_key.to_string());
422
423 if let Some(node) = self.nodes.get(node_key) {
424 let mut max_dep_depth = 0;
425 for dep_key in &node.dependencies {
426 let dep_depth = self.calculate_depth_recursive(dep_key, visited);
427 max_dep_depth = max_dep_depth.max(dep_depth);
428 }
429 max_dep_depth + 1
430 } else {
431 0
432 }
433 }
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
438pub struct GraphStats {
439 pub node_count: usize,
441 pub edge_count: usize,
443 pub max_depth: usize,
445 pub conflict_count: usize,
447 pub constraint_count: usize,
449 pub exclusion_count: usize,
451}
452
453impl Default for DependencyGraph {
454 fn default() -> Self {
455 Self::new()
456 }
457}