1use std::{
2 borrow::Cow, cell::RefCell, convert::Infallible, error::Error as StdError, fmt, io,
3 path::Path, path::PathBuf,
4};
5
6use ntex_bytes::ByteString;
7
8use crate::{Error, ErrorDiagnostic, ResultType};
9
10impl ErrorDiagnostic for Infallible {
11 type Kind = ResultType;
12
13 fn kind(&self) -> Self::Kind {
14 unreachable!()
15 }
16}
17
18impl ErrorDiagnostic for io::Error {
19 type Kind = ResultType;
20
21 fn kind(&self) -> Self::Kind {
22 match self.kind() {
23 io::ErrorKind::InvalidData
24 | io::ErrorKind::Unsupported
25 | io::ErrorKind::UnexpectedEof
26 | io::ErrorKind::BrokenPipe
27 | io::ErrorKind::ConnectionReset
28 | io::ErrorKind::ConnectionAborted
29 | io::ErrorKind::NotConnected
30 | io::ErrorKind::TimedOut => ResultType::ClientError,
31 _ => ResultType::ServiceError,
32 }
33 }
34}
35
36#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
37pub struct Success;
38
39impl StdError for Success {}
40
41impl ErrorDiagnostic for Success {
42 type Kind = ResultType;
43
44 fn kind(&self) -> Self::Kind {
45 ResultType::Success
46 }
47}
48
49impl fmt::Display for Success {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "Success")
52 }
53}
54
55pub async fn with_service<F, T, E>(svc: &'static str, fut: F) -> F::Output
59where
60 F: Future<Output = Result<T, Error<E>>>,
61 E: ErrorDiagnostic + Clone,
62{
63 fut.await.map_err(|err: Error<E>| {
64 if err.service().is_none() {
65 err.set_service(svc)
66 } else {
67 err
68 }
69 })
70}
71
72pub fn module_path(file_path: &str) -> ByteString {
74 module_path_ext("", "", "::", "", file_path)
75}
76
77pub fn module_path_prefix(prefix: &'static str, file_path: &str) -> ByteString {
79 module_path_ext(prefix, "", "::", "", file_path)
80}
81
82pub fn module_path_fs(file_path: &str) -> ByteString {
84 module_path_ext("", "/src", "/", ".rs", file_path)
85}
86
87fn module_path_ext(
88 prefix: &'static str,
89 mod_sep: &str,
90 sep: &str,
91 suffix: &str,
92 file_path: &str,
93) -> ByteString {
94 type HashMap<K, V> = std::collections::HashMap<K, V, foldhash::fast::RandomState>;
95 thread_local! {
96 static CACHE: RefCell<HashMap<&'static str, HashMap<String, ByteString>>> = RefCell::new(HashMap::default());
97 }
98
99 let cached = CACHE.with(|cache| {
100 if let Some(c) = cache.borrow().get(prefix) {
101 c.get(file_path).cloned()
102 } else {
103 None
104 }
105 });
106
107 if let Some(cached) = cached {
108 cached
109 } else {
110 let normalized_file_path = normalize_file_path(file_path);
111 let (module_name, module_root) =
112 module_root_from_file(mod_sep, &normalized_file_path);
113 let module = module_path_from_file_with_root(
114 prefix,
115 sep,
116 &normalized_file_path,
117 &module_name,
118 &module_root,
119 suffix,
120 );
121
122 let _ = CACHE.with(|cache| {
123 cache
124 .borrow_mut()
125 .entry(prefix)
126 .or_default()
127 .insert(file_path.to_string(), module.clone())
128 });
129 module
130 }
131}
132
133fn normalize_file_path(file_path: &str) -> String {
134 let path = Path::new(file_path);
135 if path.is_absolute() {
136 return path.to_string_lossy().into_owned();
137 }
138
139 match std::env::current_dir() {
140 Ok(cwd) => cwd.join(path).to_string_lossy().into_owned(),
141 Err(_) => file_path.to_string(),
142 }
143}
144
145fn module_root_from_file(mod_sep: &str, file_path: &str) -> (String, PathBuf) {
147 let normalized = file_path.replace('\\', "/");
148 if let Some((root, _)) = normalized.rsplit_once("/src/") {
149 let mut root = PathBuf::from(root);
150 let mod_name = root
151 .file_name()
152 .map_or(Cow::Borrowed("crate"), |s| s.to_string_lossy());
153 let mod_name = if mod_sep.is_empty() {
154 mod_name.replace('-', "_")
155 } else {
156 mod_name.to_string()
157 };
158 root.push("src");
159 return (format!("{mod_name}{mod_sep}"), root);
160 }
161
162 let path = Path::new(file_path)
163 .parent()
164 .map_or_else(|| PathBuf::from("."), Path::to_path_buf);
165
166 let m = path
167 .parent()
168 .and_then(|p| p.file_name())
169 .map_or_else(|| Cow::Borrowed("crate"), |p| p.to_string_lossy());
170
171 (format!("{m}{mod_sep}"), path)
172}
173
174fn module_path_from_file(sep: &str, file_path: &str) -> String {
176 let normalized = file_path.replace('\\', "/");
177 let relative = normalized
178 .split_once("/src/")
179 .map_or(normalized.as_str(), |(_, tail)| tail);
180
181 if relative == "lib.rs" || relative == "main.rs" {
182 return relative.to_string();
183 }
184
185 let without_ext = relative.strip_suffix(".rs").unwrap_or(relative);
186 if without_ext.ends_with("/mod") {
187 let parent = without_ext.strip_suffix("/mod").unwrap_or(without_ext);
188 let parent = parent.trim_matches('/');
189 return parent.replace('/', sep);
190 }
191
192 let module = without_ext.trim_matches('/').replace('/', sep);
193 if module.is_empty() {
194 "crate".to_string()
195 } else {
196 module
197 }
198}
199
200fn module_path_from_file_with_root(
202 prefix: &str,
203 sep: &str,
204 file_path: &str,
205 module_name: &str,
206 module_root: &Path,
207 suffix: &str,
208) -> ByteString {
209 let normalized = file_path.replace('\\', "/");
210 let module_root_norm = module_root.to_string_lossy().replace('\\', "/");
211
212 let Some(relative) = normalized.strip_prefix(&(module_root_norm.clone() + "/")) else {
213 return format!(
214 "{prefix}{module_name}{sep}{}{suffix}",
215 module_path_from_file(sep, file_path)
216 )
217 .into();
218 };
219 if relative == "lib.rs" || relative == "main.rs" {
220 return ByteString::from(format!("{prefix}{module_name}{sep}{relative}"));
221 }
222
223 let without_ext = relative.strip_suffix(".rs").unwrap_or(relative);
224 if without_ext.ends_with("/mod") {
225 let parent = without_ext.strip_suffix("/mod").unwrap_or(without_ext);
226 let parent = parent.trim_matches('/');
227 return format!(
228 "{prefix}{module_name}{sep}{}{sep}mod{suffix}",
229 parent.replace('/', sep)
230 )
231 .into();
232 }
233
234 let module = without_ext.trim_matches('/').replace('/', sep);
235 if module.is_empty() {
236 ByteString::from(format!("{prefix}{module_name}{suffix}"))
237 } else {
238 format!("{prefix}{module_name}{sep}{module}{suffix}").into()
239 }
240}