Browse Source

* Change Path so that it has named fields.
* Disallowed leading slashes in paths.

Matthew Carr 2 years ago
parent
commit
94e147b5b5
2 changed files with 39 additions and 42 deletions
  1. 36 39
      crates/node/src/main.rs
  2. 3 3
      crates/node/src/test_helpers.rs

+ 36 - 39
crates/node/src/main.rs

@@ -137,7 +137,10 @@ struct Principal(Hash);
 
 /// An identifier for a block in a tree.
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-struct Path(Vec<String>, Principal);
+struct Path {
+    owner: Principal,
+    components: Vec<String>,
+}
 
 impl Path {
     /// The character that is used to separate path components.
@@ -176,21 +179,6 @@ impl Path {
         }
     }
 
-    fn trim_leading_sep(mut string: &str) -> Result<&str, PathError> {
-        let mut pairs = string.char_indices();
-        string = match pairs.next() {
-            Some((_, Path::SEP)) => {
-                match pairs.next() {
-                    Some((index, _)) => &string[index..],
-                    None => return Err(PathError::Empty)
-                }
-            },
-            Some((_, _)) => string,
-            None => return Err(PathError::Empty)
-        };
-        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();
@@ -204,9 +192,8 @@ impl Path {
 impl<'s> TryFrom<&'s str> for Path {
     type Error = PathError;
 
-    fn try_from(mut string: &'s str) -> Result<Path, PathError> {
+    fn try_from(string: &'s str) -> Result<Path, PathError> {
         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;
@@ -223,18 +210,21 @@ impl<'s> TryFrom<&'s str> for Path {
         let leading = components.get(0).ok_or(PathError::InvalidLeadingComponent)?;
         let hash = Hash::try_from(leading.as_str())
             .map_err(|_| PathError::InvalidLeadingComponent)?;
-        Ok(Path(components, Principal(hash)))
+        Ok(Path {
+            owner: Principal(hash),
+            components,
+        })
     }
 }
 
 impl Display for Path {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.0.is_empty() {
-            return write!(f, "{}", Path::SEP);
+        if self.components.is_empty() {
+            return write!(f, "");
         };
-        let mut iter = self.0.iter();
+        let mut iter = self.components.iter();
         let first = iter.next().unwrap();
-        let mut output = write!(f, "{}{}", Path::SEP, first);
+        let mut output = write!(f, "{}", first);
         for component in iter {
             output = write!(f, "{}{}", Path::SEP, component)
         }
@@ -299,7 +289,7 @@ mod tests {
     #[test]
     fn path_from_str_multiple_components_ok() -> Result<(), PathError> {
         let expected = make_path(vec!["red", "green", "blue"]);
-        let input = format!("{}/red/green/blue", expected.1.0);
+        let input = format!("{}/red/green/blue", expected.owner.0);
         path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
@@ -307,26 +297,17 @@ mod tests {
     #[test]
     fn path_from_str_one_component_ok() -> Result<(), PathError> {
         let expected = make_path(vec![]);
-        let input = expected.1.0.to_string();
+        let input = expected.owner.0.to_string();
         path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
 
-    #[test]
-    fn path_from_str_leading_slash_ok() -> Result<(), PathError> {
-        let expected = make_path(vec!["orange", "banana", "shotgun"]);
-        let principal = expected.1.clone();
-        path_from_str_test_case(
-            Ok(expected), format!("/{}/orange/banana/shotgun", principal.0).as_str())?;
-        Ok(())
-    }
-
     #[test]
     fn path_from_str_trailing_slash_ok() -> Result<(), PathError> {
+        // Notice the empty component at the end of this path due to the trailing slash.
         let expected = make_path(vec!["orange", "banana", "shotgun", ""]);
-        let principal = expected.1.clone();
-        path_from_str_test_case(
-            Ok(expected), format!("{}/orange/banana/shotgun/", principal.0).as_str())?;
+        let input = format!("{}/orange/banana/shotgun/", expected.owner.0);
+        path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
 
@@ -341,9 +322,25 @@ mod tests {
 
     #[test]
     fn path_from_str_multiple_slashes_fail() -> Result<(), PathError> {
-        let principal = make_principal();
         let expected = Err(PathError::EmptyComponent);
-        path_from_str_test_case(expected, format!("{}//orange", principal.0).as_str())?;
+        let input = format!("{}//orange", make_principal().0);
+        path_from_str_test_case(expected, input.as_str())?;
+        Ok(())
+    }
+
+    #[test]
+    fn path_from_str_leading_slash_fail() -> Result<(), PathError> {
+        let expected = Err(PathError::EmptyComponent);
+        let input = format!("/{}/orange/banana/shotgun", make_principal().0);
+        path_from_str_test_case(expected, input.as_str())?;
+        Ok(())
+    }
+
+    #[test]
+    fn path_round_trip() -> Result<(), PathError> {
+        let expected = make_path(vec!["interstitial", "inter-related", "intersections"]);
+        let actual = Path::try_from(expected.to_string().as_str())?;
+        assert_eq!(expected, actual);
         Ok(())
     }
 }

+ 3 - 3
crates/node/src/test_helpers.rs

@@ -123,13 +123,13 @@ pub(crate) fn make_principal() -> Principal {
 }
 
 pub(crate) fn make_path(rel_components: Vec<&str>) -> Path {
-    let principal = make_principal();
+    let owner = make_principal();
     let mut components = Vec::with_capacity(rel_components.len() + 1);
-    components.push(principal.0.to_string());
+    components.push(owner.0.to_string());
     for component in rel_components {
         components.push(component.to_string());
     }
-    Path(components, principal)
+    Path { owner, components }
 }
 
 pub(crate) fn make_write_cap() -> Result<WriteCap> {