Skip to main content

lux_lib/luarocks/
mod.rs

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::{Error, RockManifest};
11
12pub mod install_binary_rock;
13pub mod luarocks_installation;
14pub mod rock_manifest;
15
16/// Retrieves the target compilation platform and returns it as a luarocks identifier.
17pub(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] Error),
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}