use std::fmt::Write;
use std::path::PathBuf;
use anyhow::Result;
use itertools::Itertools;
use owo_colors::OwoColorize;
use rustc_hash::FxHashSet;
use tracing::debug;
use uv_cache::Cache;
use uv_distribution_types::{DependencyMetadata, Diagnostic, InstalledDistKind, Name};
use uv_fs::Simplified;
use uv_installer::SitePackages;
use uv_normalize::PackageName;
use uv_preview::Preview;
use uv_python::PythonPreference;
use uv_python::{EnvironmentPreference, Prefix, PythonEnvironment, PythonRequest, Target};
use crate::commands::ExitStatus;
use crate::commands::pip::operations::report_target_environment;
use crate::printer::Printer;
pub(crate) fn pip_freeze(
exclude_editable: bool,
exclude: &FxHashSet<PackageName>,
strict: bool,
dependency_metadata: &DependencyMetadata,
python: Option<&str>,
system: bool,
target: Option<Target>,
prefix: Option<Prefix>,
paths: Option<Vec<PathBuf>>,
cache: &Cache,
printer: Printer,
preview: Preview,
) -> Result<ExitStatus> {
let environment = PythonEnvironment::find(
&python.map(PythonRequest::parse).unwrap_or_default(),
EnvironmentPreference::from_system_flag(system, false),
PythonPreference::default().with_system_flag(system),
cache,
preview,
)?;
let environment = if let Some(target) = target {
debug!(
"Using `--target` directory at {}",
target.root().user_display()
);
environment.with_target(target)?
} else if let Some(prefix) = prefix {
debug!(
"Using `--prefix` directory at {}",
prefix.root().user_display()
);
environment.with_prefix(prefix)?
} else {
environment
};
report_target_environment(&environment, cache, printer)?;
let site_packages = match paths {
Some(paths) => {
paths
.into_iter()
.filter_map(|path| {
environment
.clone()
.with_target(uv_python::Target::from(path))
.ok()
})
.map(|environment| SitePackages::from_environment(&environment))
.collect::<Result<Vec<_>>>()?
}
None => vec![SitePackages::from_environment(&environment)?],
};
site_packages
.iter()
.flat_map(uv_installer::SitePackages::iter)
.filter(|dist| {
if exclude_editable && dist.is_editable() {
return false;
}
if exclude.contains(dist.name()) {
return false;
}
true
})
.sorted_unstable_by(|a, b| a.name().cmp(b.name()).then(a.version().cmp(b.version())))
.map(|dist| match &dist.kind {
InstalledDistKind::Registry(dist) => {
format!("{}=={}", dist.name().bold(), dist.version)
}
InstalledDistKind::Url(dist) => {
if dist.editable {
format!("-e {}", dist.url)
} else {
format!("{} @ {}", dist.name().bold(), dist.url)
}
}
InstalledDistKind::EggInfoFile(dist) => {
format!("{}=={}", dist.name().bold(), dist.version)
}
InstalledDistKind::EggInfoDirectory(dist) => {
format!("{}=={}", dist.name().bold(), dist.version)
}
InstalledDistKind::LegacyEditable(dist) => {
format!("-e {}", dist.target.display())
}
})
.dedup()
.try_for_each(|dist| writeln!(printer.stdout_important(), "{dist}"))?;
if strict {
let markers = environment.interpreter().resolver_marker_environment();
let tags = environment.interpreter().tags()?;
for entry in site_packages {
for diagnostic in entry.diagnostics(&markers, tags, dependency_metadata)? {
writeln!(
printer.stderr(),
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
diagnostic.message().bold()
)?;
}
}
}
Ok(ExitStatus::Success)
}