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