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