mirror of
https://github.com/facebook/sapling.git
synced 2025-01-06 04:43:19 +03:00
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:
parent
ffb563f1bb
commit
f8fa8d9367
@ -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> {
|
||||
|
@ -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")]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user