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