vite_rust/utils/
resolve_path.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::path::{Path, PathBuf, Component,};
use std::env::current_dir;

/// Experimental utility for resolving the manifest path.
/// 
/// # Arguments
/// * `file`    - the current script file path (obtained with `file!()` macro);
/// * `path`    - the "path/to/manifest.json" string slice.
/// 
/// # Panics
/// This function might panic in various occasions:
/// - if `file` has no parent directory (e.g. "src/main.rs" parent would be "src");
/// - if it fails to find the `file` first segment somehow (e.g. "path/to/file" => "path");
/// - if the first file path [`Component`] is neither `RootDir` nor `Normal`;
/// - if final result fails to be canonicalized.
/// 
/// The last situation might occur if the final path doesn't really lead to
/// any existing file/directory. Or perhaps, because this function has a bug!
/// In this case, please open an issue at our GitHub repository!
/// 
/// # Example
/// ```plaintext
/// example/
/// |-- .dist/
/// |   |-- .vite/
/// |   |   |-- manifest.json
/// |-- src/
/// |   |-- main.rs
/// ```
/// ```ignore
/// 
/// // example/src/main.rs
/// let manifest_path = resolve_path(file!(), "../dist/.vite/manifest.json");
/// let mut vite_config = ViteConfig::new_with_defaults(&manifest_path);
/// 
/// println!("{manifest_path}");
/// // C:/totally/absolute/path/to/example/.dist/.vite/manifest.json
/// ```
/// 
/// [`Component`]: std::path::Component
pub fn resolve_path(file: &str, path: &str) -> String {
    let path: &Path = std::path::Path::new(path);
    let mut this_file_directory = Path::new(file)
        .parent()
        .expect("Could not get current file's directory.")
        .to_path_buf();
    
    #[allow(unused_mut)]
    let mut fp_fs: String; // file path first segment
    match this_file_directory.components().next() {
        Some(Component::Normal(segment)) => fp_fs = segment.to_string_lossy().to_string(),
        Some(Component::RootDir) => {
            let component = this_file_directory
                .components()
                .next()
                .expect(&format!(
                    "Failed to find first directory segment from path {}.",
                    this_file_directory.to_string_lossy())
                );

            match component {
                Component::Normal(segment) => fp_fs = segment.to_string_lossy().to_string(),
                _ => {
                    panic!(
                        "Failed to find first directory normal segment from path {}.",
                        this_file_directory.to_string_lossy()
                    )
                }
            }
                
        },
        _ => panic!("Unexpected kind of directory."),
    }

    let curr_dir = current_dir().unwrap();

    let paths_are_redundant = curr_dir.ends_with(&fp_fs) &&
        this_file_directory.starts_with(&fp_fs);
    
    // remove the first segment from this_file_directory so that it won't get doubled
    // on canonicalization
    if paths_are_redundant  {
        let mut new_path = PathBuf::new();
        let mut components = this_file_directory.components();

        if let Some(_) = components.next() {
            components.for_each(|component| new_path.push(component));
        }

        this_file_directory = new_path;
    }

    let joined_path = this_file_directory.join(path);
    let canonicalized = joined_path.canonicalize();
    match canonicalized {
        Err(err) => panic!("{}\n{}\n", err, joined_path.to_string_lossy()),
        Ok(path) => path.to_string_lossy().to_string()
    }
}

#[cfg(test)]
mod test {
    use std::io::Read;

    #[test]
    fn test_resolve_path() {
        let abs_path = "tests/dummy.txt";
        let rel_path = "../../tests/dummy.txt";
        let resolved_rel_path = super::resolve_path(file!(), &rel_path);

        let mut abs_file_contents = String::new();
        let mut rel_file_contents = String::new();

        let _ = std::fs::File::open(abs_path).unwrap().read_to_string(&mut abs_file_contents);
        let _ = std::fs::File::open(resolved_rel_path).unwrap().read_to_string(&mut rel_file_contents);

        assert_eq!(abs_file_contents, rel_file_contents);
    }
}