darklua_core/utils/
mod.rs1mod 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}