@@ -38,6 +38,8 @@ impl RelativePath {
3838#[ derive( Debug , thiserror:: Error ) ]
3939#[ allow( missing_docs) ]
4040pub enum Error {
41+ #[ error( "A RelativePath is not allowed to be absolute" ) ]
42+ IsAbsolute ,
4143 #[ error( transparent) ]
4244 ContainsInvalidComponent ( #[ from] gix_validate:: path:: component:: Error ) ,
4345 #[ error( transparent) ]
@@ -51,6 +53,11 @@ impl<'a> TryFrom<&'a str> for &'a RelativePath {
5153 use std:: path:: Path ;
5254
5355 let path: & std:: path:: Path = Path :: new ( value) ;
56+
57+ if path. is_absolute ( ) {
58+ return Err ( Error :: IsAbsolute ) ;
59+ }
60+
5461 let options: Options = Default :: default ( ) ;
5562
5663 for component in path. components ( ) {
@@ -68,6 +75,11 @@ impl<'a> TryFrom<&'a BStr> for &'a RelativePath {
6875
6976 fn try_from ( value : & ' a BStr ) -> Result < Self , Self :: Error > {
7077 let path: & std:: path:: Path = & try_from_bstr ( value) ?;
78+
79+ if path. is_absolute ( ) {
80+ return Err ( Error :: IsAbsolute ) ;
81+ }
82+
7183 let options: Options = Default :: default ( ) ;
7284
7385 for component in path. components ( ) {
@@ -87,6 +99,10 @@ impl<'a, const N: usize> TryFrom<&'a [u8; N]> for &'a RelativePath {
8799 fn try_from ( value : & ' a [ u8 ; N ] ) -> Result < Self , Self :: Error > {
88100 let path: & std:: path:: Path = try_from_byte_slice ( value) ?;
89101
102+ if path. is_absolute ( ) {
103+ return Err ( Error :: IsAbsolute ) ;
104+ }
105+
90106 let options: Options = Default :: default ( ) ;
91107
92108 for component in path. components ( ) {
@@ -105,6 +121,10 @@ impl<'a> TryFrom<&'a BString> for &'a RelativePath {
105121 fn try_from ( value : & ' a BString ) -> Result < Self , Self :: Error > {
106122 let path: & std:: path:: Path = & try_from_bstr ( value. as_bstr ( ) ) ?;
107123
124+ if path. is_absolute ( ) {
125+ return Err ( Error :: IsAbsolute ) ;
126+ }
127+
108128 let options: Options = Default :: default ( ) ;
109129
110130 for component in path. components ( ) {
@@ -131,3 +151,60 @@ impl AsRef<[u8]> for RelativePath {
131151 self . inner . as_bytes ( )
132152 }
133153}
154+
155+ #[ cfg( test) ]
156+ mod tests {
157+ use super :: * ;
158+
159+ #[ cfg( not( windows) ) ]
160+ #[ test]
161+ fn absolute_paths_return_err ( ) {
162+ let path_str: & str = "/refs/heads" ;
163+ let path_bstr: & BStr = path_str. into ( ) ;
164+ let path_u8: & [ u8 ; 11 ] = b"/refs/heads" ;
165+ let path_bstring: BString = "/refs/heads" . into ( ) ;
166+
167+ assert ! ( matches!(
168+ TryInto :: <& RelativePath >:: try_into( path_str) ,
169+ Err ( Error :: IsAbsolute )
170+ ) ) ;
171+ assert ! ( matches!(
172+ TryInto :: <& RelativePath >:: try_into( path_bstr) ,
173+ Err ( Error :: IsAbsolute )
174+ ) ) ;
175+ assert ! ( matches!(
176+ TryInto :: <& RelativePath >:: try_into( path_u8) ,
177+ Err ( Error :: IsAbsolute )
178+ ) ) ;
179+ assert ! ( matches!(
180+ TryInto :: <& RelativePath >:: try_into( & path_bstring) ,
181+ Err ( Error :: IsAbsolute )
182+ ) ) ;
183+ }
184+
185+ #[ cfg( windows) ]
186+ #[ test]
187+ fn absolute_paths_return_err ( ) {
188+ let path_str: & str = r"c:\refs\heads" ;
189+ let path_bstr: & BStr = path_str. into ( ) ;
190+ let path_u8: & [ u8 ; 11 ] = r"c:\refs\heads" ;
191+ let path_bstring: BString = r"c:\refs\heads" . into ( ) ;
192+
193+ assert ! ( matches!(
194+ TryInto :: <& RelativePath >:: try_into( path_str) ,
195+ Err ( Error :: IsAbsolute )
196+ ) ) ;
197+ assert ! ( matches!(
198+ TryInto :: <& RelativePath >:: try_into( path_bstr) ,
199+ Err ( Error :: IsAbsolute )
200+ ) ) ;
201+ assert ! ( matches!(
202+ TryInto :: <& RelativePath >:: try_into( path_u8) ,
203+ Err ( Error :: IsAbsolute )
204+ ) ) ;
205+ assert ! ( matches!(
206+ TryInto :: <& RelativePath >:: try_into( & path_bstring) ,
207+ Err ( Error :: IsAbsolute )
208+ ) ) ;
209+ }
210+ }
0 commit comments