1
+ use crate :: Stack ;
2
+ use bstr:: { BStr , BString , ByteSlice } ;
3
+ use std:: ffi:: OsStr ;
1
4
use std:: path:: { Component , Path , PathBuf } ;
2
5
3
- use crate :: Stack ;
6
+ ///
7
+ pub mod to_normal_path_components {
8
+ use std:: ffi:: OsString ;
9
+
10
+ /// The error used in [`ToNormalPathComponents::to_normal_path_components()`](super::ToNormalPathComponents::to_normal_path_components()).
11
+ #[ derive( Debug , thiserror:: Error ) ]
12
+ #[ allow( missing_docs) ]
13
+ pub enum Error {
14
+ #[ error( "Input path \" {path}\" contains relative or absolute components" , path = std:: path:: Path :: new( . 0 . as_os_str( ) ) . display( ) ) ]
15
+ NotANormalComponent ( OsString ) ,
16
+ #[ error( "Could not convert to UTF8 or from UTF8 due to ill-formed input" ) ]
17
+ IllegalUtf8 ,
18
+ }
19
+ }
20
+
21
+ /// Obtain an iterator over `OsStr`-components which are normal, none-relative and not absolute.
22
+ pub trait ToNormalPathComponents {
23
+ /// Return an iterator over the normal components of a path, without the separator.
24
+ fn to_normal_path_components ( & self ) -> impl Iterator < Item = Result < & OsStr , to_normal_path_components:: Error > > ;
25
+ }
26
+
27
+ impl ToNormalPathComponents for & Path {
28
+ fn to_normal_path_components ( & self ) -> impl Iterator < Item = Result < & OsStr , to_normal_path_components:: Error > > {
29
+ self . components ( ) . map ( |component| match component {
30
+ Component :: Normal ( os_str) => Ok ( os_str) ,
31
+ _ => Err ( to_normal_path_components:: Error :: NotANormalComponent (
32
+ self . as_os_str ( ) . to_owned ( ) ,
33
+ ) ) ,
34
+ } )
35
+ }
36
+ }
37
+
38
+ impl ToNormalPathComponents for PathBuf {
39
+ fn to_normal_path_components ( & self ) -> impl Iterator < Item = Result < & OsStr , to_normal_path_components:: Error > > {
40
+ self . components ( ) . map ( |component| match component {
41
+ Component :: Normal ( os_str) => Ok ( os_str) ,
42
+ _ => Err ( to_normal_path_components:: Error :: NotANormalComponent (
43
+ self . as_os_str ( ) . to_owned ( ) ,
44
+ ) ) ,
45
+ } )
46
+ }
47
+ }
48
+
49
+ impl ToNormalPathComponents for & BStr {
50
+ fn to_normal_path_components ( & self ) -> impl Iterator < Item = Result < & OsStr , to_normal_path_components:: Error > > {
51
+ self . split ( |b| * b == b'/' ) . filter ( |c| !c. is_empty ( ) ) . map ( |component| {
52
+ gix_path:: try_from_byte_slice ( component. as_bstr ( ) )
53
+ . map_err ( |_| to_normal_path_components:: Error :: IllegalUtf8 )
54
+ . map ( Path :: as_os_str)
55
+ } )
56
+ }
57
+ }
58
+
59
+ impl ToNormalPathComponents for & str {
60
+ fn to_normal_path_components ( & self ) -> impl Iterator < Item = Result < & OsStr , to_normal_path_components:: Error > > {
61
+ self . split ( '/' ) . filter ( |c| !c. is_empty ( ) ) . map ( |component| {
62
+ gix_path:: try_from_byte_slice ( component. as_bytes ( ) )
63
+ . map_err ( |_| to_normal_path_components:: Error :: IllegalUtf8 )
64
+ . map ( Path :: as_os_str)
65
+ } )
66
+ }
67
+ }
68
+
69
+ impl ToNormalPathComponents for & BString {
70
+ fn to_normal_path_components ( & self ) -> impl Iterator < Item = Result < & OsStr , to_normal_path_components:: Error > > {
71
+ self . split ( |b| * b == b'/' ) . filter ( |c| !c. is_empty ( ) ) . map ( |component| {
72
+ gix_path:: try_from_byte_slice ( component. as_bstr ( ) )
73
+ . map_err ( |_| to_normal_path_components:: Error :: IllegalUtf8 )
74
+ . map ( Path :: as_os_str)
75
+ } )
76
+ }
77
+ }
4
78
5
79
/// Access
6
80
impl Stack {
@@ -62,8 +136,13 @@ impl Stack {
62
136
/// `relative` paths are terminal, so point to their designated file or directory.
63
137
/// The path is also expected to be normalized, and should not contain extra separators, and must not contain `..`
64
138
/// or have leading or trailing slashes (or additionally backslashes on Windows).
65
- pub fn make_relative_path_current ( & mut self , relative : & Path , delegate : & mut dyn Delegate ) -> std:: io:: Result < ( ) > {
66
- if self . valid_components != 0 && relative. as_os_str ( ) . is_empty ( ) {
139
+ pub fn make_relative_path_current (
140
+ & mut self ,
141
+ relative : impl ToNormalPathComponents ,
142
+ delegate : & mut dyn Delegate ,
143
+ ) -> std:: io:: Result < ( ) > {
144
+ let mut components = relative. to_normal_path_components ( ) . peekable ( ) ;
145
+ if self . valid_components != 0 && components. peek ( ) . is_none ( ) {
67
146
return Err ( std:: io:: Error :: new (
68
147
std:: io:: ErrorKind :: Other ,
69
148
"empty inputs are not allowed" ,
@@ -73,15 +152,19 @@ impl Stack {
73
152
delegate. push_directory ( self ) ?;
74
153
}
75
154
76
- let mut components = relative. components ( ) . peekable ( ) ;
77
155
let mut existing_components = self . current_relative . components ( ) ;
78
156
let mut matching_components = 0 ;
79
157
while let ( Some ( existing_comp) , Some ( new_comp) ) = ( existing_components. next ( ) , components. peek ( ) ) {
80
- if existing_comp == * new_comp {
81
- components. next ( ) ;
82
- matching_components += 1 ;
83
- } else {
84
- break ;
158
+ match new_comp {
159
+ Ok ( new_comp) => {
160
+ if existing_comp. as_os_str ( ) == * new_comp {
161
+ components. next ( ) ;
162
+ matching_components += 1 ;
163
+ } else {
164
+ break ;
165
+ }
166
+ }
167
+ Err ( err) => return Err ( std:: io:: Error :: other ( format ! ( "{err}" ) ) ) ,
85
168
}
86
169
}
87
170
@@ -100,15 +183,7 @@ impl Stack {
100
183
}
101
184
102
185
while let Some ( comp) = components. next ( ) {
103
- if !matches ! ( comp, Component :: Normal ( _) ) {
104
- return Err ( std:: io:: Error :: new (
105
- std:: io:: ErrorKind :: Other ,
106
- format ! (
107
- "Input path \" {}\" contains relative or absolute components" ,
108
- relative. display( )
109
- ) ,
110
- ) ) ;
111
- }
186
+ let comp = comp. map_err ( std:: io:: Error :: other) ?;
112
187
let is_last_component = components. peek ( ) . is_none ( ) ;
113
188
self . current_is_directory = !is_last_component;
114
189
self . current . push ( comp) ;
0 commit comments