lux_lib/operations/
pin.rs1use std::io;
2
3use fs_extra::dir::CopyOptions;
4use itertools::Itertools;
5use thiserror::Error;
6
7use crate::{
8 lockfile::{LocalPackageId, PinnedState},
9 package::PackageSpec,
10 tree::{Tree, TreeError},
11};
12
13#[derive(Error, Debug)]
16pub enum PinError {
17 #[error("package with ID {0} not found in lockfile")]
18 PackageNotFound(LocalPackageId),
19 #[error("rock {rock} is already {}pinned!", if *.pin_state == PinnedState::Unpinned { "un" } else { "" })]
20 PinStateUnchanged {
21 pin_state: PinnedState,
22 rock: PackageSpec,
23 },
24 #[error("cannot change pin state of {rock}, since a second version of {rock} is already installed with `pin: {}`", .pin_state.as_bool())]
25 PinStateConflict {
26 pin_state: PinnedState,
27 rock: PackageSpec,
28 },
29 #[error(transparent)]
30 Io(#[from] io::Error),
31 #[error(transparent)]
32 Tree(#[from] TreeError),
33 #[error("failed to move old package: {0}")]
34 MoveItemsFailure(#[from] fs_extra::error::Error),
35 #[error("cannot change pin state of {rock}, because it is not an entrypoint")]
36 NotAnEntrypoint { rock: PackageSpec },
37}
38
39pub fn set_pinned_state(
40 package_id: &LocalPackageId,
41 tree: &Tree,
42 pin: PinnedState,
43) -> Result<(), PinError> {
44 let lockfile = tree.lockfile()?;
45 let mut package = lockfile
46 .get(package_id)
47 .ok_or_else(|| PinError::PackageNotFound(package_id.clone()))?
48 .clone();
49
50 if !lockfile.is_entrypoint(&package.id()) {
51 return Err(PinError::NotAnEntrypoint {
52 rock: package.to_package(),
53 });
54 }
55
56 if pin == package.pinned() {
57 return Err(PinError::PinStateUnchanged {
58 pin_state: package.pinned(),
59 rock: package.to_package(),
60 });
61 }
62
63 let old_package = package.clone();
64 let items = std::fs::read_dir(tree.root_for(&package))?
65 .filter_map(Result::ok)
66 .map(|dir| dir.path())
67 .collect_vec();
68
69 package.spec.pinned = pin;
70
71 if lockfile.get(&package.id()).is_some() {
72 return Err(PinError::PinStateConflict {
73 pin_state: package.pinned(),
74 rock: package.to_package(),
75 });
76 }
77
78 let new_root = tree.root_for(&package);
79
80 std::fs::create_dir_all(&new_root)?;
81
82 fs_extra::move_items(&items, new_root, &CopyOptions::new())?;
83
84 lockfile.map_then_flush(|lockfile| {
85 lockfile.remove(&old_package);
86 lockfile.add_entrypoint(&package);
87
88 Ok::<_, io::Error>(())
89 })?;
90
91 Ok(())
92}