diff --git a/Cargo.lock b/Cargo.lock index 759aaf5e08..7af4510e90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4631,14 +4631,21 @@ dependencies = [ "pixi_pypi_spec", "pixi_spec", "pixi_utils", + "pixi_uv_conversions", + "pypi_modifiers", "rattler_conda_types", + "rattler_lock", + "regex", "same-file", "serde", "tempfile", "tokio", "tracing", "url", + "uv-distribution", + "uv-distribution-types", "uv-normalize", + "uv-types", ] [[package]] @@ -4774,9 +4781,7 @@ dependencies = [ "pixi_spec", "pixi_task", "pixi_utils", - "pixi_uv_context", "pixi_uv_conversions", - "pypi_modifiers", "rattler", "rattler_conda_types", "rattler_digest", @@ -4810,13 +4815,9 @@ dependencies = [ "url", "uv-client", "uv-configuration", - "uv-distribution", - "uv-distribution-types", - "uv-normalize", "uv-pep508", "uv-pypi-types", "uv-requirements-txt", - "uv-types", "which", "zip 2.4.2", ] diff --git a/crates/pixi_api/Cargo.toml b/crates/pixi_api/Cargo.toml index 47ee5e4cfd..0a4668af35 100644 --- a/crates/pixi_api/Cargo.toml +++ b/crates/pixi_api/Cargo.toml @@ -24,11 +24,18 @@ pixi_manifest = { workspace = true } pixi_pypi_spec = { workspace = true } pixi_spec = { workspace = true } pixi_utils = { workspace = true } +pixi_uv_conversions = { workspace = true } +pypi_modifiers = { workspace = true } rattler_conda_types = { workspace = true } +rattler_lock = { workspace = true } +regex = { workspace = true } same-file = { workspace = true } serde = { workspace = true, features = ["derive"] } tempfile = { workspace = true } tokio = { workspace = true, features = ["fs"] } tracing = { workspace = true } url = { workspace = true } +uv-distribution = { workspace = true } +uv-distribution-types = { workspace = true } uv-normalize = { workspace = true } +uv-types = { workspace = true } diff --git a/crates/pixi_api/src/context.rs b/crates/pixi_api/src/context.rs index 86d1a5922d..30c2c794ee 100644 --- a/crates/pixi_api/src/context.rs +++ b/crates/pixi_api/src/context.rs @@ -12,7 +12,7 @@ use pixi_spec::PixiSpec; use rattler_conda_types::{PackageName, Platform}; use crate::interface::Interface; -use crate::workspace::{InitOptions, ReinstallOptions}; +use crate::workspace::{InitOptions, Package, ReinstallOptions}; pub struct WorkspaceContext { interface: I, @@ -47,6 +47,27 @@ impl WorkspaceContext { crate::workspace::workspace::name::set(&self.interface, self.workspace_mut()?, name).await } + pub async fn list_packages( + &self, + regex: Option, + platform: Option, + environment: Option, + explicit: bool, + no_install: bool, + lock_file_usage: LockFileUsage, + ) -> miette::Result> { + crate::workspace::list::list( + &self.workspace, + regex, + platform, + environment, + explicit, + no_install, + lock_file_usage, + ) + .await + } + pub async fn list_features(&self) -> IndexMap { crate::workspace::workspace::feature::list_features(&self.workspace).await } diff --git a/crates/pixi_api/src/workspace/list/mod.rs b/crates/pixi_api/src/workspace/list/mod.rs new file mode 100644 index 0000000000..bdc825400c --- /dev/null +++ b/crates/pixi_api/src/workspace/list/mod.rs @@ -0,0 +1,140 @@ +use itertools::Itertools; +use miette::IntoDiagnostic; +use pixi_core::{ + UpdateLockFileOptions, Workspace, environment::LockFileUsage, lock_file::UvResolutionContext, +}; +use pixi_manifest::FeaturesExt; +use pixi_uv_conversions::{ConversionError, pypi_options_to_index_locations, to_uv_normalize}; +use pypi_modifiers::pypi_tags::{get_pypi_tags, is_python_record}; +use rattler_conda_types::Platform; +use rattler_lock::LockedPackageRef; +use uv_distribution::RegistryWheelIndex; +use uv_distribution_types::{ + ConfigSettings, ExtraBuildRequires, ExtraBuildVariables, PackageConfigSettings, +}; + +mod package; + +use package::PackageExt; +pub use package::{Package, PackageKind}; + +pub async fn list( + workspace: &Workspace, + regex: Option, + platform: Option, + environment: Option, + explicit: bool, + no_install: bool, + lock_file_usage: LockFileUsage, +) -> miette::Result> { + let environment = workspace.environment_from_name_or_env_var(environment)?; + + let lock_file = workspace + .update_lock_file(UpdateLockFileOptions { + lock_file_usage, + no_install, + max_concurrent_solves: workspace.config().max_concurrent_solves(), + }) + .await? + .0 + .into_lock_file(); + + // Load the platform + let platform = platform.unwrap_or_else(|| environment.best_platform()); + + // Get all the packages in the environment. + let locked_deps = lock_file + .environment(environment.name().as_str()) + .and_then(|env| env.packages(platform).map(Vec::from_iter)) + .unwrap_or_default(); + + let locked_deps_ext = locked_deps + .into_iter() + .map(|p| match p { + LockedPackageRef::Pypi(pypi_data, _) => { + let name = to_uv_normalize(&pypi_data.name)?; + Ok(PackageExt::PyPI(pypi_data.clone(), name)) + } + LockedPackageRef::Conda(c) => Ok(PackageExt::Conda(c.clone())), + }) + .collect::, ConversionError>>() + .into_diagnostic()?; + + // Get the python record from the lock file + let mut conda_records = locked_deps_ext.iter().filter_map(|d| d.as_conda()); + + // Construct the registry index if we have a python record + let python_record = conda_records.find(|r| is_python_record(r)); + let tags; + let uv_context; + let index_locations; + let config_settings = ConfigSettings::default(); + let package_config_settings = PackageConfigSettings::default(); + let extra_build_requires = ExtraBuildRequires::default(); + let extra_build_variables = ExtraBuildVariables::default(); + + let mut registry_index = if let Some(python_record) = python_record { + if environment.has_pypi_dependencies() { + uv_context = UvResolutionContext::from_config(workspace.config())?; + index_locations = + pypi_options_to_index_locations(&environment.pypi_options(), workspace.root()) + .into_diagnostic()?; + tags = get_pypi_tags( + platform, + &environment.system_requirements(), + python_record.record(), + )?; + Some(RegistryWheelIndex::new( + &uv_context.cache, + &tags, + &index_locations, + &uv_types::HashStrategy::None, + &config_settings, + &package_config_settings, + &extra_build_requires, + &extra_build_variables, + )) + } else { + None + } + } else { + None + }; + + // Get the explicit project dependencies + let mut project_dependency_names = environment + .combined_dependencies(Some(platform)) + .names() + .map(|p| p.as_source().to_string()) + .collect_vec(); + project_dependency_names.extend( + environment + .pypi_dependencies(Some(platform)) + .into_iter() + .map(|(name, _)| name.as_normalized().as_dist_info_name().into_owned()), + ); + + let mut packages_to_output = locked_deps_ext + .iter() + .map(|p| Package::new(p, &project_dependency_names, registry_index.as_mut())) + .collect::>(); + + // Filter packages by regex if needed + if let Some(regex) = regex { + let regex = regex::Regex::new(®ex).map_err(|_| miette::miette!("Invalid regex"))?; + packages_to_output = packages_to_output + .into_iter() + .filter(|p| regex.is_match(&p.name)) + .collect::>(); + } + + // Filter packages by explicit if needed + if explicit { + packages_to_output = packages_to_output + .into_iter() + .filter(|p| p.is_explicit) + .collect::>(); + } + + Ok(packages_to_output) +} diff --git a/crates/pixi_api/src/workspace/list/package.rs b/crates/pixi_api/src/workspace/list/package.rs new file mode 100644 index 0000000000..c01adf5e18 --- /dev/null +++ b/crates/pixi_api/src/workspace/list/package.rs @@ -0,0 +1,170 @@ +use std::borrow::Cow; + +use pixi_uv_conversions::to_uv_version; +use rattler_lock::{CondaPackageData, PypiPackageData, UrlOrPath}; +use serde::Serialize; +use uv_distribution::RegistryWheelIndex; + +#[derive(Debug, Clone, Serialize)] +pub struct Package { + pub name: String, + pub version: String, + pub build: Option, + pub size_bytes: Option, + pub kind: PackageKind, + pub source: Option, + pub is_explicit: bool, + #[serde(skip_serializing_if = "serde_skip_is_editable")] + pub is_editable: bool, +} + +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "lowercase")] +pub enum PackageKind { + Conda, + Pypi, +} + +impl Package { + pub(crate) fn new<'a, 'b>( + package: &'b PackageExt, + project_dependency_names: &'a [String], + registry_index: Option<&'a mut RegistryWheelIndex<'b>>, + ) -> Self { + let name = package.name().to_string(); + let version = package.version().into_owned(); + let kind = PackageKind::from(package); + + let build = match package { + PackageExt::Conda(pkg) => Some(pkg.record().build.clone()), + PackageExt::PyPI(_, _) => None, + }; + + let (size_bytes, source) = match package { + PackageExt::Conda(pkg) => ( + pkg.record().size, + match pkg { + CondaPackageData::Source(source) => Some(source.location.to_string()), + CondaPackageData::Binary(binary) => { + binary.channel.as_ref().map(|c| c.to_string()) + } + }, + ), + PackageExt::PyPI(p, name) => { + // Check the hash to avoid non index packages to be handled by the registry + // index as wheels + if p.hash.is_some() { + if let Some(registry_index) = registry_index { + // Handle case where the registry index is present + let entry = registry_index.get(name).find(|i| { + i.dist.filename.version + == to_uv_version(&p.version).expect("invalid version") + }); + let size = entry.and_then(|e| get_dir_size(e.dist.path.clone()).ok()); + let name = entry.map(|e| e.dist.filename.to_string()); + (size, name) + } else { + get_pypi_location_information(&p.location) + } + } else { + get_pypi_location_information(&p.location) + } + } + }; + + let is_explicit = project_dependency_names.contains(&name); + let is_editable = match package { + PackageExt::Conda(_) => false, + PackageExt::PyPI(p, _) => p.editable, + }; + + Self { + name, + version, + build, + size_bytes, + kind, + source, + is_explicit, + is_editable, + } + } +} + +/// Return the size and source location of the pypi package +fn get_pypi_location_information(location: &UrlOrPath) -> (Option, Option) { + match location { + UrlOrPath::Url(url) => (None, Some(url.to_string())), + UrlOrPath::Path(path) => ( + get_dir_size(std::path::Path::new(path.as_str())).ok(), + Some(path.to_string()), + ), + } +} + +/// Get directory size +pub fn get_dir_size

(path: P) -> std::io::Result +where + P: AsRef, +{ + let mut result = 0; + + if path.as_ref().is_dir() { + for entry in fs_err::read_dir(path.as_ref())? { + let _path = entry?.path(); + if _path.is_file() { + result += _path.metadata()?.len(); + } else { + result += get_dir_size(_path)?; + } + } + } else { + result = path.as_ref().metadata()?.len(); + } + Ok(result) +} + +fn serde_skip_is_editable(editable: &bool) -> bool { + !(*editable) +} + +/// Associate with a uv_normalize::PackageName +#[allow(clippy::large_enum_variant)] +pub(crate) enum PackageExt { + PyPI(PypiPackageData, uv_normalize::PackageName), + Conda(CondaPackageData), +} + +impl From<&PackageExt> for PackageKind { + fn from(package: &PackageExt) -> Self { + match package { + PackageExt::Conda(_) => PackageKind::Conda, + PackageExt::PyPI(_, _) => PackageKind::Pypi, + } + } +} + +impl PackageExt { + pub fn as_conda(&self) -> Option<&CondaPackageData> { + match self { + PackageExt::Conda(c) => Some(c), + _ => None, + } + } + + /// Returns the name of the package. + pub fn name(&self) -> Cow<'_, str> { + match self { + Self::Conda(value) => value.record().name.as_normalized().into(), + Self::PyPI(value, _) => value.name.as_dist_info_name(), + } + } + + /// Returns the version string of the package + pub fn version(&self) -> Cow<'_, str> { + match self { + Self::Conda(value) => value.record().version.as_str(), + Self::PyPI(value, _) => value.version.to_string().into(), + } + } +} diff --git a/crates/pixi_api/src/workspace/mod.rs b/crates/pixi_api/src/workspace/mod.rs index 1baf8d0711..22c4dd5b5d 100644 --- a/crates/pixi_api/src/workspace/mod.rs +++ b/crates/pixi_api/src/workspace/mod.rs @@ -1,6 +1,9 @@ pub(crate) mod init; pub use init::{GitAttributes, InitOptions, ManifestFormat}; +pub(crate) mod list; +pub use list::{Package, PackageKind}; + pub(crate) mod reinstall; pub use reinstall::ReinstallOptions; diff --git a/crates/pixi_cli/Cargo.toml b/crates/pixi_cli/Cargo.toml index 957f0f9303..ed4cca1562 100644 --- a/crates/pixi_cli/Cargo.toml +++ b/crates/pixi_cli/Cargo.toml @@ -80,9 +80,7 @@ pixi_reporters = { workspace = true } pixi_spec = { workspace = true } pixi_task = { workspace = true } pixi_utils = { workspace = true, default-features = false } -pixi_uv_context = { workspace = true } pixi_uv_conversions = { workspace = true } -pypi_modifiers = { workspace = true } rattler = { workspace = true, features = ["cli-tools", "indicatif"] } rattler_conda_types = { workspace = true } rattler_digest = { workspace = true } @@ -120,13 +118,9 @@ typed-path = { workspace = true } url = { workspace = true } uv-client = { workspace = true } uv-configuration = { workspace = true } -uv-distribution = { workspace = true } -uv-distribution-types = { workspace = true } -uv-normalize = { workspace = true } uv-pep508 = { workspace = true } uv-pypi-types = { workspace = true } uv-requirements-txt = { workspace = true } -uv-types = { workspace = true } which = { workspace = true } zip = { workspace = true, features = ["deflate", "time"] } diff --git a/crates/pixi_cli/src/list.rs b/crates/pixi_cli/src/list.rs index 85d3ffcd61..9d9ed12552 100644 --- a/crates/pixi_cli/src/list.rs +++ b/crates/pixi_cli/src/list.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, io, io::{Write, stdout}, }; @@ -8,25 +7,20 @@ use clap::Parser; use console::Color; use fancy_display::FancyDisplay; use human_bytes::human_bytes; -use itertools::Itertools; use miette::IntoDiagnostic; -use pixi_consts::consts; -use pixi_core::{WorkspaceLocator, lock_file::UpdateLockFileOptions}; -use pixi_manifest::FeaturesExt; -use pixi_uv_context::UvResolutionContext; -use pixi_uv_conversions::{ - ConversionError, pypi_options_to_index_locations, to_uv_normalize, to_uv_version, +use pixi_api::{ + WorkspaceContext, + workspace::{Package, PackageKind}, }; -use pypi_modifiers::pypi_tags::{get_pypi_tags, is_python_record}; +use pixi_consts::consts; +use pixi_core::WorkspaceLocator; use rattler_conda_types::Platform; -use rattler_lock::{CondaPackageData, LockedPackageRef, PypiPackageData, UrlOrPath}; use serde::Serialize; -use uv_distribution::RegistryWheelIndex; -use uv_distribution_types::{ - ConfigSettings, ExtraBuildRequires, ExtraBuildVariables, PackageConfigSettings, -}; -use crate::cli_config::{LockFileUpdateConfig, NoInstallConfig, WorkspaceConfig}; +use crate::{ + cli_config::{LockFileUpdateConfig, NoInstallConfig, WorkspaceConfig}, + cli_interface::CliInterface, +}; // an enum to sort by size or name #[derive(clap::ValueEnum, Clone, Debug, Serialize)] @@ -81,215 +75,26 @@ pub struct Args { pub explicit: bool, } -fn serde_skip_is_editable(editable: &bool) -> bool { - !(*editable) -} - -#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)] -#[serde(rename_all = "lowercase")] -enum KindPackage { - Conda, - Pypi, -} - -impl From<&PackageExt> for KindPackage { - fn from(package: &PackageExt) -> Self { - match package { - PackageExt::Conda(_) => KindPackage::Conda, - PackageExt::PyPI(_, _) => KindPackage::Pypi, - } - } -} - -impl FancyDisplay for KindPackage { - fn fancy_display(&self) -> console::StyledObject<&str> { - match self { - KindPackage::Conda => consts::CONDA_PACKAGE_STYLE.apply_to("conda"), - KindPackage::Pypi => consts::PYPI_PACKAGE_STYLE.apply_to("pypi"), - } - } -} - -#[derive(Serialize)] -struct PackageToOutput { - name: String, - version: String, - build: Option, - size_bytes: Option, - kind: KindPackage, - source: Option, - is_explicit: bool, - #[serde(skip_serializing_if = "serde_skip_is_editable")] - is_editable: bool, -} - -/// Get directory size -pub(crate) fn get_dir_size

(path: P) -> std::io::Result -where - P: AsRef, -{ - let mut result = 0; - - if path.as_ref().is_dir() { - for entry in fs_err::read_dir(path.as_ref())? { - let _path = entry?.path(); - if _path.is_file() { - result += _path.metadata()?.len(); - } else { - result += get_dir_size(_path)?; - } - } - } else { - result = path.as_ref().metadata()?.len(); - } - Ok(result) -} - -/// Associate with a uv_normalize::PackageName -#[allow(clippy::large_enum_variant)] -enum PackageExt { - PyPI(PypiPackageData, uv_normalize::PackageName), - Conda(CondaPackageData), -} - -impl PackageExt { - fn as_conda(&self) -> Option<&CondaPackageData> { - match self { - PackageExt::Conda(c) => Some(c), - _ => None, - } - } - - /// Returns the name of the package. - pub fn name(&self) -> Cow<'_, str> { - match self { - Self::Conda(value) => value.record().name.as_normalized().into(), - Self::PyPI(value, _) => value.name.as_dist_info_name(), - } - } - - /// Returns the version string of the package - pub fn version(&self) -> Cow<'_, str> { - match self { - Self::Conda(value) => value.record().version.as_str(), - Self::PyPI(value, _) => value.version.to_string().into(), - } - } -} - pub async fn execute(args: Args) -> miette::Result<()> { let workspace = WorkspaceLocator::for_cli() .with_search_start(args.workspace_config.workspace_locator_start()) .locate()?; - let environment = workspace.environment_from_name_or_env_var(args.environment)?; - - let lock_file = workspace - .update_lock_file(UpdateLockFileOptions { - lock_file_usage: args.lock_file_update_config.lock_file_usage()?, - no_install: args.no_install_config.no_install, - max_concurrent_solves: workspace.config().max_concurrent_solves(), - }) - .await? - .0 - .into_lock_file(); - - // Load the platform + let lock_file_usage = args.lock_file_update_config.lock_file_usage()?; + let environment = workspace.environment_from_name_or_env_var(args.environment.clone())?; let platform = args.platform.unwrap_or_else(|| environment.best_platform()); - // Get all the packages in the environment. - let locked_deps = lock_file - .environment(environment.name().as_str()) - .and_then(|env| env.packages(platform).map(Vec::from_iter)) - .unwrap_or_default(); - - let locked_deps_ext = locked_deps - .into_iter() - .map(|p| match p { - LockedPackageRef::Pypi(pypi_data, _) => { - let name = to_uv_normalize(&pypi_data.name)?; - Ok(PackageExt::PyPI(pypi_data.clone(), name)) - } - LockedPackageRef::Conda(c) => Ok(PackageExt::Conda(c.clone())), - }) - .collect::, ConversionError>>() - .into_diagnostic()?; - - // Get the python record from the lock file - let mut conda_records = locked_deps_ext.iter().filter_map(|d| d.as_conda()); - - // Construct the registry index if we have a python record - let python_record = conda_records.find(|r| is_python_record(r)); - let tags; - let uv_context; - let index_locations; - let config_settings = ConfigSettings::default(); - let package_config_settings = PackageConfigSettings::default(); - let extra_build_requires = ExtraBuildRequires::default(); - let extra_build_variables = ExtraBuildVariables::default(); - - let mut registry_index = if let Some(python_record) = python_record { - if environment.has_pypi_dependencies() { - uv_context = UvResolutionContext::from_config(workspace.config())?; - index_locations = - pypi_options_to_index_locations(&environment.pypi_options(), workspace.root()) - .into_diagnostic()?; - tags = get_pypi_tags( - platform, - &environment.system_requirements(), - python_record.record(), - )?; - Some(RegistryWheelIndex::new( - &uv_context.cache, - &tags, - &index_locations, - &uv_types::HashStrategy::None, - &config_settings, - &package_config_settings, - &extra_build_requires, - &extra_build_variables, - )) - } else { - None - } - } else { - None - }; - - // Get the explicit project dependencies - let mut project_dependency_names = environment - .combined_dependencies(Some(platform)) - .names() - .map(|p| p.as_source().to_string()) - .collect_vec(); - project_dependency_names.extend( - environment - .pypi_dependencies(Some(platform)) - .into_iter() - .map(|(name, _)| name.as_normalized().as_dist_info_name().into_owned()), - ); - - let mut packages_to_output = locked_deps_ext - .iter() - .map(|p| create_package_to_output(p, &project_dependency_names, registry_index.as_mut())) - .collect::, _>>()?; - - // Filter packages by regex if needed - if let Some(regex) = args.regex { - let regex = regex::Regex::new(®ex).map_err(|_| miette::miette!("Invalid regex"))?; - packages_to_output = packages_to_output - .into_iter() - .filter(|p| regex.is_match(&p.name)) - .collect::>(); - } - - // Filter packages by explicit if needed - if args.explicit { - packages_to_output = packages_to_output - .into_iter() - .filter(|p| p.is_explicit) - .collect::>(); - } + let workspace_ctx = WorkspaceContext::new(CliInterface {}, workspace.clone()); + let mut packages_to_output = workspace_ctx + .list_packages( + args.regex, + args.platform, + args.environment, + args.explicit, + args.no_install_config.no_install, + lock_file_usage, + ) + .await?; // Sort according to the sorting strategy match args.sort_by { @@ -337,7 +142,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { Ok(()) } -fn print_packages_as_table(packages: &Vec) -> io::Result<()> { +fn print_packages_as_table(packages: &Vec) -> io::Result<()> { let mut writer = tabwriter::TabWriter::new(stdout()); let header_style = console::Style::new().bold().cyan(); @@ -358,9 +163,9 @@ fn print_packages_as_table(packages: &Vec) -> io::Result<()> { writer, "{}", match package.kind { - KindPackage::Conda => + PackageKind::Conda => consts::CONDA_PACKAGE_STYLE.apply_to(&package.name).bold(), - KindPackage::Pypi => consts::PYPI_PACKAGE_STYLE.apply_to(&package.name).bold(), + PackageKind::Pypi => consts::PYPI_PACKAGE_STYLE.apply_to(&package.name).bold(), } )? } else { @@ -373,13 +178,18 @@ fn print_packages_as_table(packages: &Vec) -> io::Result<()> { .map(|size| human_bytes(size as f64)) .unwrap_or_default(); + let fancy_kind = match package.kind { + PackageKind::Conda => consts::CONDA_PACKAGE_STYLE.apply_to("conda"), + PackageKind::Pypi => consts::PYPI_PACKAGE_STYLE.apply_to("pypi"), + }; + writeln!( writer, "\t{}\t{}\t{}\t{}\t{}{}", &package.version, package.build.as_deref().unwrap_or(""), size_human, - &package.kind.fancy_display(), + &fancy_kind, package.source.as_deref().unwrap_or(""), if package.is_editable { format!(" {}", console::style("(editable)").fg(Color::Yellow)) @@ -392,7 +202,7 @@ fn print_packages_as_table(packages: &Vec) -> io::Result<()> { writer.flush() } -fn json_packages(packages: &Vec, json_pretty: bool) { +fn json_packages(packages: &Vec, json_pretty: bool) { let json_string = if json_pretty { serde_json::to_string_pretty(&packages) } else { @@ -402,76 +212,3 @@ fn json_packages(packages: &Vec, json_pretty: bool) { println!("{json_string}"); } - -/// Return the size and source location of the pypi package -fn get_pypi_location_information(location: &UrlOrPath) -> (Option, Option) { - match location { - UrlOrPath::Url(url) => (None, Some(url.to_string())), - UrlOrPath::Path(path) => ( - get_dir_size(std::path::Path::new(path.as_str())).ok(), - Some(path.to_string()), - ), - } -} - -fn create_package_to_output<'a, 'b>( - package: &'b PackageExt, - project_dependency_names: &'a [String], - registry_index: Option<&'a mut RegistryWheelIndex<'b>>, -) -> miette::Result { - let name = package.name().to_string(); - let version = package.version().into_owned(); - let kind = KindPackage::from(package); - - let build = match package { - PackageExt::Conda(pkg) => Some(pkg.record().build.clone()), - PackageExt::PyPI(_, _) => None, - }; - - let (size_bytes, source) = match package { - PackageExt::Conda(pkg) => ( - pkg.record().size, - match pkg { - CondaPackageData::Source(source) => Some(source.location.to_string()), - CondaPackageData::Binary(binary) => binary.channel.as_ref().map(|c| c.to_string()), - }, - ), - PackageExt::PyPI(p, name) => { - // Check the hash to avoid non index packages to be handled by the registry - // index as wheels - if p.hash.is_some() { - if let Some(registry_index) = registry_index { - // Handle case where the registry index is present - let entry = registry_index.get(name).find(|i| { - i.dist.filename.version - == to_uv_version(&p.version).expect("invalid version") - }); - let size = entry.and_then(|e| get_dir_size(e.dist.path.clone()).ok()); - let name = entry.map(|e| e.dist.filename.to_string()); - (size, name) - } else { - get_pypi_location_information(&p.location) - } - } else { - get_pypi_location_information(&p.location) - } - } - }; - - let is_explicit = project_dependency_names.contains(&name); - let is_editable = match package { - PackageExt::Conda(_) => false, - PackageExt::PyPI(p, _) => p.editable, - }; - - Ok(PackageToOutput { - name, - version, - build, - size_bytes, - kind, - source, - is_explicit, - is_editable, - }) -}