From 58d69ebf7f441cfde043593fc7efbc8912033759 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Thu, 8 Sep 2022 10:45:44 -0700 Subject: [PATCH] copy radicle-surf and radicle-source into this repo. Signed-off-by: Han Xu --- .github/workflows/ci.yaml | 11 + .gitignore | 4 + Cargo.toml | 2 + deny.toml | 53 +- radicle-source/Cargo.toml | 31 + radicle-source/src/branch.rs | 108 ++ radicle-source/src/commit.rs | 243 +++ radicle-source/src/error.rs | 46 + radicle-source/src/lib.rs | 51 + radicle-source/src/object.rs | 76 + radicle-source/src/object/blob.rs | 218 +++ radicle-source/src/object/tree.rs | 177 ++ radicle-source/src/oid.rs | 44 + radicle-source/src/person.rs | 28 + radicle-source/src/revision.rs | 175 ++ radicle-source/src/syntax.rs | 87 + radicle-source/src/tag.rs | 60 + radicle-surf/Cargo.toml | 52 + radicle-surf/DEVELOPMENT.md | 83 + radicle-surf/README.md | 74 + radicle-surf/benches/last_commit.rs | 45 + radicle-surf/build.rs | 81 + radicle-surf/data/README.md | 21 + radicle-surf/data/git-platinum.tgz | Bin 0 -> 95889 bytes radicle-surf/data/mock-branches.txt | 12 + radicle-surf/docs/denotational-design.md | 265 +++ radicle-surf/examples/diff.rs | 176 ++ radicle-surf/src/diff.rs | 767 ++++++++ radicle-surf/src/diff/git.rs | 306 ++++ radicle-surf/src/file_system.rs | 118 ++ radicle-surf/src/file_system/directory.rs | 722 ++++++++ radicle-surf/src/file_system/error.rs | 79 + radicle-surf/src/file_system/path.rs | 470 +++++ radicle-surf/src/file_system/path/unsound.rs | 62 + radicle-surf/src/lib.rs | 93 + radicle-surf/src/nonempty.rs | 33 + radicle-surf/src/tree.rs | 1191 ++++++++++++ radicle-surf/src/vcs.rs | 238 +++ radicle-surf/src/vcs/git.rs | 1725 ++++++++++++++++++ radicle-surf/src/vcs/git/branch.rs | 193 ++ radicle-surf/src/vcs/git/commit.rs | 102 ++ radicle-surf/src/vcs/git/error.rs | 126 ++ radicle-surf/src/vcs/git/ext.rs | 111 ++ radicle-surf/src/vcs/git/namespace.rs | 82 + radicle-surf/src/vcs/git/reference.rs | 302 +++ radicle-surf/src/vcs/git/reference/glob.rs | 183 ++ radicle-surf/src/vcs/git/repo.rs | 446 +++++ radicle-surf/src/vcs/git/stats.rs | 36 + radicle-surf/src/vcs/git/tag.rs | 176 ++ 49 files changed, 9783 insertions(+), 1 deletion(-) create mode 100644 radicle-source/Cargo.toml create mode 100644 radicle-source/src/branch.rs create mode 100644 radicle-source/src/commit.rs create mode 100644 radicle-source/src/error.rs create mode 100644 radicle-source/src/lib.rs create mode 100644 radicle-source/src/object.rs create mode 100644 radicle-source/src/object/blob.rs create mode 100644 radicle-source/src/object/tree.rs create mode 100644 radicle-source/src/oid.rs create mode 100644 radicle-source/src/person.rs create mode 100644 radicle-source/src/revision.rs create mode 100644 radicle-source/src/syntax.rs create mode 100644 radicle-source/src/tag.rs create mode 100644 radicle-surf/Cargo.toml create mode 100644 radicle-surf/DEVELOPMENT.md create mode 100644 radicle-surf/README.md create mode 100644 radicle-surf/benches/last_commit.rs create mode 100644 radicle-surf/build.rs create mode 100644 radicle-surf/data/README.md create mode 100644 radicle-surf/data/git-platinum.tgz create mode 100644 radicle-surf/data/mock-branches.txt create mode 100644 radicle-surf/docs/denotational-design.md create mode 100644 radicle-surf/examples/diff.rs create mode 100644 radicle-surf/src/diff.rs create mode 100644 radicle-surf/src/diff/git.rs create mode 100644 radicle-surf/src/file_system.rs create mode 100644 radicle-surf/src/file_system/directory.rs create mode 100644 radicle-surf/src/file_system/error.rs create mode 100644 radicle-surf/src/file_system/path.rs create mode 100644 radicle-surf/src/file_system/path/unsound.rs create mode 100644 radicle-surf/src/lib.rs create mode 100644 radicle-surf/src/nonempty.rs create mode 100644 radicle-surf/src/tree.rs create mode 100644 radicle-surf/src/vcs.rs create mode 100644 radicle-surf/src/vcs/git.rs create mode 100644 radicle-surf/src/vcs/git/branch.rs create mode 100644 radicle-surf/src/vcs/git/commit.rs create mode 100644 radicle-surf/src/vcs/git/error.rs create mode 100644 radicle-surf/src/vcs/git/ext.rs create mode 100644 radicle-surf/src/vcs/git/namespace.rs create mode 100644 radicle-surf/src/vcs/git/reference.rs create mode 100644 radicle-surf/src/vcs/git/reference/glob.rs create mode 100644 radicle-surf/src/vcs/git/repo.rs create mode 100644 radicle-surf/src/vcs/git/stats.rs create mode 100644 radicle-surf/src/vcs/git/tag.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c3ebf90..ad8011f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -82,6 +82,13 @@ jobs: continue-on-error: ${{ matrix.toolchain == 'nightly' }} steps: - uses: actions/checkout@master + with: + fetch-depth: 0 + # The 'main' branch is needed for radicle-surf vcs::git::tests::test_submodule_failure, + # however actions/checkout uses the default branch 'master' at this moment. + # We will improve the test soon to remove this need. + - run: git branch main + - run: git branch -u origin/main main - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -111,6 +118,10 @@ jobs: needs: build-macos steps: - uses: actions/checkout@master + with: + fetch-depth: 0 + - run: git branch main + - run: git branch -u origin/main main - uses: actions-rs/toolchain@v1 with: profile: minimal diff --git a/.gitignore b/.gitignore index 4942acc..3b8a63d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ tags Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# This is an inner git repo unpacked (if necessary) from the archive. +# Git doesn't like nested repos, and we don't really want to use submodules. +radicle-surf/data/git-platinum diff --git a/Cargo.toml b/Cargo.toml index 87f473b..2d36867 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,9 @@ members = [ "radicle-git-ext", "radicle-git-types", "radicle-macros", + "radicle-source", "radicle-std-ext", + "radicle-surf", # TODO: port gitd-lib over # "cli/gitd-lib", "test", diff --git a/deny.toml b/deny.toml index c740252..1ec00b0 100644 --- a/deny.toml +++ b/deny.toml @@ -47,12 +47,63 @@ allow = [ "CC0-1.0", "GPL-3.0", "MIT", + "Unicode-DFS-2016", "Unlicense", ] # List of explictly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. -deny = [] +deny = [ + # As per https://www.gnu.org/licenses/license-list.html#GPLIncompatibleLicenses + "AGPL-1.0", +# fails to parse: +# "AFL-1.0", +# "AFL-1.2", +# "AFL-2.0", +# "AFL-2.1", +# "AFL-3.0", + "Apache-1.0", + "Apache-1.1", + "APSL-2.0", + "BitTorrent-1.0", + "BitTorrent-1.1", + "BSD-4-Clause", + "CECILL-B", + "CECILL-C", + "CDDL-1.0", + "CDDL-1.1", + "CNRI-Python", + "CPAL-1.0", + "CPL-1.0", + "Condor-1.1", + "EPL-1.0", + "EPL-2.0", + "EUPL-1.1", + "EUPL-1.2", + "gnuplot", + "IPL-1.0", + "LPPL-1.3a", + "LPPL-1.2", + "LPL-1.02", + "MS-PL", + "MS-RL", + "MPL-1.1", + "NOSL", + "NPL-1.0", + "NPL-1.1", + "Nokia", + "OpenSSL", + "PHP-3.01", + "QPL-1.0", + "RPSL-1.0", + "SISSL", + "SPL-1.0", + "xinetd", + "YPL-1.1", + "Zend-2.0", + "Zimbra-1.3", + "ZPL-1.1" +] # Lint level for licenses considered copyleft copyleft = "allow" # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses diff --git a/radicle-source/Cargo.toml b/radicle-source/Cargo.toml new file mode 100644 index 0000000..166dcac --- /dev/null +++ b/radicle-source/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "radicle-source" +description = "A high level API for browsing source files" +version = "0.4.0" +authors = ["The Radicle Team "] +edition = "2018" +homepage = "https://github.com/radicle-dev/radicle-surf" +repository = "https://github.com/radicle-dev/radicle-surf" +license = "GPL-3.0-or-later" + +[features] +syntax = ["syntect"] + +[dependencies] +base64 = "0.13" +log = "0.4" +lazy_static = "1.4" +nonempty = "0.6" +serde = { version = "1.0", features = [ "derive" ] } +syntect = { version = "4.2", optional = true } +thiserror = "1.0" + +[dependencies.git2] +version = ">= 0.12" +default-features = false +features = [] + +[dependencies.radicle-surf] +version = "^0.8.0" +features = ["serialize"] +path = "../radicle-surf" diff --git a/radicle-source/src/branch.rs b/radicle-source/src/branch.rs new file mode 100644 index 0000000..7dfbbeb --- /dev/null +++ b/radicle-source/src/branch.rs @@ -0,0 +1,108 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::fmt; + +use serde::{Deserialize, Serialize}; + +use radicle_surf::vcs::git::{self, Browser, RefScope}; + +use crate::error::Error; + +/// Branch name representation. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)] +pub struct Branch(pub(crate) String); + +impl From for Branch { + fn from(name: String) -> Self { + Self(name) + } +} + +impl From for Branch { + fn from(branch: git::Branch) -> Self { + Self(branch.name.to_string()) + } +} + +impl fmt::Display for Branch { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Given a project id to a repo returns the list of branches. +/// +/// # Errors +/// +/// Will return [`Error`] if the project doesn't exist or the surf interaction +/// fails. +pub fn branches(browser: &Browser<'_>, filter: RefScope) -> Result, Error> { + let mut branches = browser + .list_branches(filter)? + .into_iter() + .map(|b| Branch(b.name.name().to_string())) + .collect::>(); + + branches.sort(); + + Ok(branches) +} + +/// Information about a locally checked out repository. +#[derive(Deserialize, Serialize)] +pub struct LocalState { + /// List of branches. + branches: Vec, +} + +/// Given a path to a repo returns the list of branches and if it is managed by +/// coco. +/// +/// # Errors +/// +/// Will return [`Error`] if the repository doesn't exist. +pub fn local_state(repo_path: &str, default_branch: &str) -> Result { + let repo = git2::Repository::open(repo_path).map_err(git::error::Error::from)?; + let first_branch = repo + .branches(Some(git2::BranchType::Local)) + .map_err(git::error::Error::from)? + .filter_map(|branch_result| { + let (branch, _) = branch_result.ok()?; + let name = branch.name().ok()?; + name.map(String::from) + }) + .min() + .ok_or(Error::NoBranches)?; + + let repo = git::Repository::new(repo_path)?; + + let browser = match Browser::new(&repo, git::Branch::local(default_branch)) { + Ok(browser) => browser, + Err(_) => Browser::new(&repo, git::Branch::local(&first_branch))?, + }; + + let mut branches = browser + .list_branches(RefScope::Local)? + .into_iter() + .map(|b| Branch(b.name.name().to_string())) + .collect::>(); + + branches.sort(); + + Ok(LocalState { branches }) +} diff --git a/radicle-source/src/commit.rs b/radicle-source/src/commit.rs new file mode 100644 index 0000000..308e31e --- /dev/null +++ b/radicle-source/src/commit.rs @@ -0,0 +1,243 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::convert::TryFrom as _; + +use serde::{ + ser::{SerializeStruct as _, Serializer}, + Serialize, +}; + +use radicle_surf::{ + diff, + vcs::git::{self, Browser, Rev}, +}; + +use crate::{branch::Branch, error::Error, person::Person, revision::Revision}; + +/// Commit statistics. +#[derive(Clone, Serialize)] +pub struct Stats { + /// Additions. + pub additions: u64, + /// Deletions. + pub deletions: u64, +} + +/// Representation of a changeset between two revs. +#[derive(Clone, Serialize)] +pub struct Commit { + /// The commit header. + pub header: Header, + /// The change statistics for this commit. + pub stats: Stats, + /// The changeset introduced by this commit. + pub diff: diff::Diff, + /// The list of branches this commit belongs to. + pub branches: Vec, +} + +/// Representation of a code commit. +#[derive(Clone)] +pub struct Header { + /// Identifier of the commit in the form of a sha1 hash. Often referred to + /// as oid or object id. + pub sha1: git2::Oid, + /// The author of the commit. + pub author: Person, + /// The summary of the commit message body. + pub summary: String, + /// The entire commit message body. + pub message: String, + /// The committer of the commit. + pub committer: Person, + /// The recorded time of the committer signature. This is a convenience + /// alias until we expose the actual author and commiter signatures. + pub committer_time: git2::Time, +} + +impl Header { + /// Returns the commit description text. This is the text after the one-line + /// summary. + #[must_use] + pub fn description(&self) -> &str { + self.message + .strip_prefix(&self.summary) + .unwrap_or(&self.message) + .trim() + } +} + +impl From<&git::Commit> for Header { + fn from(commit: &git::Commit) -> Self { + Self { + sha1: commit.id, + author: Person { + name: commit.author.name.clone(), + email: commit.author.email.clone(), + }, + summary: commit.summary.clone(), + message: commit.message.clone(), + committer: Person { + name: commit.committer.name.clone(), + email: commit.committer.email.clone(), + }, + committer_time: commit.committer.time, + } + } +} + +impl Serialize for Header { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Header", 6)?; + state.serialize_field("sha1", &self.sha1.to_string())?; + state.serialize_field("author", &self.author)?; + state.serialize_field("summary", &self.summary)?; + state.serialize_field("description", &self.description())?; + state.serialize_field("committer", &self.committer)?; + state.serialize_field("committerTime", &self.committer_time.seconds())?; + state.end() + } +} + +/// A selection of commit headers and their statistics. +#[derive(Serialize)] +pub struct Commits { + /// The commit headers + pub headers: Vec
, + /// The statistics for the commit headers + pub stats: radicle_surf::vcs::git::Stats, +} + +/// Retrieves a [`Commit`]. +/// +/// # Errors +/// +/// Will return [`Error`] if the project doesn't exist or the surf interaction +/// fails. +pub fn commit(browser: &mut Browser<'_>, sha1: git2::Oid) -> Result { + browser.commit(sha1)?; + + let history = browser.get(); + let commit = history.first(); + + let diff = if let Some(parent) = commit.parents.first() { + browser.diff(*parent, sha1)? + } else { + browser.initial_diff(sha1)? + }; + + let mut deletions = 0; + let mut additions = 0; + + for file in &diff.modified { + if let diff::FileDiff::Plain { ref hunks } = file.diff { + for hunk in hunks.iter() { + for line in &hunk.lines { + match line { + diff::LineDiff::Addition { .. } => additions += 1, + diff::LineDiff::Deletion { .. } => deletions += 1, + _ => {}, + } + } + } + } + } + + for file in &diff.created { + if let diff::FileDiff::Plain { ref hunks } = file.diff { + for hunk in hunks.iter() { + for line in &hunk.lines { + if let diff::LineDiff::Addition { .. } = line { + additions += 1 + } + } + } + } + } + + for file in &diff.deleted { + if let diff::FileDiff::Plain { ref hunks } = file.diff { + for hunk in hunks.iter() { + for line in &hunk.lines { + if let diff::LineDiff::Deletion { .. } = line { + deletions += 1 + } + } + } + } + } + + let branches = browser + .revision_branches(sha1)? + .into_iter() + .map(Branch::from) + .collect(); + + Ok(Commit { + header: Header::from(commit), + stats: Stats { + additions, + deletions, + }, + diff, + branches, + }) +} + +/// Retrieves the [`Header`] for the given `sha1`. +/// +/// # Errors +/// +/// Will return [`Error`] if the project doesn't exist or the surf interaction +/// fails. +pub fn header(browser: &mut Browser<'_>, sha1: git2::Oid) -> Result { + browser.commit(sha1)?; + + let history = browser.get(); + let commit = history.first(); + + Ok(Header::from(commit)) +} + +/// Retrieves the [`Commit`] history for the given `revision`. +/// +/// # Errors +/// +/// Will return [`Error`] if the project doesn't exist or the surf interaction +/// fails. +pub fn commits

