1use clap::Args;
2use eyre::eyre;
3use eyre::Context;
4use eyre::Result;
5use itertools::Itertools;
6use lux_lib::config::{Config, LuaVersion};
7use lux_lib::lockfile::PinnedState;
8use lux_lib::operations;
9use lux_lib::package::PackageName;
10use lux_lib::package::PackageReq;
11use lux_lib::progress::MultiProgress;
12use lux_lib::project::Project;
13use lux_lib::rockspec::lua_dependency;
14use lux_lib::tree::RockMatches;
15
16#[derive(Args)]
17pub struct ChangePin {
18 package: Vec<PackageReq>,
22
23 #[arg(short, long, alias = "dev", visible_short_aliases = ['d', 'b'])]
26 build: Option<Vec<PackageName>>,
27
28 #[arg(short, long)]
30 test: Option<Vec<PackageName>>,
31}
32
33pub async fn set_pinned_state(data: ChangePin, config: Config, pin: PinnedState) -> Result<()> {
34 match Project::current()? {
35 Some(mut project) => {
36 let progress = MultiProgress::new_arc();
37 if data.package.iter().any(|pkg| !pkg.version_req().is_any()) {
38 return Err(eyre!(
39 "Cannot pin project dependencies using version constraints."
40 ));
41 }
42 let packages = data
43 .package
44 .iter()
45 .map(|pkg| pkg.name())
46 .cloned()
47 .collect_vec();
48 if !packages.is_empty() {
49 project
50 .set_pinned_state(lua_dependency::LuaDependencyType::Regular(packages), pin)
51 .await?;
52 operations::Sync::new(&project, &config)
53 .progress(progress.clone())
54 .sync_dependencies()
55 .await
56 .wrap_err("syncing dependencies with the project lockfile failed.")?;
57 }
58 let build_packages = data.build.unwrap_or_default();
59 if !build_packages.is_empty() {
60 project
61 .set_pinned_state(
62 lua_dependency::LuaDependencyType::Build(build_packages),
63 pin,
64 )
65 .await?;
66 operations::Sync::new(&project, &config)
67 .progress(progress.clone())
68 .sync_build_dependencies()
69 .await
70 .wrap_err("syncing build dependencies with the project lockfile failed.")?;
71 }
72 let test_packages = data.test.unwrap_or_default();
73 if !test_packages.is_empty() {
74 project
75 .set_pinned_state(lua_dependency::LuaDependencyType::Test(test_packages), pin)
76 .await?;
77 operations::Sync::new(&project, &config)
78 .progress(progress.clone())
79 .sync_test_dependencies()
80 .await
81 .wrap_err("syncing test dependencies with the project lockfile failed.")?;
82 }
83 }
84 None => {
85 let tree = config.user_tree(LuaVersion::from(&config)?.clone())?;
86
87 for package in &data.package {
88 match tree.match_rocks_and(package, |package| pin != package.pinned())? {
89 RockMatches::Single(rock) => {
90 operations::set_pinned_state(&rock, &tree, pin)?;
91 }
92 RockMatches::Many(_) => {
93 todo!("Add an error here about many conflicting types and to use `all:`")
94 }
95 RockMatches::NotFound(_) => return Err(eyre!("Rock {} not found!", package)),
96 }
97 }
98 }
99 }
100 Ok(())
101}