darklua_core/utils/
mod.rs

1mod expressions_as_statement;
2mod serde_string_or_struct;
3#[cfg(not(target_arch = "wasm32"))]
4mod timer;
5#[cfg(target_arch = "wasm32")]
6mod wasm_timer;
7
8pub(crate) use expressions_as_statement::{expressions_as_expression, expressions_as_statement};
9pub(crate) use serde_string_or_struct::string_or_struct;
10#[cfg(not(target_arch = "wasm32"))]
11pub use timer::Timer;
12#[cfg(target_arch = "wasm32")]
13pub use wasm_timer::Timer;
14
15use std::{
16    ffi::OsStr,
17    iter::FromIterator,
18    path::{Component, Path, PathBuf},
19};
20
21use crate::DarkluaError;
22
23pub(crate) fn convert_os_string(os_str: &OsStr) -> Result<&str, DarkluaError> {
24    os_str
25        .to_str()
26        .ok_or_else(|| DarkluaError::os_string_conversion(os_str))
27}
28
29pub(crate) fn normalize_path(path: impl AsRef<Path>) -> PathBuf {
30    normalize(path, false)
31}
32
33pub(crate) fn normalize_path_with_current_dir(path: impl AsRef<Path>) -> PathBuf {
34    normalize(path, true)
35}
36
37#[inline]
38fn current_dir() -> &'static OsStr {
39    OsStr::new(".")
40}
41
42#[inline]
43fn parent_dir() -> &'static OsStr {
44    OsStr::new("..")
45}
46
47fn normalize(path: impl AsRef<Path>, keep_current_dir: bool) -> PathBuf {
48    let path = path.as_ref();
49
50    if path == Path::new("") {
51        return PathBuf::new();
52    }
53
54    let mut components = path.components().peekable();
55    let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
56        components.next();
57        vec![c.as_os_str()]
58    } else {
59        Vec::new()
60    };
61
62    for component in components {
63        match component {
64            Component::Prefix(..) => unreachable!(),
65            Component::RootDir => {
66                ret.push(component.as_os_str());
67            }
68            Component::CurDir => {
69                if keep_current_dir && ret.is_empty() {
70                    ret.push(current_dir());
71                }
72            }
73            Component::ParentDir => {
74                if let Some(last) = ret.last() {
75                    let last = *last;
76                    if last == current_dir() {
77                        ret.pop();
78                        ret.push(parent_dir());
79                    } else if last != parent_dir() {
80                        ret.pop();
81                    } else {
82                        ret.push(parent_dir());
83                    }
84                } else {
85                    ret.push(parent_dir());
86                }
87            }
88            Component::Normal(c) => {
89                ret.push(c);
90            }
91        }
92    }
93
94    if ret.is_empty() {
95        ret.push(OsStr::new("."));
96    }
97
98    PathBuf::from_iter(ret)
99}
100
101#[cfg(test)]
102mod test {
103    use super::*;
104
105    fn verify_normalize_path(input: impl AsRef<Path>, output: impl AsRef<Path>) {
106        assert_eq!(normalize_path(input.as_ref()), output.as_ref());
107    }
108
109    #[test]
110    fn current_directory_with_name() {
111        verify_normalize_path("./directory", "directory")
112    }
113
114    #[test]
115    fn from_directory() {
116        verify_normalize_path("/directory", "/directory")
117    }
118
119    #[test]
120    fn parent_directory_path() {
121        verify_normalize_path("..", "..")
122    }
123
124    #[test]
125    fn current_directory_path() {
126        verify_normalize_path(".", ".")
127    }
128
129    #[test]
130    fn src_parent_with_directory_name() {
131        verify_normalize_path("src/../directory", "directory")
132    }
133
134    #[test]
135    fn src_current_dir_with_directory_name() {
136        verify_normalize_path("src/./directory", "src/directory")
137    }
138
139    #[test]
140    fn double_parent_directory_path() {
141        verify_normalize_path("../..", "../..")
142    }
143
144    #[test]
145    fn current_dir_parent_directory_path() {
146        verify_normalize_path("./..", "..")
147    }
148
149    #[test]
150    fn parent_directory_of_directory_inside_current_path() {
151        verify_normalize_path("./directory/..", ".")
152    }
153
154    #[test]
155    fn empty_path() {
156        verify_normalize_path("", "")
157    }
158
159    mod with_current_dir {
160        use super::*;
161
162        fn verify_normalize_path_with_current_dir(
163            input: impl AsRef<Path>,
164            output: impl AsRef<Path>,
165        ) {
166            assert_eq!(
167                normalize_path_with_current_dir(input.as_ref()),
168                output.as_ref()
169            );
170        }
171
172        #[test]
173        fn current_directory_with_name() {
174            verify_normalize_path_with_current_dir("./directory", "./directory")
175        }
176
177        #[test]
178        fn src_parent_with_directory_name_from_current_directory() {
179            verify_normalize_path_with_current_dir("./src/../directory", "./directory")
180        }
181
182        #[test]
183        fn current_dir_parent_directory_path() {
184            verify_normalize_path_with_current_dir("./..", "..")
185        }
186
187        #[test]
188        fn current_directory_path() {
189            verify_normalize_path_with_current_dir(".", ".")
190        }
191
192        #[test]
193        fn parent_directory_of_directory_inside_current_path() {
194            verify_normalize_path_with_current_dir("./directory/..", ".")
195        }
196
197        #[test]
198        fn empty_path() {
199            verify_normalize_path_with_current_dir("", "")
200        }
201    }
202}