Accept multiple packages in `uv export` (#16603) · astral-sh/uv@5fe8af1

@@ -57,7 +57,7 @@ pub(crate) async fn export(

5757

project_dir: &Path,

5858

format: Option<ExportFormat>,

5959

all_packages: bool,

60-

package: Option<PackageName>,

60+

package: Vec<PackageName>,

6161

prune: Vec<PackageName>,

6262

hashes: bool,

6363

install_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() {

102102

VirtualProject::Project(

103103

Workspace::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

};

112124

ExportTarget::Project(project)

113125

};

@@ -219,18 +231,24 @@ pub(crate) async fn export(

219231

workspace: project.workspace(),

220232

lock: &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,

241259

lock: &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

}