Mercurial > public > mercurial-scm > hg
comparison rust/hg-core/src/utils/hg_path.rs @ 44136:baa4e7fdfd47
rust-utils: add Rust implementation of Python's "os.path.splitdrive"
I also wrote the NT version although I didn't mean to at first, so I thought
I would keep it, so that any further effort to get the Rust code working on
Windows is a little easier.
Differential Revision: https://phab.mercurial-scm.org/D7864
author | Rapha?l Gom?s <rgomes@octobus.net> |
---|---|
date | Tue, 14 Jan 2020 18:03:28 +0100 |
parents | 4b3c8df189bc |
children | 732098027b34 |
comparison
equal
deleted
inserted
replaced
44128:ff396501e841 | 44136:baa4e7fdfd47 |
---|---|
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 } |