From 6100b12006c25678990cee7de935b41746edc291 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 28 Nov 2022 14:41:29 -0800 Subject: [PATCH] Updated docs, include tests in file_system.rs and fix bugs found. Signed-off-by: Han Xu --- radicle-surf/README.md | 6 --- radicle-surf/examples/browsing.rs | 4 +- radicle-surf/src/diff.rs | 2 + radicle-surf/src/file_system/directory.rs | 37 ++++++++++++-- radicle-surf/src/lib.rs | 57 ++------------------- radicle-surf/src/source/object/tree.rs | 2 +- radicle-surf/t/src/file_system.rs | 60 +++++++++++++++++++---- radicle-surf/t/src/lib.rs | 3 ++ 8 files changed, 95 insertions(+), 76 deletions(-) diff --git a/radicle-surf/README.md b/radicle-surf/README.md index aceaa98..405e755 100644 --- a/radicle-surf/README.md +++ b/radicle-surf/README.md @@ -20,9 +20,3 @@ our [LICENSE](../LICENSE) file. ## The Community Join our community disccussions at [radicle.community](https://radicle.community)! - - -# Example - -To a taste for the capabilities of `radicle-surf` we provide an example below, but we also -keep our documentation and doc-tests up to date. diff --git a/radicle-surf/examples/browsing.rs b/radicle-surf/examples/browsing.rs index ea8401f..cdccd55 100644 --- a/radicle-surf/examples/browsing.rs +++ b/radicle-surf/examples/browsing.rs @@ -51,10 +51,10 @@ fn main() { fn print_directory(d: &Directory, repo: &Repository, indent_level: usize) { let indent = " ".repeat(indent_level * 4); println!("{}{}/", &indent, d.name()); - for entry in d.entries(repo).unwrap().entries() { + for entry in d.entries(repo).unwrap() { match entry { directory::Entry::File(f) => println!(" {}{}", &indent, f.name()), - directory::Entry::Directory(d) => print_directory(d, repo, indent_level + 1), + directory::Entry::Directory(d) => print_directory(&d, repo, indent_level + 1), } } } diff --git a/radicle-surf/src/diff.rs b/radicle-surf/src/diff.rs index e7b0676..10ad9f9 100644 --- a/radicle-surf/src/diff.rs +++ b/radicle-surf/src/diff.rs @@ -15,6 +15,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +//! Types that represent diff(s) in a Git repo. + #![allow(dead_code, unused_variables, missing_docs)] use std::path::PathBuf; diff --git a/radicle-surf/src/file_system/directory.rs b/radicle-surf/src/file_system/directory.rs index 204440a..41eea0b 100644 --- a/radicle-surf/src/file_system/directory.rs +++ b/radicle-surf/src/file_system/directory.rs @@ -44,6 +44,8 @@ pub mod error { Entry(#[from] Entry), #[error(transparent)] File(#[from] File), + #[error("the path {0} is not valid")] + InvalidPath(String), } #[derive(Debug, Error, PartialEq, Eq)] @@ -178,6 +180,19 @@ impl Entries { } } +impl Iterator for Entries { + type Item = Entry; + + fn next(&mut self) -> Option { + // Can be improved when `pop_first()` is stable for BTreeMap. + let next_key = match self.listing.keys().next() { + Some(k) => k.clone(), + None => return None, + }; + self.listing.remove(&next_key) + } +} + /// An `Entry` is either a [`File`] entry or a [`Directory`] entry. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Entry { @@ -228,6 +243,16 @@ impl Entry { } } + /// Returns `true` if the `Entry` is a file. + pub fn is_file(&self) -> bool { + matches!(self, Entry::File(_)) + } + + /// Returns `true` if the `Entry` is a directory. + pub fn is_directory(&self) -> bool { + matches!(self, Entry::Directory(_)) + } + pub(crate) fn from_entry( entry: &git2::TreeEntry, path: PathBuf, @@ -366,14 +391,18 @@ impl Directory { P: AsRef, { // Search the path in git2 tree. + let path = path.as_ref(); let git2_tree = repo.find_tree(self.id)?; let entry = git2_tree - .get_path(path.as_ref()) + .get_path(path) .map(Some) .or_matches::(is_not_found_err, || Ok(None))?; + let parent = path + .parent() + .ok_or_else(|| error::Directory::InvalidPath(path.to_string_lossy().to_string()))?; Ok(entry - .and_then(|entry| Entry::from_entry(&entry, path.as_ref().to_path_buf()).transpose()) + .and_then(|entry| Entry::from_entry(&entry, parent.to_path_buf()).transpose()) .transpose() .unwrap()) } @@ -396,13 +425,13 @@ impl Directory { /// Find the `Directory` found at `path`, if it exists. pub fn find_directory

