1+ use crate :: Stack ;
2+ use bstr:: { BStr , BString , ByteSlice } ;
3+ use std:: ffi:: OsStr ;
14use std:: path:: { Component , Path , PathBuf } ;
25
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+ }
478
579/// Access
680impl Stack {
@@ -62,8 +136,13 @@ impl Stack {
62136 /// `relative` paths are terminal, so point to their designated file or directory.
63137 /// The path is also expected to be normalized, and should not contain extra separators, and must not contain `..`
64138 /// 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 ( ) {
67146 return Err ( std:: io:: Error :: new (
68147 std:: io:: ErrorKind :: Other ,
69148 "empty inputs are not allowed" ,
@@ -73,15 +152,19 @@ impl Stack {
73152 delegate. push_directory ( self ) ?;
74153 }
75154
76- let mut components = relative. components ( ) . peekable ( ) ;
77155 let mut existing_components = self . current_relative . components ( ) ;
78156 let mut matching_components = 0 ;
79157 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}" ) ) ) ,
85168 }
86169 }
87170
@@ -100,15 +183,7 @@ impl Stack {
100183 }
101184
102185 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) ?;
112187 let is_last_component = components. peek ( ) . is_none ( ) ;
113188 self . current_is_directory = !is_last_component;
114189 self . current . push ( comp) ;
0 commit comments