1use clap::Args;
2use itertools::Itertools;
3use lux_lib::config::Config;
4use lux_lib::lockfile::PinnedState;
5use lux_lib::lua_version::LuaVersion;
6use lux_lib::operations;
7use lux_lib::package::PackageName;
8use lux_lib::package::PackageReq;
9use lux_lib::rockspec::lua_dependency;
10use lux_lib::tree::RockMatches;
11use lux_lib::workspace::Workspace;
12
13use miette::miette;
14use miette::Context;
15use miette::Result;
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
43 if data
44 .package_req
45 .iter()
46 .any(|pkg| !pkg.version_req().is_any())
47 {
48 return Err(miette!(
49 "Cannot pin project dependencies using version constraints."
50 ));
51 }
52 let packages = data
53 .package_req
54 .iter()
55 .map(|pkg| pkg.name())
56 .cloned()
57 .collect_vec();
58 if !packages.is_empty() {
59 project
60 .set_pinned_state(
61 lua_dependency::LuaDependencyType::Regular(packages.iter().collect()),
62 pin,
63 )
64 .await?;
65 }
66 let build_packages = data.build.unwrap_or_default();
67 if !build_packages.is_empty() {
68 project
69 .set_pinned_state(
70 lua_dependency::LuaDependencyType::Build(build_packages.iter().collect()),
71 pin,
72 )
73 .await?;
74 }
75 let test_packages = data.test.unwrap_or_default();
76 if !test_packages.is_empty() {
77 project
78 .set_pinned_state(
79 lua_dependency::LuaDependencyType::Test(test_packages.iter().collect()),
80 pin,
81 )
82 .await?;
83 }
84 if !packages.is_empty() {
85 operations::Sync::new(&workspace, &config)
86 .sync_dependencies()
87 .await
88 .wrap_err("syncing dependencies with the project lockfile failed.")?;
89 }
90 if !build_packages.is_empty() {
91 operations::Sync::new(&workspace, &config)
92 .sync_build_dependencies()
93 .await
94 .wrap_err("syncing build dependencies with the project lockfile failed.")?;
95 }
96 if !test_packages.is_empty() {
97 operations::Sync::new(&workspace, &config)
98 .sync_test_dependencies()
99 .await
100 .wrap_err("syncing test dependencies with the project lockfile failed.")?;
101 }
102 }
103 None => {
104 let tree = config.user_tree(LuaVersion::from(&config)?.clone())?;
105
106 for package in &data.package_req {
107 match tree.match_rocks_and(package, |package| pin != package.pinned())? {
108 RockMatches::Single(rock) => {
109 operations::set_pinned_state(&rock, &tree, pin)?;
110 }
111 RockMatches::Many(_) => {
112 todo!("Add an error here about many conflicting types and to use `all:`")
113 }
114 RockMatches::NotFound(_) => return Err(miette!("Rock {} not found!", package)),
115 }
116 }
117 }
118 }
119 Ok(())
120}