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}