mirror of
https://seed.radicle.dev/rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt.git
synced 2026-11-16 00:10:35 +08:00
merge radicle-source into radicle-surf
Initially, radicle-source was envisioned to be another layer on top of radicle-surf, however, it ended up duplicating a lot of concepts. We consolidate them by merging radicle-source into radicle-surf. This meant removing modules branch.rs, tag.rs, oid.rs and error.rs.
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
// This file is part of radicle-surf
|
||||
// <https://github.com/radicle-dev/radicle-surf>
|
||||
//
|
||||
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Represents a commit.
|
||||
|
||||
use std::convert::TryFrom as _;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{
|
||||
ser::{SerializeStruct as _, Serializer},
|
||||
Serialize,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
diff,
|
||||
file_system,
|
||||
person::Person,
|
||||
revision::Revision,
|
||||
vcs::git::{self, BranchName, Browser, Rev},
|
||||
};
|
||||
|
||||
/// Commit statistics.
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[derive(Clone)]
|
||||
pub struct Stats {
|
||||
/// Additions.
|
||||
pub additions: u64,
|
||||
/// Deletions.
|
||||
pub deletions: u64,
|
||||
}
|
||||
|
||||
/// Representation of a changeset between two revs.
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[derive(Clone)]
|
||||
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<BranchName>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for Header {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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.
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
pub struct Commits {
|
||||
/// The commit headers
|
||||
pub headers: Vec<Header>,
|
||||
/// The statistics for the commit headers
|
||||
pub stats: 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<Commit, Error> {
|
||||
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(|b| b.name)
|
||||
.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<Header, Error> {
|
||||
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<P>(
|
||||
browser: &mut Browser<'_>,
|
||||
maybe_revision: Option<Revision<P>>,
|
||||
) -> Result<Commits, Error>
|
||||
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 })
|
||||
}
|
||||
|
||||
/// An error reported by commit API.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// An error occurred during a file system operation.
|
||||
#[error(transparent)]
|
||||
FileSystem(#[from] file_system::Error),
|
||||
|
||||
/// An error occurred during a git operation.
|
||||
#[error(transparent)]
|
||||
Git(#[from] git::error::Error),
|
||||
|
||||
/// Trying to find a file path which could not be found.
|
||||
#[error("the path '{0}' was not found")]
|
||||
PathNotFound(file_system::Path),
|
||||
}
|
||||
Reference in New Issue
Block a user