|
@@ -155,7 +155,9 @@ impl Display for PathError {
|
|
|
}
|
|
|
|
|
|
impl Path {
|
|
|
- fn consume_component<I: Iterator<Item = (usize, char)>>(
|
|
|
+ /// Returns a result which, when successful, returns the index after the last character in the
|
|
|
+ /// current path component.
|
|
|
+ fn component_end<I: Iterator<Item = (usize, char)>>(
|
|
|
start: usize, first: char, pairs: &mut I
|
|
|
) -> Result<usize, PathError> {
|
|
|
if first == Path::SEP {
|
|
@@ -198,29 +200,39 @@ impl Path {
|
|
|
};
|
|
|
Ok(string)
|
|
|
}
|
|
|
+
|
|
|
+ /// Asserts that the number of bytes in the given string is no more than `Path::BYTE_LIMIT`.
|
|
|
+ fn assert_not_too_long(string: &str) -> Result<(), PathError> {
|
|
|
+ let len = string.len();
|
|
|
+ if len > Path::BYTE_LIMIT {
|
|
|
+ return Err(PathError::PathTooLong(len))
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl<'s> TryFrom<&'s str> for Path {
|
|
|
type Error = PathError;
|
|
|
|
|
|
fn try_from(mut string: &'s str) -> Result<Path, PathError> {
|
|
|
- {
|
|
|
- let len = string.len();
|
|
|
- if len >= Path::BYTE_LIMIT {
|
|
|
- return Err(PathError::PathTooLong(len))
|
|
|
- }
|
|
|
- }
|
|
|
+ Path::assert_not_too_long(string)?;
|
|
|
string = Path::trim_leading_sep(string)?;
|
|
|
let mut pairs = string.char_indices();
|
|
|
let mut components = Vec::new();
|
|
|
+ let mut last_end = 0;
|
|
|
loop {
|
|
|
let (start, end) = match pairs.next() {
|
|
|
- Some((start, c)) => (start, Path::consume_component(start, c, &mut pairs)),
|
|
|
+ Some((start, c)) => (start, Path::component_end(start, c, &mut pairs)?),
|
|
|
None => break
|
|
|
};
|
|
|
- let slice = &string[start..end?];
|
|
|
+ last_end = end;
|
|
|
+ let slice = &string[start..end];
|
|
|
components.push(slice.to_string());
|
|
|
}
|
|
|
+ // An empty component is added to the end to indicate if there was a trailing slash.
|
|
|
+ if string.len() - 1 == last_end {
|
|
|
+ components.push("".to_string());
|
|
|
+ }
|
|
|
Ok(Path(components))
|
|
|
}
|
|
|
}
|
|
@@ -296,6 +308,15 @@ mod tests {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+ #[test]
|
|
|
+ fn path_from_str_trailing_slash_ok() -> Result<(), PathError> {
|
|
|
+ let expected = Path(vec![
|
|
|
+ "orange".to_string(), "banana".to_string(), "shotgun".to_string(), "".to_string()
|
|
|
+ ]);
|
|
|
+ path_from_str_test_case(Ok(expected), "/orange/banana/shotgun/")?;
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
#[test]
|
|
|
fn path_from_str_path_too_long_fail() -> Result<(), PathError> {
|
|
|
let expected = Err(PathError::PathTooLong(4097));
|