mirror of
https://seed.radicle.dev/rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt.git
synced 2026-10-27 07:10:58 +08:00
- A new design document. - Uses examples/diff.rs to test out some ideas. - Removes the type Browser. - Removes the type Snapshot.
168 lines
4.9 KiB
Rust
168 lines
4.9 KiB
Rust
// 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/>.
|
|
|
|
//! A model of a general VCS. The components consist of a [`History`]
|
|
//! and a [`Vcs`] trait.
|
|
|
|
// use crate::file_system::directory::Directory;
|
|
use nonempty::NonEmpty;
|
|
|
|
pub mod git;
|
|
|
|
/// A non-empty bag of artifacts which are used to
|
|
/// derive a [`crate::file_system::Directory`] view. Examples of artifacts
|
|
/// would be commits in Git or patches in Pijul.
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct History<A>(pub NonEmpty<A>);
|
|
|
|
impl<A> History<A> {
|
|
/// Create a new `History` consisting of one artifact.
|
|
pub fn new(a: A) -> Self {
|
|
History(NonEmpty::new(a))
|
|
}
|
|
|
|
/// Push an artifact to the end of the `History`.
|
|
pub fn push(&mut self, a: A) {
|
|
self.0.push(a)
|
|
}
|
|
|
|
/// Iterator over the artifacts.
|
|
pub fn iter(&self) -> impl Iterator<Item = &A> {
|
|
self.0.iter()
|
|
}
|
|
|
|
/// Get the firest artifact in the `History`.
|
|
pub fn first(&self) -> &A {
|
|
self.0.first()
|
|
}
|
|
|
|
/// Get the length of `History` (aka the artefacts count)
|
|
pub fn len(&self) -> usize {
|
|
self.0.len()
|
|
}
|
|
|
|
/// Check if `History` is empty
|
|
pub fn is_empty(&self) -> bool {
|
|
self.0.is_empty()
|
|
}
|
|
|
|
/// Given that the `History` is topological order from most
|
|
/// recent artifact to least recent, `find_suffix` gets returns
|
|
/// the history up until the point of the given artifact.
|
|
///
|
|
/// This operation may fail if the artifact does not exist in
|
|
/// the given `History`.
|
|
pub fn find_suffix(&self, artifact: &A) -> Option<Self>
|
|
where
|
|
A: Clone + PartialEq,
|
|
{
|
|
let new_history: Option<NonEmpty<A>> = NonEmpty::from_slice(
|
|
&self
|
|
.iter()
|
|
.cloned()
|
|
.skip_while(|current| *current != *artifact)
|
|
.collect::<Vec<_>>(),
|
|
);
|
|
|
|
new_history.map(History)
|
|
}
|
|
|
|
/// Apply a function from `A` to `B` over the `History`
|
|
pub fn map<F, B>(self, f: F) -> History<B>
|
|
where
|
|
F: FnMut(A) -> B,
|
|
{
|
|
History(self.0.map(f))
|
|
}
|
|
|
|
/// Find an artifact in the `History`.
|
|
///
|
|
/// The function provided should return `Some` if the item is the desired
|
|
/// output and `None` otherwise.
|
|
pub fn find<F, B>(&self, f: F) -> Option<B>
|
|
where
|
|
F: Fn(&A) -> Option<B>,
|
|
{
|
|
self.iter().find_map(f)
|
|
}
|
|
|
|
/// Find an atrifact in the given `History` using the artifacts ID.
|
|
///
|
|
/// This operation may fail if the artifact does not exist in the history.
|
|
pub fn find_in_history<Identifier, F>(&self, identifier: &Identifier, id_of: F) -> Option<A>
|
|
where
|
|
A: Clone,
|
|
F: Fn(&A) -> Identifier,
|
|
Identifier: PartialEq,
|
|
{
|
|
self.iter()
|
|
.find(|artifact| {
|
|
let current_id = id_of(artifact);
|
|
*identifier == current_id
|
|
})
|
|
.cloned()
|
|
}
|
|
|
|
/// Find all occurences of an artifact in a bag of `History`s.
|
|
pub fn find_in_histories<Identifier, F>(
|
|
histories: Vec<Self>,
|
|
identifier: &Identifier,
|
|
id_of: F,
|
|
) -> Vec<Self>
|
|
where
|
|
A: Clone,
|
|
F: Fn(&A) -> Identifier + Copy,
|
|
Identifier: PartialEq,
|
|
{
|
|
histories
|
|
.into_iter()
|
|
.filter(|history| history.find_in_history(identifier, id_of).is_some())
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
pub(crate) trait GetVcs<Error>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
/// The way to identify a Repository.
|
|
type RepoId;
|
|
|
|
/// Find a Repository
|
|
fn get_repo(identifier: Self::RepoId) -> Result<Self, Error>;
|
|
}
|
|
|
|
/// The `VCS` trait encapsulates the minimal amount of information for
|
|
/// interacting with some notion of `History` from a given
|
|
/// Version-Control-System.
|
|
pub trait Vcs<A, Error> {
|
|
/// The way to identify a History.
|
|
type HistoryId;
|
|
|
|
/// The way to identify an artefact.
|
|
type ArtefactId;
|
|
|
|
/// Find a History in a Repo given a way to identify it
|
|
fn get_history(&self, identifier: Self::HistoryId) -> Result<History<A>, Error>;
|
|
|
|
/// Find all histories in a Repo
|
|
fn get_histories(&self) -> Result<Vec<History<A>>, Error>;
|
|
|
|
/// Identify artefacts of a Repository
|
|
fn get_identifier(artefact: &A) -> Self::ArtefactId;
|
|
}
|