realpath/util.rs
1use std::path::PathBuf;
2
3extern crate path_absolutize;
4use path_absolutize::Absolutize;
5
6#[no_mangle]
7///Mutates the path in-place
8///Canonicalizes the entire path, if it is possible
9///If it is not possible, then it canonicalizes the head of the path and then appends the tail to it
10///Repeats the process until either the head is successfully canonicalized or the head is the root directory
11fn step_by_step_canonicalize(path: &mut PathBuf) {
12
13 let mut components = path.components().collect::<Vec<_>>();
14 let mut head = path.clone();
15 let mut tail = PathBuf::new();
16 let mut result = head.canonicalize();
17 let mut component;
18 let mut cx;
19
20 //Canonicalize the head
21 //If error is raised, head = head.parent
22 //And then loop again
23 //If head.parent is None, then return the original path without mutation
24 //Otherwise, return the canonicalized head with the tail components attached
25 while result.is_err(){
26 head = match head.parent(){
27 Some(parent) => parent.to_path_buf(),
28 None => return,
29 };
30
31 result = head.canonicalize();
32 component = components.pop();
33
34 match component{
35 Some(component) => {
36 //tail = component\tail
37 cx = PathBuf::from(component.as_os_str());
38 tail = cx.join(tail);
39 }
40 None => return,
41 }
42 }
43
44 let result = result.unwrap().join(tail);
45 let result = result.absolutize().unwrap().to_path_buf();
46
47 //Remove last / from the result
48
49 *path = result;
50
51}
52
53
54///Takes a PathBuf as input
55///Normalizes it, removes any trailing slashes, intermediate dots, and such
56///Traces symlinks and processes as far as can be canonicalized
57///If only a part of the path is canonicalize-able, then the remaining part is appended as is to the canonicalized part
58///Returns the canonicalized path as a PathBuf
59///Returns an Error if there is some problem with absolutization/canonicalization
60///Error => std::io::Error
61///Only convern : Returns \\?\Drive:\path\to\file instead of Drive:\path\to\file on Windows
62#[inline]
63pub fn realpath_og(path: &PathBuf) -> Result<PathBuf, std::io::Error> {
64
65 let mut path = path.absolutize()?.to_path_buf();
66
67 step_by_step_canonicalize(&mut path);
68 Ok(path)
69}
70
71pub fn realpath(path : &PathBuf) -> Result<PathBuf, std::io::Error> {
72
73 #[cfg(target_os = "windows")]
74 {
75 realpath_win(path, true)
76 }
77
78 #[cfg(not(target_os = "windows"))]
79 {
80 realpath_og(path)
81 }
82
83}
84
85#[cfg(target_os = "windows")]
86#[inline]
87///Similar to realpath, but will convert \\?\Drive:\path\to\file to Drive:\path\to\file if it is possible and valid if `dl=true`
88///But does not convert \\?\UNC\server\share\path\to\file to \\server\share\path\to\file
89pub fn realpath_win(path : &PathBuf, dl : bool) -> Result<PathBuf, std::io::Error> {
90 let mut path = path.absolutize()?.to_path_buf();
91 step_by_step_canonicalize(&mut path);
92
93 println!("{}" , path.display());
94
95 if dl {
96
97 //It can be \\?\Drive:\path\to\file or \\?\UNC\server\share\path\to\file
98 //In case of UNC, return as is
99 //In case of Drive, remove \\?\ and return
100
101 let conv = path.to_string_lossy().to_string();
102
103 match conv.strip_prefix(include_str!("../unc.txt")){
104 Some(res) => return Ok(PathBuf::from(res)),
105 None => (),
106 };
107
108 match conv.strip_prefix(include_str!("../straight.txt")){
109 Some(res) => return Ok(PathBuf::from(res)),
110 None => (),
111 };
112 }
113
114 Ok(path)
115}