( &self, - path: P, + path: &P, repo: &Repository, ) -> Result, error::Directory> where P: AsRef, { - Ok(match self.find_entry(&path, repo)? { + Ok(match self.find_entry(path, repo)? { Some(Entry::Directory(d)) => Some(d), _ => None, }) diff --git a/radicle-surf/src/lib.rs b/radicle-surf/src/lib.rs index 53e50e8..a400e96 100644 --- a/radicle-surf/src/lib.rs +++ b/radicle-surf/src/lib.rs @@ -1,7 +1,7 @@ // This file is part of radicle-git // // -// Copyright (C) 2019-2020 The Radicle Team +// Copyright (C) 2019-2022 The Radicle Team // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License version 3 or @@ -15,65 +15,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Welcome to `radicle-surf`! -//! //! `radicle-surf` is a library to describe a Git repository as a file system. //! It aims to provide an easy-to-use API to browse a repository via the concept //! of files and directories for any given revision. It also allows the user to //! diff any two different revisions. //! -//! One of the use cases would be to create a web GUI for interacting with a Git -//! repository (thinking GitHub, GitLab or similar systems). +//! The main entry point of the API is [git::Repository]. //! -//! Let's start surfing (and apologies for the `expect`s): -//! -//! ``` -//! use radicle_surf::git; -//! use radicle_surf::file_system::{Label, Path, SystemType}; -//! use radicle_surf::file_system::unsound; -//! use pretty_assertions::assert_eq; -//! use std::str::FromStr; -//! # use std::error::Error; -//! -//! # fn main() -> Result<(), Box> { -//! // We're going to point to this repo. -//! let repo = git::Repository::open("./data/git-platinum")?; -//! -//! // Get the snapshot of the directory for our current HEAD of history. -//! let directory = repo.root_dir("80ded66281a4de2889cc07293a8f10947c6d57fe")?; -//! -//! // Let's get a Path to the memory.rs file -//! let memory = unsound::path::new("src/memory.rs"); -//! -//! // And assert that we can find it! -//! assert!(directory.find_file(memory).is_some()); -//! -//! let root_contents = directory.list_directory(); -//! -//! assert_eq!(root_contents, vec![ -//! SystemType::file(unsound::label::new(".i-am-well-hidden")), -//! SystemType::file(unsound::label::new(".i-too-am-hidden")), -//! SystemType::file(unsound::label::new("README.md")), -//! SystemType::directory(unsound::label::new("bin")), -//! SystemType::directory(unsound::label::new("src")), -//! SystemType::directory(unsound::label::new("text")), -//! SystemType::directory(unsound::label::new("this")), -//! ]); -//! -//! let src = directory -//! .find_directory(Path::new(unsound::label::new("src"))) -//! .expect("failed to find src"); -//! let src_contents = src.list_directory(); -//! -//! assert_eq!(src_contents, vec![ -//! SystemType::file(unsound::label::new("Eval.hs")), -//! SystemType::file(unsound::label::new("Folder.svelte")), -//! SystemType::file(unsound::label::new("memory.rs")), -//! ]); -//! # -//! # Ok(()) -//! # } -//! ``` +//! Let's start surfing! pub extern crate git_ref_format; diff --git a/radicle-surf/src/source/object/tree.rs b/radicle-surf/src/source/object/tree.rs index c06c416..ef70379 100644 --- a/radicle-surf/src/source/object/tree.rs +++ b/radicle-surf/src/source/object/tree.rs @@ -57,7 +57,7 @@ impl Tree { None => repo.root_dir(revision)?, Some(path) => repo .root_dir(revision)? - .find_directory(path, repo)? + .find_directory(&path, repo)? .ok_or_else(|| Error::PathNotFound(path.to_path_buf()))?, }; diff --git a/radicle-surf/t/src/file_system.rs b/radicle-surf/t/src/file_system.rs index 7bf97ef..a20b984 100644 --- a/radicle-surf/t/src/file_system.rs +++ b/radicle-surf/t/src/file_system.rs @@ -3,11 +3,10 @@ //! Unit tests for radicle_surf::file_system -#[cfg(test)] mod directory { use git_ref_format::refname; use radicle_surf::{ - file_system::directory, + file_system::{directory, Entry}, git::{Branch, Repository}, }; use std::path::Path; @@ -21,35 +20,78 @@ mod directory { // find_entry for a file. let path = Path::new("src/memory.rs"); - let entry = root.find_entry(path, &repo).unwrap(); + let entry = root.find_entry(&path, &repo).unwrap(); assert!(matches!(entry, Some(directory::Entry::File(_)))); // find_entry for a directory. let path = Path::new("this/is/a/really/deeply/nested/directory/tree"); - let entry = root.find_entry(path, &repo).unwrap(); + let entry = root.find_entry(&path, &repo).unwrap(); assert!(matches!(entry, Some(directory::Entry::Directory(_)))); // find_entry for a non-leaf directory and its relative path. let path = Path::new("text"); - let entry = root.find_entry(path, &repo).unwrap(); + let entry = root.find_entry(&path, &repo).unwrap(); assert!(matches!(entry, Some(directory::Entry::Directory(_)))); if let Some(directory::Entry::Directory(sub_dir)) = entry { let inner_path = Path::new("garden.txt"); - let inner_entry = sub_dir.find_entry(inner_path, &repo).unwrap(); + let inner_entry = sub_dir.find_entry(&inner_path, &repo).unwrap(); assert!(matches!(inner_entry, Some(directory::Entry::File(_)))); } // find_entry for non-existing file let path = Path::new("this/is/a/really/missing_file"); - let result = root.find_entry(path, &repo).unwrap(); + let result = root.find_entry(&path, &repo).unwrap(); assert_eq!(result, None); // find_entry for absolute path: fail. let path = Path::new("/src/memory.rs"); - let result = root.find_entry(path, &repo); + let result = root.find_entry(&path, &repo); assert!(result.is_err()); } + #[test] + fn directory_find_file_and_directory() { + let repo = Repository::open(GIT_PLATINUM).unwrap(); + // Get the snapshot of the directory for a given commit. + let root = repo + .root_dir(&"80ded66281a4de2889cc07293a8f10947c6d57fe") + .unwrap(); + + // Assert that we can find the memory.rs file! + assert!(root + .find_file(&Path::new("src/memory.rs"), &repo) + .unwrap() + .is_some()); + + let root_contents: Vec = root.entries(&repo).unwrap().collect(); + assert_eq!(root_contents.len(), 7); + assert!(root_contents[0].is_file()); + assert!(root_contents[1].is_file()); + assert!(root_contents[2].is_file()); + assert_eq!(root_contents[0].name(), ".i-am-well-hidden"); + assert_eq!(root_contents[1].name(), ".i-too-am-hidden"); + assert_eq!(root_contents[2].name(), "README.md"); + + assert!(root_contents[3].is_directory()); + assert!(root_contents[4].is_directory()); + assert!(root_contents[5].is_directory()); + assert!(root_contents[6].is_directory()); + assert_eq!(root_contents[3].name(), "bin"); + assert_eq!(root_contents[4].name(), "src"); + assert_eq!(root_contents[5].name(), "text"); + assert_eq!(root_contents[6].name(), "this"); + + let src = root + .find_directory(&Path::new("src"), &repo) + .unwrap() + .unwrap(); + let src_contents: Vec = src.entries(&repo).unwrap().collect(); + assert_eq!(src_contents.len(), 3); + assert_eq!(src_contents[0].name(), "Eval.hs"); + assert_eq!(src_contents[1].name(), "Folder.svelte"); + assert_eq!(src_contents[2].name(), "memory.rs"); + } + #[test] fn directory_size() { let repo = Repository::open(GIT_PLATINUM).unwrap(); @@ -64,7 +106,7 @@ mod directory { */ let path = Path::new("src"); - let entry = root.find_entry(path, &repo).unwrap(); + let entry = root.find_entry(&path, &repo).unwrap(); assert!(matches!(entry, Some(directory::Entry::Directory(_)))); if let Some(directory::Entry::Directory(d)) = entry { assert_eq!(16297, d.size(&repo).unwrap()); diff --git a/radicle-surf/t/src/lib.rs b/radicle-surf/t/src/lib.rs index 7d021db..fc9023d 100644 --- a/radicle-surf/t/src/lib.rs +++ b/radicle-surf/t/src/lib.rs @@ -3,3 +3,6 @@ #[cfg(test)] mod git; + +#[cfg(test)] +mod file_system;