##// END OF EJS Templates
rust-utils: add Rust implementation of Python's "os.path.splitdrive"...
Raphaël Gomès -
r44588:baa4e7fd default
parent child Browse files
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