1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use crate::{fs, project, tree::RockLayout};
7use miette::Diagnostic;
8use thiserror::Error;
9
10use self::rock_manifest::{RockManifest, RockManifestError};
11
12pub mod install_binary_rock;
13pub mod luarocks_installation;
14pub mod rock_manifest;
15
16pub(crate) fn current_platform_luarocks_identifier() -> String {
18 let platform = match std::env::consts::OS {
19 "macos" => "macosx",
20 p => p,
21 };
22 format!("{}-{}", platform, std::env::consts::ARCH)
23}
24
25#[derive(Error, Debug, Diagnostic)]
26#[non_exhaustive]
27pub enum InstallRockManifestError {
28 #[error(transparent)]
29 #[diagnostic(transparent)]
30 Fs(#[from] fs::Error),
31 #[error("rock_manifest not found")]
32 #[diagnostic(help("the rock may have been packed using LuaRocks version 1"))]
33 RockManifestNotFound,
34 #[error(transparent)]
35 #[diagnostic(transparent)]
36 RockManifest(#[from] RockManifestError),
37}
38
39pub(crate) struct RockManifestSources {
40 pub root: PathBuf,
41 pub lib: PathBuf,
42 pub lua: PathBuf,
43 pub bin: PathBuf,
44}
45
46#[tracing::instrument(level = "trace", skip(entry))]
47async fn install_manifest_entries<T>(
48 entry: &HashMap<PathBuf, T>,
49 src: &Path,
50 dest: &Path,
51) -> Result<(), fs::Error> {
52 for relative_src_path in entry.keys() {
53 let target = dest.join(relative_src_path);
54 let src_path = src.join(relative_src_path);
55 if src_path.is_dir() {
56 project::copy_dir(&src_path, &target).await?;
57 } else if src_path.is_file() {
58 if let Some(target_parent_dir) = target.parent() {
59 fs::tokio::create_dir_all(target_parent_dir).await?;
60 }
61 fs::tokio::copy(&src_path, target).await?;
62 } else {
63 return Err(fs::Error::Other(format!(
64 "the entry {} listed in the `rock_manifest` is neither a file nor a directory",
65 src_path.display()
66 )));
67 }
68 }
69 Ok(())
70}
71
72async fn install_bin_manifest_entries(
73 entries: &HashMap<PathBuf, String>,
74 src: &Path,
75 dest: &Path,
76) -> Result<(), fs::Error> {
77 install_manifest_entries(entries, src, dest).await?;
78 #[cfg(unix)]
79 for relative_src_path in entries.keys() {
80 fs::set_executable_permissions(&dest.join(relative_src_path)).await?;
81 }
82 Ok(())
83}
84
85pub(crate) async fn install_rock_manifest(
86 sources: RockManifestSources,
87 layout: &RockLayout,
88 bin_dest: &Path,
89) -> Result<(), InstallRockManifestError> {
90 let rock_manifest_file = sources.root.join("rock_manifest");
91 if !rock_manifest_file.is_file() {
92 return Err(InstallRockManifestError::RockManifestNotFound);
93 }
94 let rock_manifest_content = fs::tokio::read_to_string(rock_manifest_file).await?;
95 let rock_manifest = RockManifest::new(&rock_manifest_content)?;
96 install_manifest_entries(&rock_manifest.lib.entries, &sources.lib, &layout.lib).await?;
97 install_manifest_entries(&rock_manifest.lua.entries, &sources.lua, &layout.src).await?;
98 install_bin_manifest_entries(&rock_manifest.bin.entries, &sources.bin, bin_dest).await?;
99 install_manifest_entries(
100 &rock_manifest.doc.entries,
101 &sources.root.join("doc"),
102 &layout.doc,
103 )
104 .await?;
105 install_manifest_entries(
106 &rock_manifest.conf.entries,
107 &sources.root.join("conf"),
108 &layout.conf,
109 )
110 .await?;
111 install_manifest_entries(&rock_manifest.root.entries, &sources.root, &layout.etc).await?;
112 Ok(())
113}