( + browser: &mut Browser<'_>, + maybe_revision: Option>, +) -> Result +where + P: ToString, +{ + let maybe_revision = maybe_revision.map(Rev::try_from).transpose()?; + + if let Some(revision) = maybe_revision { + browser.rev(revision)?; + } + + let headers = browser.get().iter().map(Header::from).collect(); + let stats = browser.get_stats()?; + + Ok(Commits { headers, stats }) +} diff --git a/radicle-source/src/error.rs b/radicle-source/src/error.rs new file mode 100644 index 0000000..05eb822 --- /dev/null +++ b/radicle-source/src/error.rs @@ -0,0 +1,46 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use radicle_surf::{file_system, git}; + +/// An error occurred when interacting with [`radicle_surf`] for browsing source +/// code. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// We expect at least one [`crate::revision::Revisions`] when looking at a + /// project, however the computation found none. + #[error( + "while trying to get user revisions we could not find any, there should be at least one" + )] + EmptyRevisions, + + /// An error occurred during a [`radicle_surf::file_system`] operation. + #[error(transparent)] + FileSystem(#[from] file_system::Error), + + /// An error occurred during a [`radicle_surf::git`] operation. + #[error(transparent)] + Git(#[from] git::error::Error), + + /// When trying to query a repositories branches, but there are none. + #[error("the repository has no branches")] + NoBranches, + + /// Trying to find a file path which could not be found. + #[error("the path '{0}' was not found")] + PathNotFound(file_system::Path), +} diff --git a/radicle-source/src/lib.rs b/radicle-source/src/lib.rs new file mode 100644 index 0000000..c23000c --- /dev/null +++ b/radicle-source/src/lib.rs @@ -0,0 +1,51 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Source code related functionality. + +/// To avoid incompatible versions of `radicle-surf`, `radicle-source` +/// re-exports the package under the `surf` alias. +pub use radicle_surf as surf; + +pub mod branch; +pub use branch::{branches, local_state, Branch, LocalState}; + +pub mod commit; +pub use commit::{commit, commits, Commit}; + +pub mod error; +pub use error::Error; + +pub mod object; +pub use object::{blob, tree, Blob, BlobContent, Info, ObjectType, Tree}; + +pub mod oid; +pub use oid::Oid; + +pub mod person; +pub use person::Person; + +pub mod revision; +pub use revision::Revision; + +#[cfg(feature = "syntax")] +pub mod syntax; +#[cfg(feature = "syntax")] +pub use syntax::SYNTAX_SET; + +pub mod tag; +pub use tag::{tags, Tag}; diff --git a/radicle-source/src/object.rs b/radicle-source/src/object.rs new file mode 100644 index 0000000..1343320 --- /dev/null +++ b/radicle-source/src/object.rs @@ -0,0 +1,76 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use serde::{ + ser::{SerializeStruct as _, Serializer}, + Serialize, +}; + +pub mod blob; +pub use blob::{blob, Blob, BlobContent}; + +pub mod tree; +pub use tree::{tree, Tree, TreeEntry}; + +use crate::commit; + +/// Git object types. +/// +/// `shafiul.github.io/gitbook/1_the_git_object_model.html` +#[derive(Debug, Eq, Ord, PartialOrd, PartialEq)] +pub enum ObjectType { + /// References a list of other trees and blobs. + Tree, + /// Used to store file data. + Blob, +} + +impl Serialize for ObjectType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Blob => serializer.serialize_unit_variant("ObjectType", 0, "BLOB"), + Self::Tree => serializer.serialize_unit_variant("ObjectType", 1, "TREE"), + } + } +} + +/// Set of extra information we carry for blob and tree objects returned from +/// the API. +pub struct Info { + /// Name part of an object. + pub name: String, + /// The type of the object. + pub object_type: ObjectType, + /// The last commmit that touched this object. + pub last_commit: Option, +} + +impl Serialize for Info { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Info", 3)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("objectType", &self.object_type)?; + state.serialize_field("lastCommit", &self.last_commit)?; + state.end() + } +} diff --git a/radicle-source/src/object/blob.rs b/radicle-source/src/object/blob.rs new file mode 100644 index 0000000..6eb81cf --- /dev/null +++ b/radicle-source/src/object/blob.rs @@ -0,0 +1,218 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::{ + convert::TryFrom as _, + str::{self, FromStr as _}, +}; + +use serde::{ + ser::{SerializeStruct as _, Serializer}, + Serialize, +}; + +use radicle_surf::{ + file_system, + vcs::git::{Browser, Rev}, +}; + +use crate::{ + commit, + error::Error, + object::{Info, ObjectType}, + revision::Revision, +}; + +#[cfg(feature = "syntax")] +use crate::syntax; + +/// File data abstraction. +pub struct Blob { + /// Actual content of the file, if the content is ASCII. + pub content: BlobContent, + /// Extra info for the file. + pub info: Info, + /// Absolute path to the object from the root of the repo. + pub path: String, +} + +impl Blob { + /// Indicates if the content of the [`Blob`] is binary. + #[must_use] + pub fn is_binary(&self) -> bool { + matches!(self.content, BlobContent::Binary(_)) + } + + /// Indicates if the content of the [`Blob`] is HTML. + #[must_use] + pub const fn is_html(&self) -> bool { + matches!(self.content, BlobContent::Html(_)) + } +} + +impl Serialize for Blob { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Blob", 5)?; + state.serialize_field("binary", &self.is_binary())?; + state.serialize_field("html", &self.is_html())?; + state.serialize_field("content", &self.content)?; + state.serialize_field("info", &self.info)?; + state.serialize_field("path", &self.path)?; + state.end() + } +} + +/// Variants of blob content. +#[derive(PartialEq, Eq)] +pub enum BlobContent { + /// Content is plain text and can be passed as a string. + Plain(String), + /// Content is syntax-highlighted HTML. + /// + /// Note that is necessary to enable the `syntax` feature flag for this + /// variant to be constructed. Use `highlighting::blob`, instead of + /// [`blob`] to get highlighted content. + Html(String), + /// Content is binary and needs special treatment. + Binary(Vec), +} + +impl Serialize for BlobContent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Plain(content) | Self::Html(content) => serializer.serialize_str(content), + Self::Binary(bytes) => { + let encoded = base64::encode(bytes); + serializer.serialize_str(&encoded) + }, + } + } +} + +/// Returns the [`Blob`] for a file at `revision` under `path`. +/// +/// # Errors +/// +/// Will return [`Error`] if the project doesn't exist or a surf interaction +/// fails. +pub fn blob

( + browser: &mut Browser, + maybe_revision: Option>, + path: &str, +) -> Result +where + P: ToString, +{ + make_blob(browser, maybe_revision, path, content) +} + +fn make_blob( + browser: &mut Browser, + maybe_revision: Option>, + path: &str, + content: C, +) -> Result +where + P: ToString, + C: FnOnce(&[u8]) -> BlobContent, +{ + let maybe_revision = maybe_revision.map(Rev::try_from).transpose()?; + if let Some(revision) = maybe_revision { + browser.rev(revision)?; + } + + let root = browser.get_directory()?; + let p = file_system::Path::from_str(path)?; + + let file = root + .find_file(p.clone()) + .ok_or_else(|| Error::PathNotFound(p.clone()))?; + + let mut commit_path = file_system::Path::root(); + commit_path.append(p.clone()); + + let last_commit = browser + .last_commit(commit_path)? + .map(|c| commit::Header::from(&c)); + let (_rest, last) = p.split_last(); + + let content = content(&file.contents); + + Ok(Blob { + content, + info: Info { + name: last.to_string(), + object_type: ObjectType::Blob, + last_commit, + }, + path: path.to_string(), + }) +} + +/// Return a [`BlobContent`] given a byte slice. +fn content(content: &[u8]) -> BlobContent { + match str::from_utf8(content) { + Ok(utf8) => BlobContent::Plain(utf8.to_owned()), + Err(_) => BlobContent::Binary(content.to_owned()), + } +} + +#[cfg(feature = "syntax")] +pub mod highlighting { + use super::*; + + /// Returns the [`Blob`] for a file at `revision` under `path`. + /// + /// # Errors + /// + /// Will return [`Error`] if the project doesn't exist or a surf interaction + /// fails. + pub fn blob

( + browser: &mut Browser, + maybe_revision: Option>, + path: &str, + theme: Option<&str>, + ) -> Result + where + P: ToString, + { + make_blob(browser, maybe_revision, path, |contents| { + content(path, contents, theme) + }) + } + + /// Return a [`BlobContent`] given a file path, content and theme. Attempts + /// to perform syntax highlighting when the theme is `Some`. + fn content(path: &str, content: &[u8], theme_name: Option<&str>) -> BlobContent { + let content = match str::from_utf8(content) { + Ok(content) => content, + Err(_) => return BlobContent::Binary(content.to_owned()), + }; + + match theme_name { + None => BlobContent::Plain(content.to_owned()), + Some(theme) => syntax::highlight(path, content, theme) + .map_or_else(|| BlobContent::Plain(content.to_owned()), BlobContent::Html), + } + } +} diff --git a/radicle-source/src/object/tree.rs b/radicle-source/src/object/tree.rs new file mode 100644 index 0000000..f052974 --- /dev/null +++ b/radicle-source/src/object/tree.rs @@ -0,0 +1,177 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::{convert::TryFrom as _, str::FromStr as _}; + +use serde::{ + ser::{SerializeStruct as _, Serializer}, + Serialize, +}; + +use radicle_surf::{ + file_system, + vcs::git::{Browser, Rev}, +}; + +use crate::{ + commit, + error::Error, + object::{Info, ObjectType}, + revision::Revision, +}; + +/// Result of a directory listing, carries other trees and blobs. +pub struct Tree { + /// Absolute path to the tree object from the repo root. + pub path: String, + /// Entries listed in that tree result. + pub entries: Vec, + /// Extra info for the tree object. + pub info: Info, +} + +impl Serialize for Tree { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Tree", 3)?; + state.serialize_field("path", &self.path)?; + state.serialize_field("entries", &self.entries)?; + state.serialize_field("info", &self.info)?; + state.end() + } +} + +// TODO(xla): Ensure correct by construction. +/// Entry in a Tree result. +pub struct TreeEntry { + /// Extra info for the entry. + pub info: Info, + /// Absolute path to the object from the root of the repo. + pub path: String, +} + +impl Serialize for TreeEntry { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Tree", 2)?; + state.serialize_field("path", &self.path)?; + state.serialize_field("info", &self.info)?; + state.end() + } +} + +/// Retrieve the [`Tree`] for the given `revision` and directory `prefix`. +/// +/// # Errors +/// +/// Will return [`Error`] if any of the surf interactions fail. +pub fn tree

( + browser: &mut Browser<'_>, + maybe_revision: Option>, + maybe_prefix: Option, +) -> Result +where + P: ToString, +{ + let maybe_revision = maybe_revision.map(Rev::try_from).transpose()?; + let prefix = maybe_prefix.unwrap_or_default(); + + if let Some(revision) = maybe_revision { + browser.rev(revision)?; + } + + let path = if prefix == "/" || prefix.is_empty() { + file_system::Path::root() + } else { + file_system::Path::from_str(&prefix)? + }; + + let root_dir = browser.get_directory()?; + let prefix_dir = if path.is_root() { + root_dir + } else { + root_dir + .find_directory(path.clone()) + .ok_or_else(|| Error::PathNotFound(path.clone()))? + }; + let mut prefix_contents = prefix_dir.list_directory(); + prefix_contents.sort(); + + let entries_results: Result, Error> = prefix_contents + .iter() + .map(|(label, system_type)| { + let entry_path = if path.is_root() { + file_system::Path::new(label.clone()) + } else { + let mut p = path.clone(); + p.push(label.clone()); + p + }; + let mut commit_path = file_system::Path::root(); + commit_path.append(entry_path.clone()); + + let info = Info { + name: label.to_string(), + object_type: match system_type { + file_system::SystemType::Directory => ObjectType::Tree, + file_system::SystemType::File => ObjectType::Blob, + }, + last_commit: None, + }; + + Ok(TreeEntry { + info, + path: entry_path.to_string(), + }) + }) + .collect(); + + let mut entries = entries_results?; + + // We want to ensure that in the response Tree entries come first. `Ord` being + // derived on the enum ensures Variant declaration order. + // + // https://doc.rust-lang.org/std/cmp/trait.Ord.html#derivable + entries.sort_by(|a, b| a.info.object_type.cmp(&b.info.object_type)); + + let last_commit = if path.is_root() { + Some(commit::Header::from(browser.get().first())) + } else { + None + }; + let name = if path.is_root() { + "".into() + } else { + let (_first, last) = path.split_last(); + last.to_string() + }; + let info = Info { + name, + object_type: ObjectType::Tree, + last_commit, + }; + + Ok(Tree { + path: prefix, + entries, + info, + }) +} diff --git a/radicle-source/src/oid.rs b/radicle-source/src/oid.rs new file mode 100644 index 0000000..e295a1a --- /dev/null +++ b/radicle-source/src/oid.rs @@ -0,0 +1,44 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::convert::TryFrom; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +#[serde(try_from = "&str", into = "String")] +pub struct Oid(pub git2::Oid); + +impl TryFrom<&str> for Oid { + type Error = git2::Error; + + fn try_from(value: &str) -> Result { + value.parse().map(Oid) + } +} + +impl From for String { + fn from(oid: Oid) -> Self { + oid.0.to_string() + } +} + +impl From for git2::Oid { + fn from(oid: Oid) -> Self { + oid.0 + } +} diff --git a/radicle-source/src/person.rs b/radicle-source/src/person.rs new file mode 100644 index 0000000..b4b886f --- /dev/null +++ b/radicle-source/src/person.rs @@ -0,0 +1,28 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use serde::Serialize; + +/// Representation of a person (e.g. committer, author, signer) from a +/// repository. Usually extracted from a signature. +#[derive(Clone, Debug, Serialize)] +pub struct Person { + /// Name part of the commit signature. + pub name: String, + /// Email part of the commit signature. + pub email: String, +} diff --git a/radicle-source/src/revision.rs b/radicle-source/src/revision.rs new file mode 100644 index 0000000..08abc08 --- /dev/null +++ b/radicle-source/src/revision.rs @@ -0,0 +1,175 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::convert::TryFrom; + +use nonempty::NonEmpty; +use serde::{Deserialize, Serialize}; + +use radicle_surf::vcs::git::{self, Browser, RefScope, Rev}; + +use crate::{ + branch::{branches, Branch}, + error::Error, + oid::Oid, + tag::{tags, Tag}, +}; + +pub enum Category { + Local { peer_id: P, user: U }, + Remote { peer_id: P, user: U }, +} + +/// A revision selector for a `Browser`. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "type")] +pub enum Revision

{ + /// Select a tag under the name provided. + #[serde(rename_all = "camelCase")] + Tag { + /// Name of the tag. + name: String, + }, + /// Select a branch under the name provided. + #[serde(rename_all = "camelCase")] + Branch { + /// Name of the branch. + name: String, + /// The remote peer, if specified. + peer_id: Option

, + }, + /// Select a SHA1 under the name provided. + #[serde(rename_all = "camelCase")] + Sha { + /// The SHA1 value. + sha: Oid, + }, +} + +impl

TryFrom> for Rev +where + P: ToString, +{ + type Error = Error; + + fn try_from(other: Revision

) -> Result { + match other { + Revision::Tag { name } => Ok(git::TagName::new(&name).into()), + Revision::Branch { name, peer_id } => Ok(match peer_id { + Some(peer) => { + git::Branch::remote(&format!("heads/{}", name), &peer.to_string()).into() + }, + None => git::Branch::local(&name).into(), + }), + Revision::Sha { sha } => { + let oid: git2::Oid = sha.into(); + Ok(oid.into()) + }, + } + } +} + +/// Bundled response to retrieve both [`Branch`]es and [`Tag`]s for a user's +/// repo. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Revisions { + /// The peer peer_id for the user. + pub peer_id: P, + /// The user who owns these revisions. + pub user: U, + /// List of [`git::Branch`]. + pub branches: NonEmpty, + /// List of [`git::Tag`]. + pub tags: Vec, +} + +/// Provide the [`Revisions`] for the given `peer_id`, looking for the +/// branches as [`RefScope::Remote`]. +/// +/// If there are no branches then this returns `None`. +/// +/// # Errors +/// +/// * If we cannot get the branches from the `Browser` +pub fn remote( + browser: &Browser, + peer_id: P, + user: U, +) -> Result>, Error> +where + P: Clone + ToString, +{ + let remote_branches = branches(browser, Some(peer_id.clone()).into())?; + Ok( + NonEmpty::from_vec(remote_branches).map(|branches| Revisions { + peer_id, + user, + branches, + // TODO(rudolfs): implement remote peer tags once we decide how + // https://radicle.community/t/git-tags/214 + tags: vec![], + }), + ) +} + +/// Provide the [`Revisions`] for the given `peer_id`, looking for the +/// branches as [`RefScope::Local`]. +/// +/// If there are no branches then this returns `None`. +/// +/// # Errors +/// +/// * If we cannot get the branches from the `Browser` +pub fn local(browser: &Browser, peer_id: P, user: U) -> Result>, Error> +where + P: Clone + ToString, +{ + let local_branches = branches(browser, RefScope::Local)?; + let tags = tags(browser)?; + Ok( + NonEmpty::from_vec(local_branches).map(|branches| Revisions { + peer_id, + user, + branches, + tags, + }), + ) +} + +/// Provide the [`Revisions`] of a peer. +/// +/// If the peer is [`Category::Local`], meaning that is the current person doing +/// the browsing and no remote is set for the reference. +/// +/// Othewise, the peer is [`Category::Remote`], meaning that we are looking into +/// a remote part of a reference. +/// +/// # Errors +/// +/// * If we cannot get the branches from the `Browser` +pub fn revisions( + browser: &Browser, + peer: Category, +) -> Result>, Error> +where + P: Clone + ToString, +{ + match peer { + Category::Local { peer_id, user } => local(browser, peer_id, user), + Category::Remote { peer_id, user } => remote(browser, peer_id, user), + } +} diff --git a/radicle-source/src/syntax.rs b/radicle-source/src/syntax.rs new file mode 100644 index 0000000..b572a35 --- /dev/null +++ b/radicle-source/src/syntax.rs @@ -0,0 +1,87 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::path; + +use syntect::{ + easy::HighlightLines, + highlighting::ThemeSet, + parsing::SyntaxSet, + util::LinesWithEndings, +}; + +lazy_static::lazy_static! { + // The syntax set is slow to load (~30ms), so we make sure to only load it once. + // It _will_ affect the latency of the first request that uses syntax highlighting, + // but this is acceptable for now. + pub static ref SYNTAX_SET: SyntaxSet = { + let default_set = SyntaxSet::load_defaults_newlines(); + let mut builder = default_set.into_builder(); + + if cfg!(debug_assertions) { + // In development assets are relative to the proxy source. + // Don't crash if we aren't able to load additional syntaxes for some reason. + builder.add_from_folder("./assets", true).ok(); + } else { + // In production assets are relative to the proxy executable. + let exe_path = std::env::current_exe().expect("Can't get current exe path"); + let root_path = exe_path + .parent() + .expect("Could not get parent path of current executable"); + let mut tmp = root_path.to_path_buf(); + tmp.push("assets"); + let asset_path = tmp.to_str().expect("Couldn't convert pathbuf to str"); + + // Don't crash if we aren't able to load additional syntaxes for some reason. + match builder.add_from_folder(asset_path, true) { + Ok(_) => (), + Err(err) => log::warn!("Syntax builder error : {}", err), + }; + } + builder.build() + }; +} + +/// Return a [`BlobContent`] given a file path, content and theme. Attempts to +/// perform syntax highlighting when the theme is `Some`. +pub fn highlight(path: &str, content: &str, theme_name: &str) -> Option { + let syntax = path::Path::new(path) + .extension() + .and_then(std::ffi::OsStr::to_str) + .and_then(|ext| SYNTAX_SET.find_syntax_by_extension(ext)); + + let ts = ThemeSet::load_defaults(); + let theme = ts.themes.get(theme_name); + + match (syntax, theme) { + (Some(syntax), Some(theme)) => { + let mut highlighter = HighlightLines::new(syntax, theme); + let mut html = String::with_capacity(content.len()); + + for line in LinesWithEndings::from(content) { + let regions = highlighter.highlight(line, &SYNTAX_SET); + syntect::html::append_highlighted_html_for_styled_line( + ®ions[..], + syntect::html::IncludeBackground::No, + &mut html, + ); + } + Some(html) + }, + _ => None, + } +} diff --git a/radicle-source/src/tag.rs b/radicle-source/src/tag.rs new file mode 100644 index 0000000..1cfed94 --- /dev/null +++ b/radicle-source/src/tag.rs @@ -0,0 +1,60 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::fmt; + +use serde::Serialize; + +use radicle_surf::{git::RefScope, vcs::git::Browser}; + +use crate::error::Error; + +/// Tag name representation. +/// +/// We still need full tag support. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Tag(pub(crate) String); + +impl From for Tag { + fn from(name: String) -> Self { + Self(name) + } +} + +impl fmt::Display for Tag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Retrieves the list of [`Tag`] for the given project `id`. +/// +/// # Errors +/// +/// Will return [`Error`] if the project doesn't exist or the surf interaction +/// fails. +pub fn tags(browser: &Browser<'_>) -> Result, Error> { + let tag_names = browser.list_tags(RefScope::Local)?; + let mut tags: Vec = tag_names + .into_iter() + .map(|tag_name| Tag(tag_name.name().to_string())) + .collect(); + + tags.sort(); + + Ok(tags) +} diff --git a/radicle-surf/Cargo.toml b/radicle-surf/Cargo.toml new file mode 100644 index 0000000..9c48ec7 --- /dev/null +++ b/radicle-surf/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "radicle-surf" +description = "A code surfing library for VCS file systems" +readme = "README.md" +version = "0.8.0" +authors = ["The Radicle Team "] +edition = "2018" +homepage = "https://github.com/radicle-dev/radicle-surf" +repository = "https://github.com/radicle-dev/radicle-surf" +license = "GPL-3.0-or-later" + +include = [ + "**/*.rs", + "Cargo.toml", + "data/git-platinum.tgz", +] + +[features] +serialize = ["serde"] +# NOTE: testing `test_submodule_failure` on GH actions +# is painful since it uses this specific repo and expects +# certain branches to be setup. So we use this feature flag +# to ignore the test on CI. +gh-actions = [] + +[dependencies] +either = "1.5" +nom = "6" +nonempty = "0.5" +regex = ">= 1.5.5" +serde = { features = ["serde_derive"], optional = true, version = "1" } +thiserror = "1.0" + +[dependencies.git2] +version = ">= 0.12" +default-features = false +features = [] + +[dev-dependencies] +pretty_assertions = "1.3.0" +proptest = "0.9" +criterion = "0.3" +serde_json = "1" + +[build-dependencies] +anyhow = "1.0" +flate2 = "1" +tar = "0.4" + +[[bench]] +name = "last_commit" +harness = false diff --git a/radicle-surf/DEVELOPMENT.md b/radicle-surf/DEVELOPMENT.md new file mode 100644 index 0000000..fff78fc --- /dev/null +++ b/radicle-surf/DEVELOPMENT.md @@ -0,0 +1,83 @@ + +# Radicle Surfing πŸ„ + +Thanks for wanting to contribute to `radicle-surf`! + +# Licensing + +We are [GPL-3.0-or-later](./LICENSE) licensed project. To keep in compliance with this we must +add a [license header](./.license-header) to any new files added. This is checked on each run of CI. + +## Building & Testing πŸ—οΈ + +We try to make development as seemless as possible so we can get down to the real work. We supply +the toolchain via the `rust-toolchain` file, and the formatting rules `.rustmt.toml` file. + +For the [Nix](https://nixos.org/) inclined there is a `default.nix` file to get all the necessary +dependencies and it also uses the `rust-toolchain` file to pin to that version of Rust. + +You can build the project the usual way: +``` +cargo build +``` + +To run all the tests: +``` +cargo test +``` + +For the full list of checks that get executed in CI you can checkout the [ci/run](./ci/run) script. + +If any of this _isn't_ working, then let's work through it together and get it Working on Your +Machineβ„’. + +## Structure πŸ›οΈ + +The design of `radicle-surf` is to have an in-memory representation of a project's directory which +can be generated by a VCS's backend. The directory system is modeled under `file_system`, the VCS +functionality is naturally under `vcs`, and `diff` logic is held under `diff`. + +``` +src/ +β”œβ”€β”€ diff +β”œβ”€β”€ file_system +└── vcs +``` + +## Testing & Documentation πŸ“š + +We ensure that the crate is well documented. `cargo clippy` will argue with you anytime a public +facing piece of the library is undocumented. We should always provide an explanation of what +something is or does, and also provide examples to allow our users to get up and running as quick +and easy as possible. + +When writing documentation we should try provide one or two examples (if they make sense). This +provides us with some simple unit tests as well as something our users can copy and paste for ease +of development. + +If more tests are needed then we should add them under `mod tests` in the relevant module. We strive +to find properties of our programs so that we can use tools like `proptest` to extensively prove our +programs are correct. As well as this, we add unit tests to esnure the examples in our heads are +correct, and testing out the ergonomics of our API first-hand. + +## CI files πŸ€– + +Our CI infrastructure runs on Buildkite. The build process is run for every commit which is pushed +to GitHub. + +All relevant configuration can be found here: + +``` +radicle-surf/.buildkite/ +β”œβ”€β”€ docker +β”‚Β Β  β”œβ”€β”€ build +β”‚Β Β  β”‚Β Β  └── Dockerfile +β”‚Β Β  └── rust-nightly +β”‚Β Β  └── Dockerfile +└── pipeline.yaml +``` + +## Releases πŸ“… + +TODO: Once we get the API into a good shape we will keep track of releases via a `CHANGELOG.md` and +tag the releases via `git tag`. diff --git a/radicle-surf/README.md b/radicle-surf/README.md new file mode 100644 index 0000000..76aa7d8 --- /dev/null +++ b/radicle-surf/README.md @@ -0,0 +1,74 @@ +# radicle-surf + +A code surfing library for VCS file systems πŸ„β€β™€οΈπŸ„β€β™‚οΈ + +Welcome to `radicle-surf`! + +`radicle-surf` is a system to describe a file-system in a VCS world. +We have the concept of files and directories, but these objects can change over time while people iterate on them. +Thus, it is a file-system within history and we, the user, are viewing the file-system at a particular snapshot. +Alongside this, we will wish to take two snapshots and view their differences. + +## Contributing + +To get started on contributing you can check out our [developing guide](../DEVELOPMENT.md), and also +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. + +```rust +use radicle_surf::vcs::git; +use radicle_surf::file_system::{Label, Path, SystemType}; +use radicle_surf::file_system::unsound; +use pretty_assertions::assert_eq; +use std::str::FromStr; + +// We're going to point to this repo. +let repo = git::Repository::new("./data/git-platinum")?; + +// Here we initialise a new Broswer for a the git repo. +let mut browser = git::Browser::new(&repo, "master")?; + +// Set the history to a particular commit +let commit = git::Oid::from_str("80ded66281a4de2889cc07293a8f10947c6d57fe")?; +browser.commit(commit)?; + +// Get the snapshot of the directory for our current HEAD of history. +let directory = browser.get_directory()?; + +// 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")), +]); +``` diff --git a/radicle-surf/benches/last_commit.rs b/radicle-surf/benches/last_commit.rs new file mode 100644 index 0000000..2655a51 --- /dev/null +++ b/radicle-surf/benches/last_commit.rs @@ -0,0 +1,45 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use radicle_surf::{ + file_system::{unsound, Path}, + vcs::git::{Branch, Browser, Repository}, +}; + +fn last_commit_comparison(c: &mut Criterion) { + let repo = Repository::new("./data/git-platinum") + .expect("Could not retrieve ./data/git-platinum as git repository"); + let browser = + Browser::new(&repo, Branch::local("master")).expect("Could not initialise Browser"); + + let mut group = c.benchmark_group("Last Commit"); + for path in [ + Path::root(), + unsound::path::new("~/src/memory.rs"), + unsound::path::new("~/this/is/a/really/deeply/nested/directory/tree"), + ] + .iter() + { + group.bench_with_input(BenchmarkId::new("", path), path, |b, path| { + b.iter(|| browser.last_commit(path.clone())) + }); + } +} + +criterion_group!(benches, last_commit_comparison); +criterion_main!(benches); diff --git a/radicle-surf/build.rs b/radicle-surf/build.rs new file mode 100644 index 0000000..8d1711d --- /dev/null +++ b/radicle-surf/build.rs @@ -0,0 +1,81 @@ +// This file is part of radicle-surf +// +// +// Copyright (C) 2019-2020 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 +// later as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::{ + env, + fs, + fs::File, + io, + path::{Path, PathBuf}, +}; + +use anyhow::Context as _; +use flate2::read::GzDecoder; +use tar::Archive; + +enum Command { + Build(PathBuf), + Publish(PathBuf), +} + +impl Command { + fn new() -> io::Result { + let current = env::current_dir()?; + Ok(if current.ends_with("radicle-surf") { + Self::Build(current) + } else { + Self::Publish(PathBuf::from( + env::var("OUT_DIR").map_err(|err| io::Error::new(io::ErrorKind::Other, err))?, + )) + }) + } + + fn target(&self) -> PathBuf { + match self { + Self::Build(path) => path.join("data"), + Self::Publish(path) => path.join("data"), + } + } +} + +fn main() { + let target = Command::new() + .expect("could not determine the cargo command") + .target(); + let git_platinum_tarball = "./data/git-platinum.tgz"; + + unpack(git_platinum_tarball, target).expect("Failed to unpack git-platinum"); + + println!("cargo:rerun-if-changed={}", git_platinum_tarball); +} + +fn unpack(archive_path: impl AsRef, target: impl AsRef) -> anyhow::Result<()> { + let content = target.as_ref().join("git-platinum"); + if content.exists() { + fs::remove_dir_all(content).context("attempting to remove git-platinum")?; + } + let archive_path = archive_path.as_ref(); + let tar_gz = File::open(archive_path).context(format!( + "attempting to open file: {}", + archive_path.display() + ))?; + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + archive.unpack(target).context("attempting to unpack")?; + + Ok(()) +} diff --git a/radicle-surf/data/README.md b/radicle-surf/data/README.md new file mode 100644 index 0000000..45cb638 --- /dev/null +++ b/radicle-surf/data/README.md @@ -0,0 +1,21 @@ +# Updating [git-platinum][] + +1. Push your changes to [`radicle-dev/git-platinum`][git-platinum] and/or update + `surf/data/mock-branches.txt`. +2. Run `scripts/update-git-platinum.sh` from the repo root. This updates + `surf/data/git-platinum.tgz`. +3. Run `cargo build` to unpack the updated repo. +4. Run the tests +5. Commit your changes. We provide a template below so that we can easily + identify changes to `git-platinum`. Please fill in the details that follow a + comment (`#`): + ``` + data/git-platinum: # short reason for updating + + # provide a longer reason for making changes to git-platinum + # as well as what has changed. + ``` + + + +[git-platinum]: https://github.com/radicle-dev/git-platinum diff --git a/radicle-surf/data/git-platinum.tgz b/radicle-surf/data/git-platinum.tgz new file mode 100644 index 0000000000000000000000000000000000000000..50cf9f2b101785b6e357c2ce22553baf905cf10f GIT binary patch literal 95889 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