manifest: add list directory functionality

Summary:
`listdir` returns the contents a directory in the manifest. The format
is pretty simple, containing only the simple names of the files and or
directories. I don't know if this is something that eden can use because
it seems to simple. In other words, we have something but we may want
to iterate on it before we market it broadly.

Reviewed By: quark-zju

Differential Revision: D17098082

fbshipit-source-id: d6aa42c96781cf1f8b2e916fa10bb275593bdc65
This commit is contained in:
Stefan Filip 2019-08-30 10:48:01 -07:00 committed by Facebook Github Bot
parent ffb563f1bb
commit f8fa8d9367
2 changed files with 71 additions and 2 deletions

View File

@ -142,6 +142,19 @@ py_class!(class treemanifest |py| {
Ok(result)
}
def listdir(&self, path: &PyBytes) -> PyResult<Vec<PyBytes>> {
let repo_path = pybytes_to_path(py, path);
let tree = self.underlying(py).borrow();
let result = match tree.list(&repo_path).map_pyerr::<exc::RuntimeError>(py)? {
manifest::tree::List::NotFound | manifest::tree::List::File => vec![],
manifest::tree::List::Directory(components) =>
components.into_iter().map(|component|
path_to_pybytes(py, component.as_path_component())
).collect()
};
Ok(result)
}
def text(&self) -> PyResult<PyBytes> {
let mut lines = Vec::new();
let tree = self.underlying(py).borrow();
@ -457,8 +470,8 @@ fn pybytes_to_path(py: Python, pybytes: &PyBytes) -> RepoPathBuf {
local_bytes_to_repo_path(pybytes.data(py)).to_owned()
}
fn path_to_pybytes(py: Python, path: &RepoPath) -> PyBytes {
PyBytes::new(py, repo_path_to_local_bytes(path))
fn path_to_pybytes<T: AsRef<RepoPath>>(py: Python, path: T) -> PyBytes {
PyBytes::new(py, repo_path_to_local_bytes(path.as_ref()))
}
fn pystring_to_file_type(py: Python, pystring: &PyString) -> PyResult<FileType> {

View File

@ -434,6 +434,18 @@ impl Tree {
Ok(executor.converted_nodes.into_iter())
}
pub fn list(&self, path: &RepoPath) -> Fallible<List> {
let directory = match self.get_link(path)? {
None => return Ok(List::NotFound),
Some(Leaf(_)) => return Ok(List::File),
Some(Ephemeral(content)) => content,
Some(Durable(entry)) => entry.get_links(&self.store, path)?,
};
Ok(List::Directory(
directory.keys().map(|key| key.to_owned()).collect(),
))
}
fn get_link(&self, path: &RepoPath) -> Fallible<Option<&Link>> {
let mut cursor = &self.root;
for (parent, component) in path.parents().zip(path.components()) {
@ -680,6 +692,13 @@ where
}
}
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum List {
NotFound,
File,
Directory(Vec<PathComponentBuf>),
}
/// The purpose of this function is to provide compatible behavior with the C++ implementation
/// of the treemanifest. This function is problematic because it goes through abstraction
/// boundaries and is built with the assumption that the storage format is the same as the
@ -688,6 +707,9 @@ where
/// This function returns the nodes that need to be sent over the wire for a subtree of the
/// manifest to be fully hydrated. The subtree is represented by `path` and `node`. The data
/// that is present locally by the client is represented by `other_nodes`.
///
/// It is undefined what this function will do when called with a path that points to a file
/// or with nodes that don't make sense.
// NOTE: The implementation is currently custom. Consider converting the code to use Cursor.
// The suggestion received in code review was also to consider making the return type more
// simple (RepoPath, Node) and letting the call sites deal with the Bytes.
@ -1631,4 +1653,38 @@ mod tests {
);
// it is illegal to call compat_subtree_diff with "baz" but we can't validate for it
}
#[test]
fn test_list() {
let mut tree = Tree::ephemeral(Arc::new(TestStore::new()));
tree.insert(repo_path_buf("a1/b1/c1"), meta("10")).unwrap();
tree.insert(repo_path_buf("a1/b2"), meta("20")).unwrap();
let _node = tree.flush().unwrap();
tree.insert(repo_path_buf("a2/b3/c2"), meta("30")).unwrap();
tree.insert(repo_path_buf("a2/b4"), meta("30")).unwrap();
assert_eq!(tree.list(repo_path("not_found")).unwrap(), List::NotFound);
assert_eq!(tree.list(repo_path("a1/b1/c1")).unwrap(), List::File);
assert_eq!(
tree.list(repo_path("a1/b1")).unwrap(),
List::Directory(vec![path_component_buf("c1")]),
);
assert_eq!(
tree.list(repo_path("a1")).unwrap(),
List::Directory(vec![path_component_buf("b1"), path_component_buf("b2")]),
);
assert_eq!(tree.list(repo_path("a2/b3/c2")).unwrap(), List::File);
assert_eq!(
tree.list(repo_path("a2/b3")).unwrap(),
List::Directory(vec![path_component_buf("c2")]),
);
assert_eq!(
tree.list(repo_path("a2")).unwrap(),
List::Directory(vec![path_component_buf("b3"), path_component_buf("b4")]),
);
assert_eq!(
tree.list(RepoPath::empty()).unwrap(),
List::Directory(vec![path_component_buf("a1"), path_component_buf("a2")]),
);
}
}