136 Some(HgPath::new(&self.inner[base.len()..])) |
136 Some(HgPath::new(&self.inner[base.len()..])) |
137 } else { |
137 } else { |
138 None |
138 None |
139 } |
139 } |
140 } |
140 } |
|
141 |
|
142 #[cfg(windows)] |
|
143 /// Copied from the Python stdlib's `os.path.splitdrive` implementation. |
|
144 /// |
|
145 /// Split a pathname into drive/UNC sharepoint and relative path specifiers. |
|
146 /// Returns a 2-tuple (drive_or_unc, path); either part may be empty. |
|
147 /// |
|
148 /// If you assign |
|
149 /// result = split_drive(p) |
|
150 /// It is always true that: |
|
151 /// result[0] + result[1] == p |
|
152 /// |
|
153 /// If the path contained a drive letter, drive_or_unc will contain everything |
|
154 /// up to and including the colon. |
|
155 /// e.g. split_drive("c:/dir") returns ("c:", "/dir") |
|
156 /// |
|
157 /// If the path contained a UNC path, the drive_or_unc will contain the host |
|
158 /// name and share up to but not including the fourth directory separator |
|
159 /// character. |
|
160 /// e.g. split_drive("//host/computer/dir") returns ("//host/computer", "/dir") |
|
161 /// |
|
162 /// Paths cannot contain both a drive letter and a UNC path. |
|
163 pub fn split_drive<'a>(&self) -> (&HgPath, &HgPath) { |
|
164 let bytes = self.as_bytes(); |
|
165 let is_sep = |b| std::path::is_separator(b as char); |
|
166 |
|
167 if self.len() < 2 { |
|
168 (HgPath::new(b""), &self) |
|
169 } else if is_sep(bytes[0]) |
|
170 && is_sep(bytes[1]) |
|
171 && (self.len() == 2 || !is_sep(bytes[2])) |
|
172 { |
|
173 // Is a UNC path: |
|
174 // vvvvvvvvvvvvvvvvvvvv drive letter or UNC path |
|
175 // \\machine\mountpoint\directory\etc\... |
|
176 // directory ^^^^^^^^^^^^^^^ |
|
177 |
|
178 let machine_end_index = bytes[2..].iter().position(|b| is_sep(*b)); |
|
179 let mountpoint_start_index = if let Some(i) = machine_end_index { |
|
180 i + 2 |
|
181 } else { |
|
182 return (HgPath::new(b""), &self); |
|
183 }; |
|
184 |
|
185 match bytes[mountpoint_start_index + 1..] |
|
186 .iter() |
|
187 .position(|b| is_sep(*b)) |
|
188 { |
|
189 // A UNC path can't have two slashes in a row |
|
190 // (after the initial two) |
|
191 Some(0) => (HgPath::new(b""), &self), |
|
192 Some(i) => { |
|
193 let (a, b) = |
|
194 bytes.split_at(mountpoint_start_index + 1 + i); |
|
195 (HgPath::new(a), HgPath::new(b)) |
|
196 } |
|
197 None => (&self, HgPath::new(b"")), |
|
198 } |
|
199 } else if bytes[1] == b':' { |
|
200 // Drive path c:\directory |
|
201 let (a, b) = bytes.split_at(2); |
|
202 (HgPath::new(a), HgPath::new(b)) |
|
203 } else { |
|
204 (HgPath::new(b""), &self) |
|
205 } |
|
206 } |
|
207 |
|
208 #[cfg(unix)] |
|
209 /// Split a pathname into drive and path. On Posix, drive is always empty. |
|
210 pub fn split_drive(&self) -> (&HgPath, &HgPath) { |
|
211 (HgPath::new(b""), &self) |
|
212 } |
|
213 |
141 /// Checks for errors in the path, short-circuiting at the first one. |
214 /// Checks for errors in the path, short-circuiting at the first one. |
142 /// This generates fine-grained errors useful for debugging. |
215 /// This generates fine-grained errors useful for debugging. |
143 /// To simply check if the path is valid during tests, use `is_valid`. |
216 /// To simply check if the path is valid during tests, use `is_valid`. |
144 pub fn check_state(&self) -> Result<(), HgPathError> { |
217 pub fn check_state(&self) -> Result<(), HgPathError> { |
145 if self.len() == 0 { |
218 if self.len() == 0 { |
471 |
544 |
472 let path = HgPath::new(b"ends/with/dir/"); |
545 let path = HgPath::new(b"ends/with/dir/"); |
473 let base = HgPath::new(b"ends/"); |
546 let base = HgPath::new(b"ends/"); |
474 assert_eq!(Some(HgPath::new(b"with/dir/")), path.relative_to(base)); |
547 assert_eq!(Some(HgPath::new(b"with/dir/")), path.relative_to(base)); |
475 } |
548 } |
476 } |
549 |
|
550 #[test] |
|
551 #[cfg(unix)] |
|
552 fn test_split_drive() { |
|
553 // Taken from the Python stdlib's tests |
|
554 assert_eq!( |
|
555 HgPath::new(br"/foo/bar").split_drive(), |
|
556 (HgPath::new(b""), HgPath::new(br"/foo/bar")) |
|
557 ); |
|
558 assert_eq!( |
|
559 HgPath::new(br"foo:bar").split_drive(), |
|
560 (HgPath::new(b""), HgPath::new(br"foo:bar")) |
|
561 ); |
|
562 assert_eq!( |
|
563 HgPath::new(br":foo:bar").split_drive(), |
|
564 (HgPath::new(b""), HgPath::new(br":foo:bar")) |
|
565 ); |
|
566 // Also try NT paths; should not split them |
|
567 assert_eq!( |
|
568 HgPath::new(br"c:\foo\bar").split_drive(), |
|
569 (HgPath::new(b""), HgPath::new(br"c:\foo\bar")) |
|
570 ); |
|
571 assert_eq!( |
|
572 HgPath::new(b"c:/foo/bar").split_drive(), |
|
573 (HgPath::new(b""), HgPath::new(br"c:/foo/bar")) |
|
574 ); |
|
575 assert_eq!( |
|
576 HgPath::new(br"\\conky\mountpoint\foo\bar").split_drive(), |
|
577 ( |
|
578 HgPath::new(b""), |
|
579 HgPath::new(br"\\conky\mountpoint\foo\bar") |
|
580 ) |
|
581 ); |
|
582 } |
|
583 |
|
584 #[test] |
|
585 #[cfg(windows)] |
|
586 fn test_split_drive() { |
|
587 assert_eq!( |
|
588 HgPath::new(br"c:\foo\bar").split_drive(), |
|
589 (HgPath::new(br"c:"), HgPath::new(br"\foo\bar")) |
|
590 ); |
|
591 assert_eq!( |
|
592 HgPath::new(b"c:/foo/bar").split_drive(), |
|
593 (HgPath::new(br"c:"), HgPath::new(br"/foo/bar")) |
|
594 ); |
|
595 assert_eq!( |
|
596 HgPath::new(br"\\conky\mountpoint\foo\bar").split_drive(), |
|
597 ( |
|
598 HgPath::new(br"\\conky\mountpoint"), |
|
599 HgPath::new(br"\foo\bar") |
|
600 ) |
|
601 ); |
|
602 assert_eq!( |
|
603 HgPath::new(br"//conky/mountpoint/foo/bar").split_drive(), |
|
604 ( |
|
605 HgPath::new(br"//conky/mountpoint"), |
|
606 HgPath::new(br"/foo/bar") |
|
607 ) |
|
608 ); |
|
609 assert_eq!( |
|
610 HgPath::new(br"\\\conky\mountpoint\foo\bar").split_drive(), |
|
611 ( |
|
612 HgPath::new(br""), |
|
613 HgPath::new(br"\\\conky\mountpoint\foo\bar") |
|
614 ) |
|
615 ); |
|
616 assert_eq!( |
|
617 HgPath::new(br"///conky/mountpoint/foo/bar").split_drive(), |
|
618 ( |
|
619 HgPath::new(br""), |
|
620 HgPath::new(br"///conky/mountpoint/foo/bar") |
|
621 ) |
|
622 ); |
|
623 assert_eq!( |
|
624 HgPath::new(br"\\conky\\mountpoint\foo\bar").split_drive(), |
|
625 ( |
|
626 HgPath::new(br""), |
|
627 HgPath::new(br"\\conky\\mountpoint\foo\bar") |
|
628 ) |
|
629 ); |
|
630 assert_eq!( |
|
631 HgPath::new(br"//conky//mountpoint/foo/bar").split_drive(), |
|
632 ( |
|
633 HgPath::new(br""), |
|
634 HgPath::new(br"//conky//mountpoint/foo/bar") |
|
635 ) |
|
636 ); |
|
637 // UNC part containing U+0130 |
|
638 assert_eq!( |
|
639 HgPath::new(b"//conky/MOUNTPO\xc4\xb0NT/foo/bar").split_drive(), |
|
640 ( |
|
641 HgPath::new(b"//conky/MOUNTPO\xc4\xb0NT"), |
|
642 HgPath::new(br"/foo/bar") |
|
643 ) |
|
644 ); |
|
645 } |
|
646 } |