1use rez_next_package::Package;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ResolutionResult {
10 pub packages: Vec<Package>,
12 pub conflicts_resolved: bool,
14 pub resolution_time_ms: u64,
16 pub metadata: HashMap<String, String>,
18}
19
20impl ResolutionResult {
21 pub fn new(packages: Vec<Package>) -> Self {
23 Self {
24 packages,
25 conflicts_resolved: false,
26 resolution_time_ms: 0,
27 metadata: HashMap::new(),
28 }
29 }
30
31 pub fn with_conflicts_resolved(packages: Vec<Package>, resolution_time_ms: u64) -> Self {
33 Self {
34 packages,
35 conflicts_resolved: true,
36 resolution_time_ms,
37 metadata: HashMap::new(),
38 }
39 }
40
41 pub fn with_metadata(mut self, key: String, value: String) -> Self {
43 self.metadata.insert(key, value);
44 self
45 }
46
47 pub fn package_count(&self) -> usize {
49 self.packages.len()
50 }
51
52 pub fn get_package(&self, name: &str) -> Option<&Package> {
54 self.packages.iter().find(|p| p.name == name)
55 }
56
57 pub fn get_package_names(&self) -> Vec<String> {
59 self.packages.iter().map(|p| p.name.clone()).collect()
60 }
61
62 pub fn contains_package(&self, name: &str) -> bool {
64 self.packages.iter().any(|p| p.name == name)
65 }
66
67 pub fn find_packages(&self, pattern: &str) -> Vec<&Package> {
69 self.packages
70 .iter()
71 .filter(|p| self.matches_pattern(&p.name, pattern))
72 .collect()
73 }
74
75 fn matches_pattern(&self, text: &str, pattern: &str) -> bool {
77 if pattern == "*" {
78 return true;
79 }
80
81 if pattern.contains('*') {
82 let regex_pattern = pattern.replace("*", ".*");
84 if let Ok(regex) = regex::Regex::new(&format!("^{}$", regex_pattern)) {
85 return regex.is_match(text);
86 }
87 }
88
89 text == pattern
90 }
91
92 pub fn get_summary(&self) -> ResolutionSummary {
94 let mut package_versions = HashMap::new();
95 let mut total_size = 0u64;
96
97 for package in &self.packages {
98 if let Some(ref version) = package.version {
99 package_versions.insert(package.name.clone(), version.as_str().to_string());
100 } else {
101 package_versions.insert(package.name.clone(), "latest".to_string());
102 }
103
104 total_size += 1024 * 1024; }
107
108 ResolutionSummary {
109 package_count: self.packages.len(),
110 conflicts_resolved: self.conflicts_resolved,
111 resolution_time_ms: self.resolution_time_ms,
112 estimated_size_bytes: total_size,
113 package_versions,
114 }
115 }
116
117 pub fn validate(&self) -> Result<(), String> {
119 let mut seen_packages = std::collections::HashSet::new();
121 for package in &self.packages {
122 let key = match &package.version {
123 Some(version) => format!("{}-{}", package.name, version.as_str()),
124 None => package.name.clone(),
125 };
126
127 if seen_packages.contains(&key) {
128 return Err(format!("Duplicate package in resolution: {}", key));
129 }
130 seen_packages.insert(key);
131 }
132
133 for package in &self.packages {
135 if let Err(e) = package.validate() {
136 return Err(format!("Invalid package {}: {}", package.name, e));
137 }
138 }
139
140 Ok(())
141 }
142
143 pub fn to_environment_spec(&self) -> EnvironmentSpec {
145 let mut packages = Vec::new();
146 let mut environment_vars = HashMap::new();
147
148 for package in &self.packages {
149 let package_spec = PackageSpec {
150 name: package.name.clone(),
151 version: package.version.as_ref().map(|v| v.as_str().to_string()),
152 requirements: package.requires.clone(),
153 tools: package.tools.clone(),
154 };
155 packages.push(package_spec);
156
157 if let Some(ref commands) = package.commands {
159 environment_vars.insert(
160 format!("{}_COMMANDS", package.name.to_uppercase()),
161 commands.clone(),
162 );
163 }
164 }
165
166 EnvironmentSpec {
167 packages,
168 environment_vars,
169 metadata: self.metadata.clone(),
170 }
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct ResolutionSummary {
177 pub package_count: usize,
179 pub conflicts_resolved: bool,
181 pub resolution_time_ms: u64,
183 pub estimated_size_bytes: u64,
185 pub package_versions: HashMap<String, String>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct EnvironmentSpec {
192 pub packages: Vec<PackageSpec>,
194 pub environment_vars: HashMap<String, String>,
196 pub metadata: HashMap<String, String>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct PackageSpec {
203 pub name: String,
205 pub version: Option<String>,
207 pub requirements: Vec<String>,
209 pub tools: Vec<String>,
211}
212
213impl EnvironmentSpec {
214 pub fn get_package_names(&self) -> Vec<String> {
216 self.packages.iter().map(|p| p.name.clone()).collect()
217 }
218
219 pub fn get_env_var(&self, name: &str) -> Option<&String> {
221 self.environment_vars.get(name)
222 }
223
224 pub fn add_env_var(&mut self, name: String, value: String) {
226 self.environment_vars.insert(name, value);
227 }
228
229 pub fn get_all_tools(&self) -> Vec<String> {
231 let mut all_tools = Vec::new();
232 for package in &self.packages {
233 all_tools.extend(package.tools.iter().cloned());
234 }
235 all_tools.sort();
236 all_tools.dedup();
237 all_tools
238 }
239
240 pub fn generate_shell_script(&self, shell: ShellType) -> String {
242 let mut script = String::new();
243
244 match shell {
245 ShellType::Bash => {
246 script.push_str("#!/bin/bash\n");
247 script.push_str("# Generated by rez-core\n\n");
248
249 for (name, value) in &self.environment_vars {
250 script.push_str(&format!("export {}=\"{}\"\n", name, value));
251 }
252
253 let tools = self.get_all_tools();
255 if !tools.is_empty() {
256 script.push_str("\n# Add tools to PATH\n");
257 for tool in tools {
258 script.push_str(&format!("export PATH=\"$PATH:/path/to/{}\"\n", tool));
259 }
260 }
261 }
262 ShellType::Cmd => {
263 script.push_str("@echo off\n");
264 script.push_str("REM Generated by rez-core\n\n");
265
266 for (name, value) in &self.environment_vars {
267 script.push_str(&format!("set {}={}\n", name, value));
268 }
269
270 let tools = self.get_all_tools();
272 if !tools.is_empty() {
273 script.push_str("\nREM Add tools to PATH\n");
274 for tool in tools {
275 script.push_str(&format!("set PATH=%PATH%;C:\\path\\to\\{}\n", tool));
276 }
277 }
278 }
279 ShellType::PowerShell => {
280 script.push_str("# Generated by rez-core\n\n");
281
282 for (name, value) in &self.environment_vars {
283 script.push_str(&format!("$env:{} = \"{}\"\n", name, value));
284 }
285
286 let tools = self.get_all_tools();
288 if !tools.is_empty() {
289 script.push_str("\n# Add tools to PATH\n");
290 for tool in tools {
291 script.push_str(&format!("$env:PATH += \";C:\\path\\to\\{}\"\n", tool));
292 }
293 }
294 }
295 }
296
297 script
298 }
299}
300
301#[derive(Debug, Clone, PartialEq)]
303pub enum ShellType {
304 Bash,
306 Cmd,
308 PowerShell,
310}
311
312impl Default for ResolutionResult {
313 fn default() -> Self {
314 Self::new(Vec::new())
315 }
316}