|
11 | 11 | //! |
12 | 12 | //! - **🚀 Works with non-existing paths** - Plan file locations before creating them |
13 | 13 | //! - **⚡ Fast** - Optimized performance with minimal allocations and syscalls |
14 | | -//! - **✅ Compatible** - 100% behavioral match with `std::fs::canonicalize` for existing paths |
15 | | -//! - **🔒 Robust** - 436 comprehensive tests covering edge cases and security scenarios |
| 14 | +//! - **✅ Compatible** - 100% behavioral match with `std::fs::canonicalize` for existing paths, with optional UNC simplification via `dunce` feature (Windows) |
| 15 | +//! - **🎯 Virtual filesystem support** - Optional `anchored` feature for bounded canonicalization within directory boundaries |
| 16 | +//! - **🔒 Robust** - 435 comprehensive tests covering edge cases and security scenarios |
16 | 17 | //! - **🛡️ Safe traversal** - Proper `..` and symlink resolution with cycle detection |
17 | 18 | //! - **🌍 Cross-platform** - Windows, macOS, Linux with comprehensive UNC/symlink handling |
18 | 19 | //! - **🔧 Zero dependencies** - Optional features may add dependencies |
|
24 | 25 | //! soft-canonicalize = "0.4" |
25 | 26 | //! ``` |
26 | 27 | //! |
27 | | -//! ### Cross-Platform Example |
| 28 | +//! ### Basic Example |
28 | 29 | //! |
29 | 30 | //! ```rust |
30 | | -//! use soft_canonicalize::soft_canonicalize; |
31 | | -//! |
32 | | -//! // Existing path behaves like std::fs::canonicalize |
33 | | -//! let existing = soft_canonicalize(&std::env::temp_dir())?; |
34 | | -//! # let _ = existing; |
35 | | -//! |
36 | | -//! // Also works when suffixes don't exist yet |
37 | | -//! let non_existing = soft_canonicalize( |
38 | | -//! std::env::temp_dir().join("some/deep/non/existing/path.txt") |
39 | | -//! )?; |
40 | | -//! # let _ = non_existing; |
41 | | -//! # Ok::<(), std::io::Error>(()) |
42 | | -//! ``` |
43 | | -//! |
44 | | -//! ### Windows Example (UNC/extended-length) |
45 | | -//! |
46 | | -//! ```rust |
47 | | -//! use soft_canonicalize::soft_canonicalize; |
48 | | -//! # fn example() -> Result<(), std::io::Error> { |
49 | 31 | //! # #[cfg(windows)] |
50 | 32 | //! # { |
51 | | -//! let p = r"C:\\Users\\user\\documents\\..\\non\\existing\\config.json"; |
52 | | -//! let result = soft_canonicalize(p)?; |
53 | | -//! assert!(result.to_string_lossy().starts_with(r"\\\\?\\C:")); |
54 | | -//! # } |
55 | | -//! # Ok(()) |
| 33 | +//! use soft_canonicalize::soft_canonicalize; |
| 34 | +//! use std::path::PathBuf; |
| 35 | +//! |
| 36 | +//! let non_existing_path = r"C:\Users\user\documents\..\non\existing\config.json"; |
| 37 | +//! |
| 38 | +//! // Using Rust's own std canonicalize function: |
| 39 | +//! let result = std::fs::canonicalize(non_existing_path); |
| 40 | +//! assert!(result.is_err()); |
| 41 | +//! |
| 42 | +//! // Using our crate's function: |
| 43 | +//! let result = soft_canonicalize(non_existing_path); |
| 44 | +//! assert!(result.is_ok()); |
| 45 | +//! |
| 46 | +//! // Shows the UNC path conversion and path normalization |
| 47 | +//! # #[cfg(not(feature = "dunce"))] |
| 48 | +//! assert_eq!( |
| 49 | +//! result.unwrap().to_string_lossy(), |
| 50 | +//! r"\\?\C:\Users\user\non\existing\config.json" |
| 51 | +//! ); |
| 52 | +//! |
| 53 | +//! // With `dunce` feature enabled, paths are simplified when safe |
| 54 | +//! # #[cfg(feature = "dunce")] |
| 55 | +//! assert_eq!( |
| 56 | +//! result.unwrap().to_string_lossy(), |
| 57 | +//! r"C:\Users\user\non\existing\config.json" |
| 58 | +//! ); |
56 | 59 | //! # } |
| 60 | +//! # Ok::<(), std::io::Error>(()) |
57 | 61 | //! ``` |
58 | 62 | //! |
59 | 63 | //! ## How It Works |
|
68 | 72 | //! 8. Optionally canonicalize the anchor (if symlinks seen) and rebuild |
69 | 73 | //! 9. Append non-existing suffix lexically, then normalize if needed |
70 | 74 | //! 10. Windows: ensure extended-length prefix for absolute paths |
| 75 | +//! 11. Optional: simplify Windows paths when `dunce` feature enabled |
71 | 76 | //! |
72 | 77 | //! ## Security Considerations |
73 | 78 | //! |
|
195 | 200 | //! |
196 | 201 | //! ## Testing |
197 | 202 | //! |
198 | | -//! 436 tests including: |
| 203 | +//! 435 tests including: |
199 | 204 | //! - std::fs::canonicalize compatibility tests (existing paths) |
200 | 205 | //! - Path traversal and robustness tests |
201 | 206 | //! - Python pathlib-inspired behavior checks |
@@ -298,6 +303,11 @@ fn reject_nul_bytes(p: &Path) -> io::Result<()> { |
298 | 303 | /// - Unix: Returns standard absolute paths (`/foo`) - no change |
299 | 304 | /// |
300 | 305 | /// See the [module documentation](crate#optional-features) for details on the `dunce` feature. |
| 306 | +#[must_use = "this function returns a new PathBuf without modifying the input"] |
| 307 | +#[doc(alias = "realpath")] |
| 308 | +#[doc(alias = "canonicalize")] |
| 309 | +#[doc(alias = "resolve")] |
| 310 | +#[doc(alias = "absolute")] |
301 | 311 | pub fn soft_canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> { |
302 | 312 | let path = path.as_ref(); |
303 | 313 |
|
@@ -615,6 +625,11 @@ pub fn soft_canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> { |
615 | 625 | /// # #[cfg(unix)] |
616 | 626 | /// # demo().unwrap(); |
617 | 627 | /// ``` |
| 628 | +#[must_use = "this function returns a new PathBuf without modifying the input"] |
| 629 | +#[doc(alias = "chroot")] |
| 630 | +#[doc(alias = "jail")] |
| 631 | +#[doc(alias = "sandbox")] |
| 632 | +#[doc(alias = "virtual_root")] |
618 | 633 | #[cfg(feature = "anchored")] |
619 | 634 | #[cfg_attr(docsrs, doc(cfg(feature = "anchored")))] |
620 | 635 | pub fn anchored_canonicalize( |
|
0 commit comments