dinghy_lib/
utils.rs

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    // Make target file writeable if it is read-only.
25    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    // Keep filetime to avoid useless sync on some devices (e.g. Android).
37    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}