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}