rust/hg-core/src/utils/hg_path.rs
changeset 44221 baa4e7fdfd47
parent 43914 4b3c8df189bc
child 44266 732098027b34
equal deleted inserted replaced
44128:ff396501e841 44221: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 }