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