about summary refs log tree commit diff
path: root/tvix/castore/src/proto/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/castore/src/proto/mod.rs')
-rw-r--r--tvix/castore/src/proto/mod.rs121
1 files changed, 59 insertions, 62 deletions
diff --git a/tvix/castore/src/proto/mod.rs b/tvix/castore/src/proto/mod.rs
index a533c1c4f195..a40a67f53cf3 100644
--- a/tvix/castore/src/proto/mod.rs
+++ b/tvix/castore/src/proto/mod.rs
@@ -1,4 +1,4 @@
-use std::str;
+use std::{cmp::Ordering, str};
 
 use prost::Message;
 
@@ -66,79 +66,76 @@ impl Directory {
     }
 }
 
-/// Accepts a name, and a mutable reference to the previous name.
-/// If the passed name is larger than the previous one, the reference is updated.
-/// If it's not, an error is returned.
-fn update_if_lt_prev<'n>(prev_name: &mut &'n [u8], name: &'n [u8]) -> Result<(), DirectoryError> {
-    if *name < **prev_name {
-        return Err(DirectoryError::WrongSorting(bytes::Bytes::copy_from_slice(
-            name,
-        )));
-    }
-    *prev_name = name;
-    Ok(())
-}
-
-// TODO: add a proper owned version here that moves various fields
 impl TryFrom<Directory> for crate::Directory {
     type Error = DirectoryError;
 
     fn try_from(value: Directory) -> Result<Self, Self::Error> {
-        (&value).try_into()
-    }
-}
-
-impl TryFrom<&Directory> for crate::Directory {
-    type Error = DirectoryError;
-
-    fn try_from(directory: &Directory) -> Result<crate::Directory, DirectoryError> {
-        let mut dir = crate::Directory::new();
-
-        let mut last_file_name: &[u8] = b"";
-
-        // TODO: this currently loops over all three types separately, rather
-        // than peeking and picking from where would be the next.
-
-        for file in directory.files.iter().map(move |file| {
-            update_if_lt_prev(&mut last_file_name, &file.name).map(|()| file.clone())
-        }) {
-            let file = file?;
-
-            let (name, node) = Node {
-                node: Some(node::Node::File(file)),
+        // Check directories, files and symlinks are sorted
+        // We'll notice duplicates across all three fields when constructing the Directory.
+        // FUTUREWORK: use is_sorted() once stable, and/or implement the producer for
+        // [crate::Directory::try_from_iter] iterating over all three and doing all checks inline.
+        value
+            .directories
+            .iter()
+            .try_fold(&b""[..], |prev_name, e| {
+                match e.name.as_ref().cmp(prev_name) {
+                    Ordering::Less => Err(DirectoryError::WrongSorting(e.name.to_owned())),
+                    Ordering::Equal => {
+                        Err(DirectoryError::DuplicateName(e.name.to_owned().try_into()?))
+                    }
+                    Ordering::Greater => Ok(e.name.as_ref()),
+                }
+            })?;
+        value.files.iter().try_fold(&b""[..], |prev_name, e| {
+            match e.name.as_ref().cmp(prev_name) {
+                Ordering::Less => Err(DirectoryError::WrongSorting(e.name.to_owned())),
+                Ordering::Equal => {
+                    Err(DirectoryError::DuplicateName(e.name.to_owned().try_into()?))
+                }
+                Ordering::Greater => Ok(e.name.as_ref()),
             }
-            .into_name_and_node()?;
-
-            dir.add(name, node)?;
-        }
-        let mut last_directory_name: &[u8] = b"";
-        for directory in directory.directories.iter().map(move |directory| {
-            update_if_lt_prev(&mut last_directory_name, &directory.name).map(|()| directory.clone())
-        }) {
-            let directory = directory?;
-
-            let (name, node) = Node {
-                node: Some(node::Node::Directory(directory)),
+        })?;
+        value.symlinks.iter().try_fold(&b""[..], |prev_name, e| {
+            match e.name.as_ref().cmp(prev_name) {
+                Ordering::Less => Err(DirectoryError::WrongSorting(e.name.to_owned())),
+                Ordering::Equal => {
+                    Err(DirectoryError::DuplicateName(e.name.to_owned().try_into()?))
+                }
+                Ordering::Greater => Ok(e.name.as_ref()),
             }
-            .into_name_and_node()?;
+        })?;
 
-            dir.add(name, node)?;
+        let mut elems: Vec<(PathComponent, crate::Node)> =
+            Vec::with_capacity(value.directories.len() + value.files.len() + value.symlinks.len());
+
+        for e in value.directories {
+            elems.push(
+                Node {
+                    node: Some(node::Node::Directory(e)),
+                }
+                .into_name_and_node()?,
+            );
         }
-        let mut last_symlink_name: &[u8] = b"";
-        for symlink in directory.symlinks.iter().map(move |symlink| {
-            update_if_lt_prev(&mut last_symlink_name, &symlink.name).map(|()| symlink.clone())
-        }) {
-            let symlink = symlink?;
 
-            let (name, node) = Node {
-                node: Some(node::Node::Symlink(symlink)),
-            }
-            .into_name_and_node()?;
+        for e in value.files {
+            elems.push(
+                Node {
+                    node: Some(node::Node::File(e)),
+                }
+                .into_name_and_node()?,
+            )
+        }
 
-            dir.add(name, node)?;
+        for e in value.symlinks {
+            elems.push(
+                Node {
+                    node: Some(node::Node::Symlink(e)),
+                }
+                .into_name_and_node()?,
+            )
         }
 
-        Ok(dir)
+        crate::Directory::try_from_iter(elems)
     }
 }