1use std::{collections::HashMap, io, path::PathBuf};
2
3use super::{InstallTree, RockLayout, Tree, TreeError};
4use crate::{
5 config::{tree::RockLayoutConfig, Config},
6 fs,
7 lockfile::{LocalPackage, Lockfile, ReadOnly},
8 lua_version::LuaVersion,
9 package::{PackageName, PackageVersion},
10 tree::mk_rock_layout,
11};
12use miette::{Diagnostic, Result};
13use thiserror::Error;
14
15#[derive(Error, Debug, Diagnostic)]
16#[non_exhaustive]
17#[error(
18 r#"cannot install conflicting packages in flat tree:
19package: {name}
20version A: {version_a}
21version B: {version_b}
22"#
23)]
24struct ConflictingPackageError {
25 name: PackageName,
26 version_a: PackageVersion,
27 version_b: PackageVersion,
28}
29
30#[derive(Clone, Debug)]
35pub struct FlatDistTree(Tree);
36
37impl FlatDistTree {
38 pub fn new(root: PathBuf, version: LuaVersion, config: &Config) -> Result<Self, TreeError> {
39 let version_dir = root.join(version.to_string());
40 let test_tree_dir = version_dir.join("test_dependencies");
41 let build_tree_dir = version_dir.join("build_dependencies");
42 let tree = Tree::new_with_paths(root, test_tree_dir, build_tree_dir, version, config)?;
43 Ok(Self(tree))
44 }
45
46 fn guard_no_conflicting_package(&self, package: &LocalPackage) -> Result<(), io::Error> {
47 let lockfile = self.lockfile().map_err(io::Error::other)?;
48 match lockfile.has_rock(&package.clone().into_package_req(), None) {
49 Some(existing_package) => {
50 if existing_package.version() == package.version() {
51 Ok(())
52 } else {
53 Err(io::Error::other(ConflictingPackageError {
54 name: package.name().clone(),
55 version_a: existing_package.version().clone(),
56 version_b: package.version().clone(),
57 }))
58 }
59 }
60 None => Ok(()),
61 }
62 }
63}
64
65impl Drop for FlatDistTree {
66 fn drop(&mut self) {
67 let build_tree_dir = &self.0.build_tree_dir;
68 if build_tree_dir.is_dir() {
69 let _ = fs::sync::remove_dir_all(build_tree_dir);
70 }
71 let package_rockspec = self.root().join("package.rockspec");
72 if package_rockspec.is_file() {
73 let _ = fs::sync::remove_file(&package_rockspec);
74 }
75 let lockfile = self.lockfile_path();
76 if lockfile.is_file() {
77 let _ = fs::sync::remove_file(&lockfile);
78 }
79 let etc_dir = self.root().join("etc");
80 if etc_dir.is_dir() {
81 let _ = fs::sync::remove_dir_all(etc_dir);
82 }
83 }
84}
85
86impl InstallTree for FlatDistTree {
87 fn version(&self) -> &LuaVersion {
88 self.0.version()
89 }
90
91 fn root(&self) -> PathBuf {
92 self.0.root()
93 }
94
95 fn root_for(&self, _package: &LocalPackage) -> PathBuf {
96 self.0.root()
97 }
98
99 fn bin(&self) -> PathBuf {
100 self.0.bin()
101 }
102
103 fn unwrapped_bin(&self) -> PathBuf {
104 self.0.unwrapped_bin()
105 }
106
107 fn entrypoint(&self, package: &LocalPackage) -> io::Result<RockLayout> {
108 self.guard_no_conflicting_package(package)?;
109 Ok(mk_rock_layout(
110 &self.root(),
111 &self.bin(),
112 &self.root_for(package),
113 package,
114 &self.0.entrypoint_layout,
115 ))
116 }
117
118 fn dependency(&self, package: &LocalPackage) -> io::Result<RockLayout> {
119 self.guard_no_conflicting_package(package)?;
120 Ok(mk_rock_layout(
121 &self.root(),
122 &self.bin(),
123 &self.root_for(package),
124 package,
125 &RockLayoutConfig::default(),
126 ))
127 }
128
129 fn lockfile(&self) -> Result<Lockfile<ReadOnly>, TreeError> {
130 self.0.lockfile()
131 }
132
133 fn lockfile_path(&self) -> PathBuf {
134 self.0.lockfile_path()
135 }
136
137 fn build_tree(&self, config: &Config) -> Result<Tree, TreeError> {
138 self.0.build_tree(config)
139 }
140
141 fn test_tree(&self, config: &Config) -> Result<Tree, TreeError> {
142 self.0.test_tree(config)
143 }
144
145 fn installed_rock_layout(&self, package: &LocalPackage) -> Result<RockLayout, TreeError> {
146 let lockfile = self.lockfile()?;
147 let layout_config = if lockfile.is_entrypoint(&package.id()) {
148 self.0.entrypoint_layout.clone()
149 } else {
150 RockLayoutConfig::default()
151 };
152 Ok(mk_rock_layout(
153 &self.root(),
154 &self.bin(),
155 &self.root_for(package),
156 package,
157 &layout_config,
158 ))
159 }
160
161 fn list(&self) -> Result<HashMap<PackageName, Vec<LocalPackage>>, TreeError> {
162 self.0.list()
163 }
164
165 fn match_rocks(
166 &self,
167 req: &crate::package::PackageReq,
168 ) -> Result<super::RockMatches, TreeError> {
169 self.0.match_rocks(req)
170 }
171}