Skip to main content

vpxtool/
lib.rs

1use std::fs::metadata;
2use std::io;
3use std::path::{Path, PathBuf};
4
5mod backglass;
6pub mod fixprint;
7mod frontend;
8pub mod patcher;
9
10pub mod config;
11
12pub mod indexer;
13
14pub mod cli;
15mod colorful_theme_patched;
16pub mod vpinball_config;
17
18pub fn strip_cr_lf(s: &str) -> String {
19    s.chars().filter(|c| !c.is_ascii_whitespace()).collect()
20}
21
22fn expand_path<S: AsRef<str>>(path: S) -> PathBuf {
23    shellexpand::tilde(path.as_ref()).to_string().into()
24}
25
26fn expand_path_exists<S: AsRef<str>>(path: S) -> io::Result<PathBuf> {
27    // TODO expand all instead of only tilde?
28    let expanded_path = shellexpand::tilde(path.as_ref());
29    path_exists(&PathBuf::from(expanded_path.to_string()))
30}
31
32fn path_exists(expanded_path: &Path) -> io::Result<PathBuf> {
33    match metadata(expanded_path) {
34        Ok(md) => {
35            if !md.is_file() && !md.is_dir() && md.is_symlink() {
36                Err(io::Error::new(
37                    io::ErrorKind::InvalidInput,
38                    format!("{} is not a file", expanded_path.display()),
39                ))
40            } else {
41                Ok(expanded_path.to_path_buf())
42            }
43        }
44        Err(msg) => {
45            let warning = format!(
46                "Failed to read metadata for {}: {}",
47                expanded_path.display(),
48                msg
49            );
50            Err(io::Error::new(io::ErrorKind::InvalidInput, warning))
51        }
52    }
53}
54
55fn os_independent_file_name(file_path: String) -> Option<String> {
56    // we can't use path here as this uses the system path encoding
57    // we might have to parse windows paths on mac/linux
58    if file_path.is_empty() {
59        return None;
60    }
61    file_path.rsplit(['/', '\\']).next().map(|f| f.to_string())
62}
63
64/// Path to file that will be removed when it goes out of scope
65struct RemoveOnDrop {
66    path: PathBuf,
67}
68impl RemoveOnDrop {
69    fn new(path: PathBuf) -> Self {
70        RemoveOnDrop { path }
71    }
72
73    fn path(&self) -> &Path {
74        &self.path
75    }
76}
77
78impl Drop for RemoveOnDrop {
79    fn drop(&mut self) {
80        if self.path.exists() {
81            // silently ignore any errors
82            let _ = std::fs::remove_file(&self.path);
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_os_independent_file_name_windows() {
93        let file_path = "C:\\Users\\user\\Desktop\\file.txt";
94        let result = os_independent_file_name(file_path.to_string());
95        assert_eq!(result, Some("file.txt".to_string()));
96    }
97
98    #[test]
99    fn test_os_independent_file_unix() {
100        let file_path = "/users/joe/file.txt";
101        let result = os_independent_file_name(file_path.to_string());
102        assert_eq!(result, Some("file.txt".to_string()));
103    }
104
105    #[test]
106    fn test_os_independent_file_name_no_extension() {
107        let file_path = "C:\\Users\\user\\Desktop\\file";
108        let result = os_independent_file_name(file_path.to_string());
109        assert_eq!(result, Some("file".to_string()));
110    }
111
112    #[test]
113    fn test_os_independent_file_name_no_path() {
114        let file_path = "file.txt";
115        let result = os_independent_file_name(file_path.to_string());
116        assert_eq!(result, Some("file.txt".to_string()));
117    }
118
119    #[test]
120    fn test_os_independent_file_name_no_path_no_extension() {
121        let file_path = "file";
122        let result = os_independent_file_name(file_path.to_string());
123        assert_eq!(result, Some("file".to_string()));
124    }
125
126    #[test]
127    fn test_os_independent_file_name_empty() {
128        let file_path = "";
129        let result = os_independent_file_name(file_path.to_string());
130        assert_eq!(result, None);
131    }
132}