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