1use crate::errors::Result;
2use anyhow::{anyhow, bail};
3use filetime::set_file_times;
4use filetime::FileTime;
5use fs_err as fs;
6use lazy_static::lazy_static;
7use std::path::Path;
8use std::path::PathBuf;
9use std::process::Command;
10use std::sync::atomic::{AtomicI8, Ordering};
11
12pub fn copy_and_sync_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
13 let from = &from.as_ref();
14 let to = &to.as_ref();
15
16 if !from.exists() {
17 bail!("Source {from:?} is missing")
18 }
19
20 if !to.parent().unwrap().exists() {
21 bail!("Target directory is missing")
22 }
23
24 if to.exists() {
26 let mut permissions = fs::metadata(&to)?.permissions();
27 if permissions.readonly() {
28 permissions.set_readonly(false);
29 fs::set_permissions(&to, permissions)?;
30 }
31 }
32
33 log::trace!("copy {:?} to {:?}", from, to);
34 fs::copy(&from, &to)?;
35
36 let from_metadata = from.metadata()?;
38 let atime = FileTime::from_last_access_time(&from_metadata);
39 let mtime = FileTime::from_last_modification_time(&from_metadata);
40 set_file_times(&to, atime, mtime)?;
41
42 Ok(())
43}
44
45pub fn path_to_str<'a>(path: &'a Path) -> Result<&'a str> {
46 Ok(path
47 .to_str()
48 .ok_or_else(|| anyhow!("Path is invalid '{}'", path.display()))?)
49}
50
51pub fn normalize_path(path: &Path) -> PathBuf {
52 PathBuf::from(path.to_string_lossy().replace("\\", "/"))
53}
54
55pub fn contains_file_with_ext(dir_path: &Path, ext: &str) -> bool {
56 if !dir_path.is_dir() {
57 return false;
58 };
59 if let Ok(path) = dir_path.read_dir() {
60 for file in path {
61 if let Ok(file) = file {
62 if file.file_name().to_string_lossy().ends_with(ext) {
63 return true;
64 }
65 }
66 }
67 }
68 false
69}
70
71pub fn destructure_path<P: AsRef<Path>>(path: P) -> Option<(PathBuf, String)> {
72 let path = path.as_ref();
73 path.file_name()
74 .and_then(|it| it.to_str())
75 .map(|name| (path.to_path_buf(), name.to_string()))
76}
77
78pub fn file_has_ext(file_path: &Path, ext: &str) -> bool {
79 file_path.is_file()
80 && file_path
81 .file_name()
82 .and_then(|it| it.to_str())
83 .map(|it| it.ends_with(ext))
84 .unwrap_or(false)
85}
86
87pub fn is_library(file_path: &Path) -> bool {
88 file_path.is_file()
89 && file_path
90 .file_name()
91 .and_then(|it| it.to_str())
92 .map(|it| {
93 it.ends_with(".so")
94 || it.contains(".so.")
95 || it.ends_with(".dylib")
96 || it.ends_with(".a")
97 })
98 .unwrap_or(false)
99}
100
101pub fn lib_name_from(file_path: &Path) -> Result<String> {
102 let file_name = file_path
103 .file_name()
104 .and_then(|it| it.to_str())
105 .ok_or_else(|| {
106 anyhow!(
107 "'{}' doesn't point to a valid lib name",
108 file_path.display()
109 )
110 })?;
111
112 let (start_index, end_index) = file_name
113 .find(".so")
114 .map(|end_index| (if file_name.starts_with("lib") { 3 } else { 0 }, end_index))
115 .unwrap_or((0, file_name.len()));
116
117 if start_index == end_index {
118 bail!(
119 "'{}' doesn't point to a valid lib name",
120 file_path.display()
121 );
122 } else {
123 Ok(file_name[start_index..end_index].to_string())
124 }
125}
126
127pub fn file_name_as_str(file_path: &Path) -> Result<&str> {
128 Ok(file_path
129 .file_name()
130 .and_then(|it| it.to_str())
131 .ok_or_else(|| anyhow!("'{}' is not a valid file name", file_path.display()))?)
132}
133
134lazy_static! {
135 static ref CURRENT_VERBOSITY: AtomicI8 = AtomicI8::new(0);
136}
137
138pub fn set_current_verbosity(verbosity: i8) {
139 CURRENT_VERBOSITY.store(verbosity, Ordering::SeqCst)
140}
141pub fn get_current_verbosity() -> i8 {
142 CURRENT_VERBOSITY.load(Ordering::SeqCst)
143}
144
145pub fn user_facing_log(category: &str, message: &str, verbosity: i8) {
146 use colored::Colorize;
147 if verbosity <= get_current_verbosity() {
148 eprintln!("{:>12} {}", category.blue().bold(), message)
149 }
150}
151
152pub trait LogCommandExt {
153 fn log_invocation(&mut self, verbosity: i8) -> &mut Self;
154}
155
156impl LogCommandExt for Command {
157 fn log_invocation(&mut self, verbosity: i8) -> &mut Self {
158 user_facing_log(
159 "Running",
160 &format!(
161 "{}{:?}",
162 if verbosity + 1 < get_current_verbosity() {
163 self.get_envs()
164 .map(|(var_name, var_value)| {
165 format!(
166 "{}={:?} ",
167 var_name.to_str().unwrap(),
168 var_value.and_then(|it| it.to_str()).unwrap_or("")
169 )
170 })
171 .fold(String::new(), |mut result, env| {
172 result.push_str(&env);
173 result
174 })
175 } else {
176 String::new()
177 },
178 self
179 ),
180 verbosity,
181 );
182 self
183 }
184}