Show More
@@ -138,6 +138,79 b' impl HgPath {' | |||||
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`. | |
@@ -473,4 +546,101 b' mod tests {' | |||||
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 | } | |
|
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 | } | |||
476 | } |
|
646 | } |
General Comments 0
You need to be logged in to leave comments.
Login now