feat(tvix/castore/proto): add owned conv to castore::Directory
Replace the hand-rolled code comparing names with a try_fold. Also, make it slightly stricter here, detecting duplicates in the same fields earlier. Change-Id: I9c560838ece88c3b8b339249a8ecbf3b05969538 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12226 Autosubmit: flokli <flokli@flokli.de> Reviewed-by: edef <edef@edef.eu> Tested-by: BuildkiteCI
This commit is contained in:
parent
96832c0411
commit
0cfe2aaf6a
2 changed files with 86 additions and 67 deletions
|
@ -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()
|
||||
// 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()),
|
||||
}
|
||||
})?;
|
||||
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()),
|
||||
}
|
||||
})?;
|
||||
|
||||
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()?,
|
||||
);
|
||||
}
|
||||
|
||||
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)),
|
||||
for e in value.files {
|
||||
elems.push(
|
||||
Node {
|
||||
node: Some(node::Node::File(e)),
|
||||
}
|
||||
.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)),
|
||||
}
|
||||
.into_name_and_node()?;
|
||||
|
||||
dir.add(name, 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()?;
|
||||
|
||||
dir.add(name, node)?;
|
||||
.into_name_and_node()?,
|
||||
)
|
||||
}
|
||||
|
||||
Ok(dir)
|
||||
for e in value.symlinks {
|
||||
elems.push(
|
||||
Node {
|
||||
node: Some(node::Node::Symlink(e)),
|
||||
}
|
||||
.into_name_and_node()?,
|
||||
)
|
||||
}
|
||||
|
||||
crate::Directory::try_from_iter(elems)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ fn validate_invalid_names() {
|
|||
{
|
||||
let d = Directory {
|
||||
directories: vec![DirectoryNode {
|
||||
name: "".into(),
|
||||
name: b"\0"[..].into(),
|
||||
digest: DUMMY_DIGEST.to_vec().into(),
|
||||
size: 42,
|
||||
}],
|
||||
|
@ -163,7 +163,7 @@ fn validate_invalid_names() {
|
|||
};
|
||||
match crate::Directory::try_from(d).expect_err("must fail") {
|
||||
DirectoryError::InvalidName(n) => {
|
||||
assert_eq!(n.as_ref(), b"")
|
||||
assert_eq!(n.as_ref(), b"\0")
|
||||
}
|
||||
_ => panic!("unexpected error"),
|
||||
};
|
||||
|
@ -282,7 +282,7 @@ fn validate_sorting() {
|
|||
}
|
||||
}
|
||||
|
||||
// "a" exists twice, bad.
|
||||
// "a" exists twice (same types), bad.
|
||||
{
|
||||
let d = Directory {
|
||||
directories: vec![
|
||||
|
@ -307,6 +307,28 @@ fn validate_sorting() {
|
|||
}
|
||||
}
|
||||
|
||||
// "a" exists twice (different types), bad.
|
||||
{
|
||||
let d = Directory {
|
||||
directories: vec![DirectoryNode {
|
||||
name: "a".into(),
|
||||
digest: DUMMY_DIGEST.to_vec().into(),
|
||||
size: 42,
|
||||
}],
|
||||
symlinks: vec![SymlinkNode {
|
||||
name: "a".into(),
|
||||
target: "b".into(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
match crate::Directory::try_from(d).expect_err("must fail") {
|
||||
DirectoryError::DuplicateName(s) => {
|
||||
assert_eq!(s.as_ref(), b"a");
|
||||
}
|
||||
_ => panic!("unexpected error"),
|
||||
}
|
||||
}
|
||||
|
||||
// "a" comes before "b", all good.
|
||||
{
|
||||
let d = Directory {
|
||||
|
|
Loading…
Reference in a new issue