Skip to main content

lux_lib/tree/
dist.rs

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/// A staging tree with a flat hierarchy for distribution outside of Lux.
29/// When dropped, tries to remove all build artifacts that are not meant to be distributed,
30/// as well as the `etc` directory (failing silently if any artifacts cannot be removed).
31/// Unlike a regular Lux tree, **conflicting packages are not supported.**
32#[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}