|
@@ -137,7 +137,7 @@ struct Principal(Hash);
|
|
|
|
|
|
/// An identifier for a block in a tree.
|
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
|
|
-struct Path(Vec<String>);
|
|
|
+struct Path(Vec<String>, Principal);
|
|
|
|
|
|
impl Path {
|
|
|
/// The character that is used to separate path components.
|
|
@@ -220,7 +220,25 @@ impl<'s> TryFrom<&'s str> for Path {
|
|
|
if string.len() - 1 == last_end {
|
|
|
components.push("".to_string());
|
|
|
}
|
|
|
- Ok(Path(components))
|
|
|
+ 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)))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Display for Path {
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
+ if self.0.is_empty() {
|
|
|
+ return write!(f, "{}", Path::SEP);
|
|
|
+ };
|
|
|
+ let mut iter = self.0.iter();
|
|
|
+ let first = iter.next().unwrap();
|
|
|
+ let mut output = write!(f, "{}{}", Path::SEP, first);
|
|
|
+ for component in iter {
|
|
|
+ output = write!(f, "{}{}", Path::SEP, component)
|
|
|
+ }
|
|
|
+ output
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -268,6 +286,7 @@ fn main() {
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
use super::*;
|
|
|
+ use test_helpers::*;
|
|
|
|
|
|
fn path_from_str_test_case(
|
|
|
expected: Result<Path, PathError>, input: &str
|
|
@@ -279,48 +298,52 @@ mod tests {
|
|
|
|
|
|
#[test]
|
|
|
fn path_from_str_multiple_components_ok() -> Result<(), PathError> {
|
|
|
- let expected = Path(vec!["red".to_string(), "green".to_string(), "blue".to_string()]);
|
|
|
- path_from_str_test_case(Ok(expected), "red/green/blue")?;
|
|
|
+ let expected = make_path(vec!["red", "green", "blue"]);
|
|
|
+ let input = format!("{}/red/green/blue", expected.1.0);
|
|
|
+ path_from_str_test_case(Ok(expected), input.as_str())?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn path_from_str_one_component_ok() -> Result<(), PathError> {
|
|
|
- let expected = Path(vec!["red".to_string()]);
|
|
|
- path_from_str_test_case(Ok(expected), "red")?;
|
|
|
+ let expected = make_path(vec![]);
|
|
|
+ let input = expected.1.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 = Path(vec![
|
|
|
- "orange".to_string(), "banana".to_string(), "shotgun".to_string()
|
|
|
- ]);
|
|
|
- path_from_str_test_case(Ok(expected), "/orange/banana/shotgun")?;
|
|
|
+ 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> {
|
|
|
- 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/")?;
|
|
|
+ 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_path_too_long_fail() -> Result<(), PathError> {
|
|
|
- let expected = Err(PathError::PathTooLong(4097));
|
|
|
- let input = "*".repeat(4097);
|
|
|
+ let principal = make_principal();
|
|
|
+ let input = format!("{}/{}", principal.0, "*".repeat(4097));
|
|
|
+ let expected = Err(PathError::PathTooLong(input.len()));
|
|
|
path_from_str_test_case(expected, input.as_str())?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
#[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, "//orange")?;
|
|
|
+ path_from_str_test_case(expected, format!("{}//orange", principal.0).as_str())?;
|
|
|
Ok(())
|
|
|
}
|
|
|
}
|