@@ -25,15 +25,7 @@ pub struct EntryName(String);
2525
2626impl EntryName {
2727 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 ( ) ) )
28+ Self :: new_from_utf8path_preserve_root ( path) . sanitize ( )
3729 }
3830
3931 #[ inline]
@@ -71,7 +63,120 @@ impl EntryName {
7163
7264 #[ inline]
7365 fn from_path_lossy ( p : & Path ) -> Self {
74- Self :: new_from_utf8 ( & p. to_string_lossy ( ) )
66+ Self :: from_path_lossy_preserve_root ( p) . sanitize ( )
67+ }
68+
69+ /// Creates an [EntryName] from a path, preserving absolute path components.
70+ ///
71+ /// This method is similar to the `From` implementations for path-like types, but preserves absolute path components.
72+ ///
73+ /// # Examples
74+ ///
75+ /// ```rust
76+ /// use libpna::EntryName;
77+ ///
78+ /// assert_eq!("foo.txt", EntryName::from_utf8_preserve_root("foo.txt"));
79+ /// #[cfg(windows)]
80+ /// assert_eq!("\\foo.txt", EntryName::from_utf8_preserve_root("/foo.txt"));
81+ /// #[cfg(unix)]
82+ /// assert_eq!("/foo.txt", EntryName::from_utf8_preserve_root("/foo.txt"));
83+ /// assert_eq!("foo.txt", EntryName::from_utf8_preserve_root("./foo.txt"));
84+ /// #[cfg(windows)]
85+ /// assert_eq!("..\\foo.txt", EntryName::from_utf8_preserve_root("../foo.txt"));
86+ /// #[cfg(unix)]
87+ /// assert_eq!("../foo.txt", EntryName::from_utf8_preserve_root("../foo.txt"));
88+ /// assert_eq!("foo.txt", EntryName::from_utf8_preserve_root("bar/../foo.txt"));
89+ /// ```
90+ #[ inline]
91+ pub fn from_utf8_preserve_root ( path : & str ) -> Self {
92+ Self :: new_from_utf8path_preserve_root ( Utf8Path :: new ( path) )
93+ }
94+
95+ #[ inline]
96+ fn new_from_utf8path_preserve_root ( path : & Utf8Path ) -> Self {
97+ let path = normalize_utf8path ( path) ;
98+ Self ( path. into_string ( ) )
99+ }
100+
101+ /// Creates an [EntryName] from a path, preserving absolute path components.
102+ ///
103+ /// This method is similar to the `From` implementations for path-like types, but preserves absolute path components.
104+ ///
105+ /// # Examples
106+ ///
107+ /// ```rust
108+ /// use libpna::EntryName;
109+ ///
110+ /// assert_eq!("foo.txt", EntryName::from_path_preserve_root("foo.txt".as_ref()).unwrap());
111+ /// #[cfg(windows)]
112+ /// assert_eq!("\\foo.txt", EntryName::from_path_preserve_root("/foo.txt".as_ref()).unwrap());
113+ /// #[cfg(unix)]
114+ /// assert_eq!("/foo.txt", EntryName::from_path_preserve_root("/foo.txt".as_ref()).unwrap());
115+ /// assert_eq!("foo.txt", EntryName::from_path_preserve_root("./foo.txt".as_ref()).unwrap());
116+ /// #[cfg(windows)]
117+ /// assert_eq!("..\\foo.txt", EntryName::from_path_preserve_root("../foo.txt".as_ref()).unwrap());
118+ /// #[cfg(unix)]
119+ /// assert_eq!("../foo.txt", EntryName::from_path_preserve_root("../foo.txt".as_ref()).unwrap());
120+ /// assert_eq!("foo.txt", EntryName::from_path_preserve_root("bar/../foo.txt".as_ref()).unwrap());
121+ /// ```
122+ #[ inline]
123+ pub fn from_path_preserve_root ( name : & Path ) -> Result < Self , EntryNameError > {
124+ let path = str:: from_utf8 ( name. as_os_str ( ) . as_encoded_bytes ( ) ) ?;
125+ Ok ( Self :: new_from_utf8path_preserve_root ( Utf8Path :: new ( path) ) )
126+ }
127+
128+ /// Creates an [EntryName] from a path, preserving absolute path components.
129+ ///
130+ /// This method is similar to the `From` implementations for path-like types, but preserves absolute path components.
131+ ///
132+ /// # Errors
133+ ///
134+ /// Returns an [`EntryNameError`] if it cannot be represented as valid UTF-8.
135+ ///
136+ /// # Examples
137+ ///
138+ /// ```rust
139+ /// use libpna::EntryName;
140+ ///
141+ /// assert_eq!("foo.txt", EntryName::from_path_lossy_preserve_root("foo.txt".as_ref()));
142+ /// #[cfg(windows)]
143+ /// assert_eq!("\\foo.txt", EntryName::from_path_lossy_preserve_root("/foo.txt".as_ref()));
144+ /// #[cfg(unix)]
145+ /// assert_eq!("/foo.txt", EntryName::from_path_lossy_preserve_root("/foo.txt".as_ref()));
146+ /// assert_eq!("foo.txt", EntryName::from_path_lossy_preserve_root("./foo.txt".as_ref()));
147+ /// #[cfg(windows)]
148+ /// assert_eq!("..\\foo.txt", EntryName::from_path_lossy_preserve_root("../foo.txt".as_ref()));
149+ /// #[cfg(unix)]
150+ /// assert_eq!("../foo.txt", EntryName::from_path_lossy_preserve_root("../foo.txt".as_ref()));
151+ /// assert_eq!("foo.txt", EntryName::from_path_lossy_preserve_root("bar/../foo.txt".as_ref()));
152+ /// ```
153+ #[ inline]
154+ pub fn from_path_lossy_preserve_root ( name : & Path ) -> Self {
155+ Self :: new_from_utf8path_preserve_root ( Utf8Path :: new ( & name. to_string_lossy ( ) ) )
156+ }
157+
158+ /// Returns a sanitized copy of this entry name that contains only normal components.
159+ ///
160+ /// Sanitization discards prefixes, root separators, `.` and `..` segments so the
161+ /// resulting entry name is always relative and safe to embed in an archive.
162+ ///
163+ /// # Examples
164+ ///
165+ /// ```rust
166+ /// use libpna::EntryName;
167+ ///
168+ /// let name = EntryName::from_utf8_preserve_root("/var/../tmp/./log");
169+ /// assert_eq!("tmp/log", name.sanitize());
170+ /// ```
171+ #[ inline]
172+ pub fn sanitize ( & self ) -> Self {
173+ let path = Utf8Path :: new ( & self . 0 ) ;
174+ Self ( join_with_capacity (
175+ path. components ( )
176+ . filter ( |c| matches ! ( c, Utf8Component :: Normal ( _) ) ) ,
177+ "/" ,
178+ path. as_str ( ) . len ( ) ,
179+ ) )
75180 }
76181
77182 #[ inline]
0 commit comments