Skip to main content

lux_cli/
pin.rs

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