1- use crate :: util:: { path:: normalize_path, str:: join_with_capacity, utf8path:: normalize_utf8path} ;
2- use camino:: { Utf8Component , Utf8Path , Utf8PathBuf } ;
1+ use crate :: util:: {
2+ path:: normalize_path,
3+ str:: join_with_capacity,
4+ utf8path:: { normalize_utf8path, sanitize_utf8path} ,
5+ } ;
6+ use camino:: { Utf8Path , Utf8PathBuf } ;
37use std:: borrow:: Cow ;
48use std:: error:: Error ;
59use std:: ffi:: { OsStr , OsString } ;
610use std:: fmt:: { self , Display , Formatter } ;
7- use std:: path:: { Component , Path , PathBuf } ;
11+ use std:: path:: { Path , PathBuf } ;
812use std:: str:: { self , Utf8Error } ;
913
1014/// A UTF-8 encoded entry name.
@@ -25,15 +29,7 @@ pub struct EntryName(String);
2529
2630impl EntryName {
2731 fn new_from_utf8path ( path : & Utf8Path ) -> Self {
28- let path = normalize_utf8path ( path) ;
29- let iter = path. components ( ) . filter_map ( |c| match c {
30- Utf8Component :: Prefix ( _)
31- | Utf8Component :: RootDir
32- | Utf8Component :: CurDir
33- | Utf8Component :: ParentDir => None ,
34- Utf8Component :: Normal ( p) => Some ( p) ,
35- } ) ;
36- Self ( join_with_capacity ( iter, "/" , path. as_str ( ) . len ( ) ) )
32+ Self :: new_from_utf8path_preserve_root ( path) . sanitize ( )
3733 }
3834
3935 #[ inline]
@@ -69,16 +65,122 @@ impl EntryName {
6965 Self :: from_path_lossy ( & p. into ( ) )
7066 }
7167
68+ #[ inline]
7269 fn from_path_lossy ( p : & Path ) -> Self {
73- let p = normalize_path ( p) ;
74- let iter = p. components ( ) . filter_map ( |c| match c {
75- Component :: Prefix ( _)
76- | Component :: RootDir
77- | Component :: CurDir
78- | Component :: ParentDir => None ,
79- Component :: Normal ( p) => Some ( p. to_string_lossy ( ) ) ,
80- } ) ;
81- Self ( join_with_capacity ( iter, "/" , p. as_os_str ( ) . len ( ) ) )
70+ Self :: from_path_lossy_preserve_root ( p) . sanitize ( )
71+ }
72+
73+ /// Creates an [EntryName] from a path, preserving absolute path components.
74+ ///
75+ /// This method is similar to the `From` implementations for path-like types, but preserves absolute path components.
76+ ///
77+ /// # Examples
78+ ///
79+ /// ```rust
80+ /// use libpna::EntryName;
81+ ///
82+ /// assert_eq!("foo.txt", EntryName::from_utf8_preserve_root("foo.txt"));
83+ /// #[cfg(windows)]
84+ /// assert_eq!("\\foo.txt", EntryName::from_utf8_preserve_root("/foo.txt"));
85+ /// #[cfg(unix)]
86+ /// assert_eq!("/foo.txt", EntryName::from_utf8_preserve_root("/foo.txt"));
87+ /// assert_eq!("foo.txt", EntryName::from_utf8_preserve_root("./foo.txt"));
88+ /// #[cfg(windows)]
89+ /// assert_eq!("..\\foo.txt", EntryName::from_utf8_preserve_root("../foo.txt"));
90+ /// #[cfg(unix)]
91+ /// assert_eq!("../foo.txt", EntryName::from_utf8_preserve_root("../foo.txt"));
92+ /// assert_eq!("foo.txt", EntryName::from_utf8_preserve_root("bar/../foo.txt"));
93+ /// ```
94+ #[ inline]
95+ pub fn from_utf8_preserve_root ( path : & str ) -> Self {
96+ Self :: new_from_utf8path_preserve_root ( Utf8Path :: new ( path) )
97+ }
98+
99+ #[ inline]
100+ fn new_from_utf8path_preserve_root ( path : & Utf8Path ) -> Self {
101+ let path = normalize_utf8path ( path) ;
102+ Self ( path. into_string ( ) )
103+ }
104+
105+ /// Creates an [EntryName] from a path, preserving absolute path components.
106+ ///
107+ /// This method is similar to the `From` implementations for path-like types, but preserves absolute path components.
108+ ///
109+ /// # Examples
110+ ///
111+ /// ```rust
112+ /// use libpna::EntryName;
113+ ///
114+ /// assert_eq!("foo.txt", EntryName::from_path_preserve_root("foo.txt".as_ref()).unwrap());
115+ /// #[cfg(windows)]
116+ /// assert_eq!("\\foo.txt", EntryName::from_path_preserve_root("/foo.txt".as_ref()).unwrap());
117+ /// #[cfg(unix)]
118+ /// assert_eq!("/foo.txt", EntryName::from_path_preserve_root("/foo.txt".as_ref()).unwrap());
119+ /// assert_eq!("foo.txt", EntryName::from_path_preserve_root("./foo.txt".as_ref()).unwrap());
120+ /// #[cfg(windows)]
121+ /// assert_eq!("..\\foo.txt", EntryName::from_path_preserve_root("../foo.txt".as_ref()).unwrap());
122+ /// #[cfg(unix)]
123+ /// assert_eq!("../foo.txt", EntryName::from_path_preserve_root("../foo.txt".as_ref()).unwrap());
124+ /// assert_eq!("foo.txt", EntryName::from_path_preserve_root("bar/../foo.txt".as_ref()).unwrap());
125+ /// ```
126+ #[ inline]
127+ pub fn from_path_preserve_root ( name : & Path ) -> Result < Self , EntryNameError > {
128+ let path = str:: from_utf8 ( name. as_os_str ( ) . as_encoded_bytes ( ) ) ?;
129+ Ok ( Self :: new_from_utf8path_preserve_root ( Utf8Path :: new ( path) ) )
130+ }
131+
132+ /// Creates an [EntryName] from a path, preserving absolute path components.
133+ ///
134+ /// This method is similar to the `From` implementations for path-like types, but preserves absolute path components.
135+ ///
136+ /// # Errors
137+ ///
138+ /// Returns an [`EntryNameError`] if it cannot be represented as valid UTF-8.
139+ ///
140+ /// # Examples
141+ ///
142+ /// ```rust
143+ /// use libpna::EntryName;
144+ ///
145+ /// assert_eq!("foo.txt", EntryName::from_path_lossy_preserve_root("foo.txt".as_ref()));
146+ /// #[cfg(windows)]
147+ /// assert_eq!("\\foo.txt", EntryName::from_path_lossy_preserve_root("/foo.txt".as_ref()));
148+ /// #[cfg(unix)]
149+ /// assert_eq!("/foo.txt", EntryName::from_path_lossy_preserve_root("/foo.txt".as_ref()));
150+ /// assert_eq!("foo.txt", EntryName::from_path_lossy_preserve_root("./foo.txt".as_ref()));
151+ /// #[cfg(windows)]
152+ /// assert_eq!("..\\foo.txt", EntryName::from_path_lossy_preserve_root("../foo.txt".as_ref()));
153+ /// #[cfg(unix)]
154+ /// assert_eq!("../foo.txt", EntryName::from_path_lossy_preserve_root("../foo.txt".as_ref()));
155+ /// assert_eq!("foo.txt", EntryName::from_path_lossy_preserve_root("bar/../foo.txt".as_ref()));
156+ /// ```
157+ #[ inline]
158+ pub fn from_path_lossy_preserve_root ( name : & Path ) -> Self {
159+ let p = normalize_path ( name) ;
160+ Self :: new_from_utf8path_preserve_root ( Utf8Path :: new ( & p. to_string_lossy ( ) ) )
161+ }
162+
163+ /// Returns a sanitized copy of this entry name that contains only normal components.
164+ ///
165+ /// Sanitization discards prefixes, root separators, `.` and `..` segments so the
166+ /// resulting entry name is always relative and safe to embed in an archive.
167+ ///
168+ /// # Examples
169+ ///
170+ /// ```rust
171+ /// use libpna::EntryName;
172+ ///
173+ /// let name = EntryName::from_utf8_preserve_root("/var/../tmp/./log");
174+ /// assert_eq!("tmp/log", name.sanitize());
175+ /// ```
176+ #[ inline]
177+ pub fn sanitize ( & self ) -> Self {
178+ let path = sanitize_utf8path ( & self . 0 ) ;
179+ Self ( join_with_capacity (
180+ path. components ( ) ,
181+ "/" ,
182+ path. as_str ( ) . len ( ) ,
183+ ) )
82184 }
83185
84186 #[ inline]
0 commit comments