13 |
13 |
14 #[derive(Debug, Eq, PartialEq)] |
14 #[derive(Debug, Eq, PartialEq)] |
15 pub enum HgPathError { |
15 pub enum HgPathError { |
16 /// Bytes from the invalid `HgPath` |
16 /// Bytes from the invalid `HgPath` |
17 LeadingSlash(Vec<u8>), |
17 LeadingSlash(Vec<u8>), |
18 /// Bytes and index of the second slash |
18 ConsecutiveSlashes { |
19 ConsecutiveSlashes(Vec<u8>, usize), |
19 bytes: Vec<u8>, |
20 /// Bytes and index of the null byte |
20 second_slash_index: usize, |
21 ContainsNullByte(Vec<u8>, usize), |
21 }, |
|
22 ContainsNullByte { |
|
23 bytes: Vec<u8>, |
|
24 null_byte_index: usize, |
|
25 }, |
22 /// Bytes |
26 /// Bytes |
23 DecodeError(Vec<u8>), |
27 DecodeError(Vec<u8>), |
|
28 /// The rest come from audit errors |
|
29 EndsWithSlash(HgPathBuf), |
|
30 ContainsIllegalComponent(HgPathBuf), |
|
31 /// Path is inside the `.hg` folder |
|
32 InsideDotHg(HgPathBuf), |
|
33 IsInsideNestedRepo { |
|
34 path: HgPathBuf, |
|
35 nested_repo: HgPathBuf, |
|
36 }, |
|
37 TraversesSymbolicLink { |
|
38 path: HgPathBuf, |
|
39 symlink: HgPathBuf, |
|
40 }, |
|
41 NotFsCompliant(HgPathBuf), |
|
42 /// `path` is the smallest invalid path |
|
43 NotUnderRoot { |
|
44 path: PathBuf, |
|
45 root: PathBuf, |
|
46 }, |
24 } |
47 } |
25 |
48 |
26 impl ToString for HgPathError { |
49 impl ToString for HgPathError { |
27 fn to_string(&self) -> String { |
50 fn to_string(&self) -> String { |
28 match self { |
51 match self { |
29 HgPathError::LeadingSlash(bytes) => { |
52 HgPathError::LeadingSlash(bytes) => { |
30 format!("Invalid HgPath '{:?}': has a leading slash.", bytes) |
53 format!("Invalid HgPath '{:?}': has a leading slash.", bytes) |
31 } |
54 } |
32 HgPathError::ConsecutiveSlashes(bytes, pos) => format!( |
55 HgPathError::ConsecutiveSlashes { |
33 "Invalid HgPath '{:?}': consecutive slahes at pos {}.", |
56 bytes, |
|
57 second_slash_index: pos, |
|
58 } => format!( |
|
59 "Invalid HgPath '{:?}': consecutive slashes at pos {}.", |
34 bytes, pos |
60 bytes, pos |
35 ), |
61 ), |
36 HgPathError::ContainsNullByte(bytes, pos) => format!( |
62 HgPathError::ContainsNullByte { |
|
63 bytes, |
|
64 null_byte_index: pos, |
|
65 } => format!( |
37 "Invalid HgPath '{:?}': contains null byte at pos {}.", |
66 "Invalid HgPath '{:?}': contains null byte at pos {}.", |
38 bytes, pos |
67 bytes, pos |
39 ), |
68 ), |
40 HgPathError::DecodeError(bytes) => { |
69 HgPathError::DecodeError(bytes) => { |
41 format!("Invalid HgPath '{:?}': could not be decoded.", bytes) |
70 format!("Invalid HgPath '{:?}': could not be decoded.", bytes) |
42 } |
71 } |
|
72 HgPathError::EndsWithSlash(path) => { |
|
73 format!("Audit failed for '{}': ends with a slash.", path) |
|
74 } |
|
75 HgPathError::ContainsIllegalComponent(path) => format!( |
|
76 "Audit failed for '{}': contains an illegal component.", |
|
77 path |
|
78 ), |
|
79 HgPathError::InsideDotHg(path) => format!( |
|
80 "Audit failed for '{}': is inside the '.hg' folder.", |
|
81 path |
|
82 ), |
|
83 HgPathError::IsInsideNestedRepo { |
|
84 path, |
|
85 nested_repo: nested, |
|
86 } => format!( |
|
87 "Audit failed for '{}': is inside a nested repository '{}'.", |
|
88 path, nested |
|
89 ), |
|
90 HgPathError::TraversesSymbolicLink { path, symlink } => format!( |
|
91 "Audit failed for '{}': traverses symbolic link '{}'.", |
|
92 path, symlink |
|
93 ), |
|
94 HgPathError::NotFsCompliant(path) => format!( |
|
95 "Audit failed for '{}': cannot be turned into a \ |
|
96 filesystem path.", |
|
97 path |
|
98 ), |
|
99 HgPathError::NotUnderRoot { path, root } => format!( |
|
100 "Audit failed for '{}': not under root {}.", |
|
101 path.display(), |
|
102 root.display() |
|
103 ), |
43 } |
104 } |
44 } |
105 } |
45 } |
106 } |
46 |
107 |
47 impl From<HgPathError> for std::io::Error { |
108 impl From<HgPathError> for std::io::Error { |
227 return Err(HgPathError::LeadingSlash(bytes.to_vec())); |
288 return Err(HgPathError::LeadingSlash(bytes.to_vec())); |
228 } |
289 } |
229 for (index, byte) in bytes.iter().enumerate() { |
290 for (index, byte) in bytes.iter().enumerate() { |
230 match byte { |
291 match byte { |
231 0 => { |
292 0 => { |
232 return Err(HgPathError::ContainsNullByte( |
293 return Err(HgPathError::ContainsNullByte { |
233 bytes.to_vec(), |
294 bytes: bytes.to_vec(), |
234 index, |
295 null_byte_index: index, |
235 )) |
296 }) |
236 } |
297 } |
237 b'/' => { |
298 b'/' => { |
238 if previous_byte.is_some() && previous_byte == Some(b'/') { |
299 if previous_byte.is_some() && previous_byte == Some(b'/') { |
239 return Err(HgPathError::ConsecutiveSlashes( |
300 return Err(HgPathError::ConsecutiveSlashes { |
240 bytes.to_vec(), |
301 bytes: bytes.to_vec(), |
241 index, |
302 second_slash_index: index, |
242 )); |
303 }); |
243 } |
304 } |
244 } |
305 } |
245 _ => (), |
306 _ => (), |
246 }; |
307 }; |
247 previous_byte = Some(*byte); |
308 previous_byte = Some(*byte); |
429 assert_eq!( |
490 assert_eq!( |
430 Err(HgPathError::LeadingSlash(b"/".to_vec())), |
491 Err(HgPathError::LeadingSlash(b"/".to_vec())), |
431 HgPath::new(b"/").check_state() |
492 HgPath::new(b"/").check_state() |
432 ); |
493 ); |
433 assert_eq!( |
494 assert_eq!( |
434 Err(HgPathError::ConsecutiveSlashes(b"a/b//c".to_vec(), 4)), |
495 Err(HgPathError::ConsecutiveSlashes { |
|
496 bytes: b"a/b//c".to_vec(), |
|
497 second_slash_index: 4 |
|
498 }), |
435 HgPath::new(b"a/b//c").check_state() |
499 HgPath::new(b"a/b//c").check_state() |
436 ); |
500 ); |
437 assert_eq!( |
501 assert_eq!( |
438 Err(HgPathError::ContainsNullByte(b"a/b/\0c".to_vec(), 4)), |
502 Err(HgPathError::ContainsNullByte { |
|
503 bytes: b"a/b/\0c".to_vec(), |
|
504 null_byte_index: 4 |
|
505 }), |
439 HgPath::new(b"a/b/\0c").check_state() |
506 HgPath::new(b"a/b/\0c").check_state() |
440 ); |
507 ); |
441 // TODO test HgPathError::DecodeError for the Windows implementation. |
508 // TODO test HgPathError::DecodeError for the Windows implementation. |
442 assert_eq!(true, HgPath::new(b"").is_valid()); |
509 assert_eq!(true, HgPath::new(b"").is_valid()); |
443 assert_eq!(true, HgPath::new(b"a/b/c").is_valid()); |
510 assert_eq!(true, HgPath::new(b"a/b/c").is_valid()); |