use rez_next_package::Package;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolutionResult {
pub packages: Vec<Package>,
pub conflicts_resolved: bool,
pub resolution_time_ms: u64,
pub metadata: HashMap<String, String>,
}
impl ResolutionResult {
pub fn new(packages: Vec<Package>) -> Self {
Self {
packages,
conflicts_resolved: false,
resolution_time_ms: 0,
metadata: HashMap::new(),
}
}
pub fn with_conflicts_resolved(packages: Vec<Package>, resolution_time_ms: u64) -> Self {
Self {
packages,
conflicts_resolved: true,
resolution_time_ms,
metadata: HashMap::new(),
}
}
pub fn with_metadata(mut self, key: String, value: String) -> Self {
self.metadata.insert(key, value);
self
}
pub fn package_count(&self) -> usize {
self.packages.len()
}
pub fn get_package(&self, name: &str) -> Option<&Package> {
self.packages.iter().find(|p| p.name == name)
}
pub fn get_package_names(&self) -> Vec<String> {
self.packages.iter().map(|p| p.name.clone()).collect()
}
pub fn contains_package(&self, name: &str) -> bool {
self.packages.iter().any(|p| p.name == name)
}
pub fn find_packages(&self, pattern: &str) -> Vec<&Package> {
self.packages
.iter()
.filter(|p| self.matches_pattern(&p.name, pattern))
.collect()
}
fn matches_pattern(&self, text: &str, pattern: &str) -> bool {
if pattern == "*" {
return true;
}
if pattern.contains('*') {
let regex_pattern = pattern.replace("*", ".*");
if let Ok(regex) = regex::Regex::new(&format!("^{}$", regex_pattern)) {
return regex.is_match(text);
}
}
text == pattern
}
pub fn get_summary(&self) -> ResolutionSummary {
let mut package_versions = HashMap::new();
let mut total_size = 0u64;
for package in &self.packages {
if let Some(ref version) = package.version {
package_versions.insert(package.name.clone(), version.as_str().to_string());
} else {
package_versions.insert(package.name.clone(), "latest".to_string());
}
total_size += 1024 * 1024; }
ResolutionSummary {
package_count: self.packages.len(),
conflicts_resolved: self.conflicts_resolved,
resolution_time_ms: self.resolution_time_ms,
estimated_size_bytes: total_size,
package_versions,
}
}
pub fn validate(&self) -> Result<(), String> {
let mut seen_packages = std::collections::HashSet::new();
for package in &self.packages {
let key = match &package.version {
Some(version) => format!("{}-{}", package.name, version.as_str()),
None => package.name.clone(),
};
if seen_packages.contains(&key) {
return Err(format!("Duplicate package in resolution: {}", key));
}
seen_packages.insert(key);
}
for package in &self.packages {
if let Err(e) = package.validate() {
return Err(format!("Invalid package {}: {}", package.name, e));
}
}
Ok(())
}
pub fn to_environment_spec(&self) -> EnvironmentSpec {
let mut packages = Vec::new();
let mut environment_vars = HashMap::new();
for package in &self.packages {
let package_spec = PackageSpec {
name: package.name.clone(),
version: package.version.as_ref().map(|v| v.as_str().to_string()),
requirements: package.requires.clone(),
tools: package.tools.clone(),
};
packages.push(package_spec);
if let Some(ref commands) = package.commands {
environment_vars.insert(
format!("{}_COMMANDS", package.name.to_uppercase()),
commands.clone(),
);
}
}
EnvironmentSpec {
packages,
environment_vars,
metadata: self.metadata.clone(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolutionSummary {
pub package_count: usize,
pub conflicts_resolved: bool,
pub resolution_time_ms: u64,
pub estimated_size_bytes: u64,
pub package_versions: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentSpec {
pub packages: Vec<PackageSpec>,
pub environment_vars: HashMap<String, String>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackageSpec {
pub name: String,
pub version: Option<String>,
pub requirements: Vec<String>,
pub tools: Vec<String>,
}
impl EnvironmentSpec {
pub fn get_package_names(&self) -> Vec<String> {
self.packages.iter().map(|p| p.name.clone()).collect()
}
pub fn get_env_var(&self, name: &str) -> Option<&String> {
self.environment_vars.get(name)
}
pub fn add_env_var(&mut self, name: String, value: String) {
self.environment_vars.insert(name, value);
}
pub fn get_all_tools(&self) -> Vec<String> {
let mut all_tools = Vec::new();
for package in &self.packages {
all_tools.extend(package.tools.iter().cloned());
}
all_tools.sort();
all_tools.dedup();
all_tools
}
pub fn generate_shell_script(&self, shell: ShellType) -> String {
let mut script = String::new();
match shell {
ShellType::Bash => {
script.push_str("#!/bin/bash\n");
script.push_str("# Generated by rez-core\n\n");
for (name, value) in &self.environment_vars {
script.push_str(&format!("export {}=\"{}\"\n", name, value));
}
let tools = self.get_all_tools();
if !tools.is_empty() {
script.push_str("\n# Add tools to PATH\n");
for tool in tools {
script.push_str(&format!("export PATH=\"$PATH:/path/to/{}\"\n", tool));
}
}
}
ShellType::Cmd => {
script.push_str("@echo off\n");
script.push_str("REM Generated by rez-core\n\n");
for (name, value) in &self.environment_vars {
script.push_str(&format!("set {}={}\n", name, value));
}
let tools = self.get_all_tools();
if !tools.is_empty() {
script.push_str("\nREM Add tools to PATH\n");
for tool in tools {
script.push_str(&format!("set PATH=%PATH%;C:\\path\\to\\{}\n", tool));
}
}
}
ShellType::PowerShell => {
script.push_str("# Generated by rez-core\n\n");
for (name, value) in &self.environment_vars {
script.push_str(&format!("$env:{} = \"{}\"\n", name, value));
}
let tools = self.get_all_tools();
if !tools.is_empty() {
script.push_str("\n# Add tools to PATH\n");
for tool in tools {
script.push_str(&format!("$env:PATH += \";C:\\path\\to\\{}\"\n", tool));
}
}
}
}
script
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ShellType {
Bash,
Cmd,
PowerShell,
}
impl Default for ResolutionResult {
fn default() -> Self {
Self::new(Vec::new())
}
}