11use anyhow:: { Result , bail} ;
2- use yazi_shared:: { path:: PathBufDyn , url :: { UrlCow , UrlLike } } ;
2+ use yazi_shared:: path:: { PathBufDyn , PathCow , PathDyn , PathLike } ;
33
4- pub fn url_relative_to < ' a , ' b , U , V > ( from : U , to : V ) -> Result < UrlCow < ' b > >
4+ pub fn path_relative_to < ' a , ' b , P , Q > ( from : P , to : Q ) -> Result < PathCow < ' b > >
55where
6- U : Into < UrlCow < ' a > > ,
7- V : Into < UrlCow < ' b > > ,
6+ P : Into < PathCow < ' a > > ,
7+ Q : Into < PathCow < ' b > > ,
88{
9- url_relative_to_ ( from. into ( ) , to. into ( ) )
9+ path_relative_to_impl ( from. into ( ) , to. into ( ) )
1010}
1111
12- fn url_relative_to_ < ' a > ( from : UrlCow < ' _ > , to : UrlCow < ' a > ) -> Result < UrlCow < ' a > > {
13- use yazi_shared:: url :: Component :: * ;
12+ fn path_relative_to_impl < ' a > ( from : PathCow < ' _ > , to : PathCow < ' a > ) -> Result < PathCow < ' a > > {
13+ use yazi_shared:: path :: Component :: * ;
1414
1515 if from. is_absolute ( ) != to. is_absolute ( ) {
1616 return if to. is_absolute ( ) {
1717 Ok ( to)
1818 } else {
19- bail ! ( "Urls must be both absolute or both relative: {from:?} and {to:?}" ) ;
19+ bail ! ( "Paths must be both absolute or both relative: {from:?} and {to:?}" ) ;
2020 } ;
2121 }
2222
23- if from. covariant ( & to) {
24- return UrlCow :: try_from ( (
25- to. scheme ( ) . zeroed ( ) . to_owned ( ) ,
26- PathBufDyn :: with_str ( to. kind ( ) , "." ) ,
27- ) ) ;
23+ if from == to {
24+ return Ok ( PathDyn :: with_str ( to. kind ( ) , "." ) . into ( ) ) ;
2825 }
2926
3027 let ( mut f_it, mut t_it) = ( from. components ( ) , to. components ( ) ) ;
3128 let ( f_head, t_head) = loop {
3229 match ( f_it. next ( ) , t_it. next ( ) ) {
33- ( Some ( Scheme ( a) ) , Some ( Scheme ( b) ) ) if a. covariant ( b) => { }
3430 ( Some ( RootDir ) , Some ( RootDir ) ) => { }
3531 ( Some ( Prefix ( a) ) , Some ( Prefix ( b) ) ) if a == b => { }
36- ( Some ( Scheme ( _ ) | Prefix ( _) | RootDir ) , _) | ( _, Some ( Scheme ( _ ) | Prefix ( _) | RootDir ) ) => {
32+ ( Some ( Prefix ( _) | RootDir ) , _) | ( _, Some ( Prefix ( _) | RootDir ) ) => {
3733 return Ok ( to) ;
3834 }
3935 ( None , None ) => break ( None , None ) ,
@@ -45,8 +41,51 @@ fn url_relative_to_<'a>(from: UrlCow<'_>, to: UrlCow<'a>) -> Result<UrlCow<'a>>
4541 let dots = f_head. into_iter ( ) . chain ( f_it) . map ( |_| ParentDir ) ;
4642 let rest = t_head. into_iter ( ) . chain ( t_it) ;
4743
48- let iter = dots. chain ( rest) . map ( |c| c. downgrade ( ) . expect ( "path component from dot or normal" ) ) ;
49- let buf = PathBufDyn :: from_components ( to. kind ( ) , iter) ?;
44+ let buf = PathBufDyn :: from_components ( to. kind ( ) , dots. chain ( rest) ) ?;
45+ Ok ( buf. into ( ) )
46+ }
47+
48+ #[ cfg( test) ]
49+ mod tests {
50+ use yazi_shared:: path:: PathDyn ;
51+
52+ use super :: * ;
53+
54+ #[ test]
55+ fn test_path_relative_to ( ) {
56+ yazi_shared:: init_tests ( ) ;
5057
51- UrlCow :: try_from ( ( to. scheme ( ) . zeroed ( ) . to_owned ( ) , buf) )
58+ #[ cfg( unix) ]
59+ let cases = [
60+ // Same paths
61+ ( "" , "" , "." ) ,
62+ ( "." , "." , "." ) ,
63+ ( "/" , "/" , "." ) ,
64+ ( "/a" , "/a" , "." ) ,
65+ // Relative paths
66+ ( "foo" , "bar" , "../bar" ) ,
67+ // Absolute paths
68+ ( "/a/b" , "/a/b/c" , "c" ) ,
69+ ( "/a/b/c" , "/a/b" , ".." ) ,
70+ ( "/a/b/d" , "/a/b/c" , "../c" ) ,
71+ ( "/a/b/c" , "/a" , "../.." ) ,
72+ ( "/a/b/c/" , "/a/d/" , "../../d" ) ,
73+ ( "/a/b/b" , "/a/a/b" , "../../a/b" ) ,
74+ ] ;
75+
76+ #[ cfg( windows) ]
77+ let cases = [
78+ ( r"C:\a\b" , r"C:\a\b\c" , "c" ) ,
79+ ( r"C:\a\b\c" , r"C:\a\b" , r".." ) ,
80+ ( r"C:\a\b\d" , r"C:\a\b\c" , r"..\c" ) ,
81+ ( r"C:\a\b\c" , r"C:\a" , r"..\.." ) ,
82+ ( r"C:\a\b\b" , r"C:\a\a\b" , r"..\..\a\b" ) ,
83+ ] ;
84+
85+ for ( from, to, expected) in cases {
86+ let from = PathDyn :: Os ( from. as_ref ( ) ) ;
87+ let to = PathDyn :: Os ( to. as_ref ( ) ) ;
88+ assert_eq ! ( path_relative_to( from, to) . unwrap( ) . to_str( ) . unwrap( ) , expected) ;
89+ }
90+ }
5291}
0 commit comments