1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use crate::depman::NodeDependencyManager;
use clean_path::Clean;
use proto_core::{async_trait, Describable, Executable, Installable, ProtoError};
use starbase_utils::json::{self, JsonValue};
use std::path::{Path, PathBuf};

pub fn extract_bin_from_package_json(
    package_path: PathBuf,
    bin_name: &str,
) -> Result<Option<String>, ProtoError> {
    let mut bin_path = None;
    let json: JsonValue = json::read_file(package_path)?;

    if let Some(bin_field) = json.get("bin") {
        match bin_field {
            JsonValue::String(bin) => {
                bin_path = Some(bin.to_owned());
            }
            JsonValue::Object(bins) => {
                if let Some(bin) = bins.get(bin_name) {
                    bin_path = Some(bin.as_str().unwrap_or_default().to_string());
                }
            }
            _ => {}
        };
    }

    if bin_path.is_none() {
        if let Some(main_field) = json.get("main") {
            bin_path = Some(main_field.as_str().unwrap_or_default().to_string());
        }
    }

    Ok(bin_path)
}

#[async_trait]
impl Executable<'_> for NodeDependencyManager {
    async fn find_bin_path(&mut self) -> Result<(), ProtoError> {
        let install_dir = self.get_install_dir()?;
        let bin_name = &self.package_name;
        let package_json = install_dir.join("package.json");

        if package_json.exists() {
            if let Some(bin_path) = extract_bin_from_package_json(package_json, bin_name)? {
                self.bin_path = Some(install_dir.join(bin_path).clean());

                return Ok(());
            }
        }

        return Err(ProtoError::ExecuteMissingBin(
            self.get_name(),
            install_dir.join(format!("bin/{bin_name}.js")),
        ));
    }

    fn get_bin_path(&self) -> Result<&Path, ProtoError> {
        match self.bin_path.as_ref() {
            Some(bin) => Ok(bin),
            None => Err(ProtoError::MissingTool(self.get_name())),
        }
    }

    fn get_globals_bin_dir(&self) -> Result<PathBuf, ProtoError> {
        Ok(self
            .base_dir
            .parent()
            .unwrap()
            .join("node")
            .join("globals")
            .join("bin"))
    }
}