Accept multiple packages in `uv export` (#16603) · astral-sh/uv@5fe8af1
@@ -57,7 +57,7 @@ pub(crate) async fn export(
5757project_dir: &Path,
5858format: Option<ExportFormat>,
5959all_packages: bool,
60-package: Option<PackageName>,
60+package: Vec<PackageName>,
6161prune: Vec<PackageName>,
6262hashes: bool,
6363install_options: InstallOptions,
@@ -98,16 +98,28 @@ pub(crate) async fn export(
9898&workspace_cache,
9999)
100100.await?
101-} else if let Some(package) = package.as_ref() {
101+} else if let [name] = package.as_slice() {
102102VirtualProject::Project(
103103Workspace::discover(project_dir, &DiscoveryOptions::default(), &workspace_cache)
104104.await?
105-.with_current_project(package.clone())
106-.with_context(|| format!("Package `{package}` not found in workspace"))?,
105+.with_current_project(name.clone())
106+.with_context(|| format!("Package `{name}` not found in workspace"))?,
107107)
108108} else {
109-VirtualProject::discover(project_dir, &DiscoveryOptions::default(), &workspace_cache)
110-.await?
109+let project = VirtualProject::discover(
110+ project_dir,
111+&DiscoveryOptions::default(),
112+&workspace_cache,
113+)
114+.await?;
115+116+for name in &package {
117+if !project.workspace().packages().contains_key(name) {
118+return Err(anyhow::anyhow!("Package `{name}` not found in workspace"));
119+}
120+}
121+122+ project
111123};
112124ExportTarget::Project(project)
113125};
@@ -219,18 +231,24 @@ pub(crate) async fn export(
219231workspace: project.workspace(),
220232lock: &lock,
221233}
222-} else if let Some(package) = package.as_ref() {
223-InstallTarget::Project {
224-workspace: project.workspace(),
225-name: package,
226-lock: &lock,
227-}
228234} else {
229-// By default, install the root package.
230-InstallTarget::Project {
231-workspace: project.workspace(),
232-name: project.project_name(),
233-lock: &lock,
235+match package.as_slice() {
236+// By default, install the root project.
237+[] => InstallTarget::Project {
238+workspace: project.workspace(),
239+name: project.project_name(),
240+lock: &lock,
241+},
242+[name] => InstallTarget::Project {
243+workspace: project.workspace(),
244+ name,
245+lock: &lock,
246+},
247+ names => InstallTarget::Projects {
248+workspace: project.workspace(),
249+ names,
250+lock: &lock,
251+},
234252}
235253}
236254}
@@ -240,17 +258,23 @@ pub(crate) async fn export(
240258 workspace,
241259lock: &lock,
242260}
243-} else if let Some(package) = package.as_ref() {
244-InstallTarget::Project {
245- workspace,
246-name: package,
247-lock: &lock,
248-}
249261} else {
250-// By default, install the entire workspace.
251-InstallTarget::NonProjectWorkspace {
252- workspace,
253-lock: &lock,
262+match package.as_slice() {
263+// By default, install the entire workspace.
264+[] => InstallTarget::NonProjectWorkspace {
265+ workspace,
266+lock: &lock,
267+},
268+[name] => InstallTarget::Project {
269+ workspace,
270+ name,
271+lock: &lock,
272+},
273+ names => InstallTarget::Projects {
274+ workspace,
275+ names,
276+lock: &lock,
277+},
254278}
255279}
256280}