(path: &str, what: &str)
| 394 | } |
| 395 | |
| 396 | fn normalize_relative_path(path: &str, what: &str) -> Result<String, String> { |
| 397 | let trimmed = path.trim(); |
| 398 | |
| 399 | if trimmed.is_empty() { |
| 400 | return Err(format!("{what} cannot be empty")); |
| 401 | } |
| 402 | |
| 403 | if trimmed.starts_with('/') || trimmed.starts_with('\\') { |
| 404 | return Err(format!("{what} must be a relative path")); |
| 405 | } |
| 406 | |
| 407 | let bytes = trimmed.as_bytes(); |
| 408 | if bytes.len() >= 2 && bytes[1] == b':' && bytes[0].is_ascii_alphabetic() { |
| 409 | return Err(format!("{what} must be a relative path")); |
| 410 | } |
| 411 | |
| 412 | let mut parts: Vec<&str> = Vec::new(); |
| 413 | for part in trimmed.split(|c| c == '/' || c == '\\') { |
| 414 | if part.is_empty() || part == "." { |
| 415 | continue; |
| 416 | } |
| 417 | if part == ".." { |
| 418 | return Err(format!("{what} cannot contain '..'")); |
| 419 | } |
| 420 | parts.push(part); |
| 421 | } |
| 422 | |
| 423 | if parts.is_empty() { |
| 424 | return Err(format!("{what} is invalid")); |
| 425 | } |
| 426 | |
| 427 | Ok(parts.join("/")) |
| 428 | } |
| 429 | |
| 430 | #[cfg(not(target_arch = "wasm32"))] |
| 431 | fn platform_app_data_dir() -> Result<PathBuf, String> { |
no test coverage detected