easy_install/
tool.rs

1#[cfg(windows)]
2use std::os::windows::fs::MetadataExt;
3
4use easy_archive::tool::{human_size, mode_to_string};
5#[cfg(unix)]
6use std::os::unix::fs::MetadataExt;
7use std::path::Path;
8
9use crate::{
10    env::add_to_path,
11    install::{Output, OutputFile},
12};
13
14pub fn get_bin_name(s: &str) -> String {
15    if cfg!(windows) && !s.ends_with(".exe") && !s.contains(".") {
16        return s.to_string() + ".exe";
17    }
18    s.to_string()
19}
20
21pub fn get_meta<P: AsRef<Path>>(s: P) -> (u32, u32, bool) {
22    let mut mode = 0;
23    let mut size = 0;
24    let mut is_dir = false;
25    if let Ok(meta) = std::fs::metadata(s) {
26        #[cfg(windows)]
27        {
28            mode = 0;
29            size = meta.file_size() as u32;
30        }
31
32        #[cfg(unix)]
33        {
34            mode = meta.mode();
35            size = meta.size() as u32;
36        }
37
38        is_dir = meta.is_dir()
39    }
40
41    (mode, size, is_dir)
42}
43const MAX_FILE_COUNT: usize = 16;
44pub fn display_output(output: &Output) -> String {
45    let mut v = vec![];
46    for i in output.values() {
47        if i.files.len() > MAX_FILE_COUNT {
48            let sum_size = i.files.iter().fold(0, |pre, cur| pre + cur.size);
49            v.push(
50                [
51                    human_size(sum_size as usize).as_str(),
52                    format!("(total {})", i.files.len()).as_str(),
53                    i.install_dir.as_str(),
54                ]
55                .join(" "),
56            );
57        } else {
58            let max_size_len = i
59                .files
60                .iter()
61                .fold(0, |pre, cur| pre.max(human_size(cur.size as usize).len()));
62
63            for k in &i.files {
64                let s = human_size(k.size as usize);
65                v.push(
66                    [
67                        mode_to_string(k.mode, k.is_dir),
68                        " ".repeat(max_size_len - s.len()) + &s,
69                        [k.origin_path.as_str(), k.install_path.as_str()].join(" -> "),
70                    ]
71                    .join(" "),
72                );
73            }
74        }
75    }
76    v.join("\n")
77}
78
79pub fn add_output_to_path(output: &Output) {
80    for v in output.values() {
81        for f in &v.files {
82            if check(f, &v.install_dir, &v.bin_dir) {
83                println!("Warning: file exists at {}", f.install_path);
84            }
85        }
86    }
87    for v in output.values() {
88        add_to_path(&v.install_dir);
89        if v.install_dir != v.bin_dir {
90            add_to_path(&v.bin_dir);
91        }
92
93        #[cfg(unix)]
94        if v.files.len() == 1 {
95            let i = &v.files[0];
96            crate::install::add_execute_permission(&i.install_path)
97                .expect("failed to add_execute_permission");
98        }
99    }
100}
101
102pub fn get_filename(s: &str) -> Option<String> {
103    s.split("/").last().map(|i| i.to_string())
104}
105
106#[cfg(windows)]
107fn which(name: &str) -> Option<String> {
108    let cmd = std::process::Command::new("powershell")
109        .args(["-c", &format!("(get-command {name}).Source")])
110        .output()
111        .ok()?;
112    String::from_utf8(cmd.stdout)
113        .ok()
114        .map(|i| i.trim().replace("\\", "/"))
115}
116
117#[cfg(unix)]
118fn which(name: &str) -> Option<String> {
119    let cmd = std::process::Command::new("which")
120        .arg(name)
121        .output()
122        .ok()?;
123    String::from_utf8(cmd.stdout)
124        .ok()
125        .map(|i| i.trim().to_string())
126}
127
128const EXEC_MASK: u32 = 0o111;
129fn executable(name: &str, mode: u32) -> bool {
130    name.ends_with(".exe") || (!name.contains(".") && mode & EXEC_MASK != 0)
131}
132
133pub fn check(file: &OutputFile, install_dir: &str, binstall_dir: &str) -> bool {
134    let file_path = &file.install_path;
135    let name = get_filename(file_path).unwrap();
136    if !file_path.starts_with(install_dir)
137        || !file_path.starts_with(binstall_dir)
138        || !executable(&name, file.mode)
139    {
140        return false;
141    }
142    if let Some(p) = which(&name) {
143        if file_path == &p {
144            return true;
145        }
146    }
147    false
148}