Remove `Project` type

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-09-22 15:59:20 +02:00
parent c2b599f068
commit e382bc4eb9
No known key found for this signature in database
11 changed files with 101 additions and 142 deletions

View File

@ -21,9 +21,10 @@ use crate::address_manager::AddressManager;
use crate::clock::RefClock;
use crate::collections::{HashMap, HashSet};
use crate::crypto;
use crate::crypto::Verified;
use crate::git;
use crate::git::Url;
use crate::identity::{Id, Project};
use crate::identity::{Doc, Id};
use crate::service::config::ProjectTracking;
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
use crate::service::peer::{Peer, PeerError, PeerState};
@ -255,7 +256,7 @@ impl<'r, T: WriteStorage<'r>, S: address_book::Store, G: crypto::Signer> Service
}
/// Get a project from storage, using the local node's key.
pub fn get(&self, proj: &Id) -> Result<Option<Project>, storage::Error> {
pub fn get(&self, proj: &Id) -> Result<Option<Doc<Verified>>, storage::Error> {
self.storage.get(&self.node_id(), proj)
}
@ -645,7 +646,7 @@ impl<S, T, G> Iterator for Service<S, T, G> {
#[derive(Debug)]
pub struct Lookup {
/// Whether the project was found locally or not.
pub local: Option<Project>,
pub local: Option<Doc<Verified>>,
/// A list of remote peers on which the project is known to exist.
pub remote: Vec<NodeId>,
}

View File

@ -6,6 +6,7 @@ use log::*;
use crate::address_book::{KnownAddress, Source};
use crate::clock::RefClock;
use crate::collections::HashMap;
use crate::git;
use crate::git::Url;
use crate::service;
use crate::service::config::*;
@ -63,10 +64,17 @@ where
S: WriteStorage<'r> + 'static,
{
pub fn new(name: &'static str, ip: impl Into<net::IpAddr>, storage: S) -> Self {
let git_url = Url {
scheme: git::url::Scheme::File,
path: storage.path().to_string_lossy().to_string().into(),
..git::Url::default()
};
Self::config(
name,
Config {
git_url: storage.url(),
git_url,
..Config::default()
},
ip,

View File

@ -473,9 +473,9 @@ fn prop_inventory_exchange_dense() {
(bob_inv.inventory, bob.node_id()),
(eve_inv.inventory, eve.node_id()),
] {
for proj in inv {
for id in inv.keys() {
routing
.entry(proj.id.clone())
.entry(id.clone())
.or_insert_with(|| HashSet::with_hasher(rng.clone().into()))
.insert(*peer);
}

View File

@ -187,17 +187,11 @@ pub fn configure_remote<'r>(
repo: &'r git2::Repository,
remote_name: &str,
remote_id: &RemoteId,
remote_path: &Path,
remote_url: &Url,
) -> Result<git2::Remote<'r>, git2::Error> {
let url = Url {
scheme: git_url::Scheme::File,
path: remote_path.to_string_lossy().to_string().into(),
..Url::default()
};
let fetch = format!("+refs/remotes/{remote_id}/heads/*:refs/remotes/rad/*");
let push = format!("refs/heads/*:refs/remotes/{remote_id}/heads/*");
let remote = repo.remote_with_fetch(remote_name, url.to_string().as_str(), &fetch)?;
let remote = repo.remote_with_fetch(remote_name, remote_url.to_string().as_str(), &fetch)?;
repo.remote_add_push(remote_name, &push)?;
Ok(remote)

View File

@ -1,17 +1,14 @@
pub mod doc;
use std::ops::Deref;
use std::path::PathBuf;
use std::{ffi::OsString, fmt, str::FromStr};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::crypto;
use crate::crypto::Verified;
use crate::git;
use crate::serde_ext;
use crate::storage::Remotes;
pub use crypto::PublicKey;
pub use doc::{Delegate, Doc};
@ -175,36 +172,6 @@ impl Deref for Did {
}
}
/// A stored and verified project.
#[derive(Debug, Clone)]
pub struct Project {
/// The project identifier.
pub id: Id,
/// The latest project identity document.
pub doc: Doc<Verified>,
/// The project remotes.
pub remotes: Remotes<Verified>,
/// On-disk file path for this project's repository.
pub path: PathBuf,
}
impl Project {
pub fn delegate(&mut self, name: String, key: crypto::PublicKey) -> bool {
self.doc.delegate(Delegate {
name,
id: Did::from(key),
})
}
}
impl Deref for Project {
type Target = Doc<Verified>;
fn deref(&self) -> &Self::Target {
&self.doc
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -102,7 +102,12 @@ impl Doc<Verified> {
}
/// Attempt to add a new delegate to the document. Returns `true` if it wasn't there before.
pub fn delegate(&mut self, delegate: Delegate) -> bool {
pub fn delegate(&mut self, name: String, key: crypto::PublicKey) -> bool {
let delegate = Delegate {
name,
id: Did::from(key),
};
if self.delegates.iter().all(|d| d.id != delegate.id) {
self.delegates.push(delegate);
return true;
@ -510,7 +515,7 @@ mod test {
let repo = storage.repository(&id).unwrap();
// Make a change to the description and sign it.
proj.doc.payload.description += "!";
proj.payload.description += "!";
proj.sign(&alice)
.and_then(|(_, sig)| {
proj.update(
@ -524,7 +529,7 @@ mod test {
// Add Bob as a delegate, and sign it.
proj.delegate("bob".to_owned(), *bob.public_key());
proj.doc.threshold = 2;
proj.threshold = 2;
proj.sign(&alice)
.and_then(|(_, sig)| {
proj.update(
@ -552,7 +557,7 @@ mod test {
.unwrap();
// Update description again with signatures by Eve and Bob.
proj.doc.payload.description += "?";
proj.payload.description += "?";
let (current, head) = proj
.sign(&bob)
.and_then(|(_, bob_sig)| {
@ -579,7 +584,7 @@ mod test {
assert_eq!(identity.root, id);
assert_eq!(identity.current, current);
assert_eq!(identity.head, head);
assert_eq!(identity.doc, proj.doc);
assert_eq!(identity.doc, proj);
let proj = storage.get(alice.public_key(), &id).unwrap().unwrap();
assert_eq!(proj.description, "Acme's repository!?");

View File

@ -58,6 +58,7 @@ pub fn init<'r, G: Signer, S: storage::WriteStorage<'r>>(
.verified()?;
let (id, _, project) = doc.create(pk, "Initialize Radicle", storage)?;
let url = storage.url(&id);
git::set_upstream(
repo,
@ -69,7 +70,7 @@ pub fn init<'r, G: Signer, S: storage::WriteStorage<'r>>(
// TODO: Note that you'll likely want to use `RemoteCallbacks` and set
// `push_update_reference` to test whether all the references were pushed
// successfully.
git::configure_remote(repo, REMOTE_NAME, pk, project.path())?.push::<&str>(
git::configure_remote(repo, REMOTE_NAME, pk, &url)?.push::<&str>(
&[&format!(
"{}:{}",
&git::refs::workdir::branch(&default_branch),
@ -120,14 +121,11 @@ pub fn fork_remote<'r, G: Signer, S: storage::WriteStorage<'r>>(
let raw = repository.raw();
let remote_head = raw
.find_reference(&git::refs::storage::branch(
remote,
&project.doc.default_branch,
))?
.find_reference(&git::refs::storage::branch(remote, &project.default_branch))?
.target()
.ok_or(ForkError::InvalidReference)?;
raw.reference(
&git::refs::storage::branch(me, &project.doc.default_branch),
&git::refs::storage::branch(me, &project.default_branch),
remote_head,
false,
&format!("creating default branch for {me}"),
@ -274,17 +272,14 @@ pub fn checkout<P: AsRef<Path>, S: storage::ReadStorage>(
.ok_or_else(|| CheckoutError::NotFound(proj.clone()))?;
let mut opts = git2::RepositoryInitOptions::new();
opts.no_reinit(true).description(&project.doc.description);
opts.no_reinit(true).description(&project.description);
let repo = git2::Repository::init_opts(path, &opts)?;
let default_branch = project.doc.default_branch.as_str();
let default_branch = project.default_branch.as_str();
let url = storage.url(proj);
// Configure and fetch all refs from remote.
git::configure_remote(&repo, REMOTE_NAME, remote, &project.path)?.fetch::<&str>(
&[],
None,
None,
)?;
git::configure_remote(&repo, REMOTE_NAME, remote, &url)?.fetch::<&str>(&[], None, None)?;
{
// Setup default branch.
@ -306,6 +301,8 @@ pub fn checkout<P: AsRef<Path>, S: storage::ReadStorage>(
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
use crate::git::fmt::refname;
use crate::identity::{Delegate, Did};
@ -332,14 +329,20 @@ mod tests {
.unwrap();
let project = storage.get(&public_key, &proj).unwrap().unwrap();
let remotes: HashMap<_, _> = storage
.repository(&proj)
.unwrap()
.remotes()
.unwrap()
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(project.remotes[&public_key].refs, refs);
assert_eq!(project.id, proj);
assert_eq!(project.doc.name, "acme");
assert_eq!(project.doc.description, "Acme's repo");
assert_eq!(project.doc.default_branch, BranchName::from("master"));
assert_eq!(remotes[&public_key].refs, refs);
assert_eq!(project.name, "acme");
assert_eq!(project.description, "Acme's repo");
assert_eq!(project.default_branch, BranchName::from("master"));
assert_eq!(
project.doc.delegates.first(),
project.delegates.first(),
&Delegate {
name: String::from("anonymous"),
id: Did::from(public_key),

View File

@ -18,7 +18,7 @@ use crate::git::ext as git_ext;
use crate::git::Url;
use crate::git::{RefError, RefStr, RefString};
use crate::identity;
use crate::identity::{Id, IdError, Project};
use crate::identity::{Id, IdError};
use crate::storage::git::IdentityError;
use crate::storage::refs::Refs;
@ -216,8 +216,9 @@ impl Remote<Verified> {
}
pub trait ReadStorage {
fn url(&self) -> Url;
fn get(&self, remote: &RemoteId, proj: &Id) -> Result<Option<Project>, Error>;
fn path(&self) -> &Path;
fn url(&self, proj: &Id) -> Url;
fn get(&self, remote: &RemoteId, proj: &Id) -> Result<Option<identity::Doc<Verified>>, Error>;
fn inventory(&self) -> Result<Inventory, Error>;
}
@ -254,7 +255,7 @@ pub trait ReadRepository<'r> {
fn remote(&self, remote: &RemoteId) -> Result<Remote<Verified>, refs::Error>;
fn remotes(&'r self) -> Result<Self::Remotes, git2::Error>;
/// Return the project associated with this repository.
fn project(&self) -> Result<Project, Error>;
fn project(&self) -> Result<identity::Doc<Verified>, Error>;
fn project_identity(&self) -> Result<(Oid, identity::Doc<Unverified>), IdentityError>;
}
@ -268,15 +269,19 @@ where
T: Deref<Target = S>,
S: ReadStorage + 'static,
{
fn url(&self) -> Url {
self.deref().url()
fn path(&self) -> &Path {
self.deref().path()
}
fn url(&self, proj: &Id) -> Url {
self.deref().url(proj)
}
fn inventory(&self) -> Result<Inventory, Error> {
self.deref().inventory()
}
fn get(&self, remote: &RemoteId, proj: &Id) -> Result<Option<Project>, Error> {
fn get(&self, remote: &RemoteId, proj: &Id) -> Result<Option<identity::Doc<Verified>>, Error> {
self.deref().get(remote, proj)
}
}

View File

@ -8,7 +8,7 @@ use once_cell::sync::Lazy;
use crate::crypto::{Signer, Unverified, Verified};
use crate::git;
use crate::identity;
use crate::identity::{Doc, Id, Project};
use crate::identity::{Doc, Id};
use crate::storage::refs;
use crate::storage::refs::{Refs, SignedRefs};
use crate::storage::{
@ -50,37 +50,27 @@ impl fmt::Debug for Storage {
}
impl ReadStorage for Storage {
fn url(&self) -> Url {
fn path(&self) -> &Path {
self.path.as_path()
}
fn url(&self, proj: &Id) -> Url {
let path = self.path().join(proj.to_string());
Url {
scheme: git_url::Scheme::File,
host: None,
path: self.path.to_string_lossy().to_string().into(),
..Url::default()
path: path.to_string_lossy().to_string().into(),
..git::Url::default()
}
}
fn get(&self, remote: &RemoteId, proj: &Id) -> Result<Option<Project>, Error> {
fn get(&self, remote: &RemoteId, proj: &Id) -> Result<Option<Doc<Verified>>, Error> {
// TODO: Don't create a repo here if it doesn't exist?
// Perhaps for checking we could have a `contains` method?
let repo = self.repository(proj)?;
if let Some(doc) = repo.identity_of(remote)? {
let remotes = repo.remotes()?.collect::<Result<_, _>>()?;
let path = repo.path().to_path_buf();
// TODO: We should check that there is at least one remote, which is
// the one of the local user, otherwise it means the project is in
// an corrupted state.
Ok(Some(Project {
id: proj.clone(),
doc,
remotes,
path,
}))
} else {
Ok(None)
}
self.repository(proj)?
.identity_of(remote)
.map_err(Error::from)
}
fn inventory(&self) -> Result<Inventory, Error> {
@ -445,7 +435,7 @@ impl<'r> ReadRepository<'r> for Repository {
Ok(Box::new(iter))
}
fn project(&self) -> Result<Project, Error> {
fn project(&self) -> Result<Doc<Verified>, Error> {
todo!()
}

View File

@ -2,7 +2,6 @@ use std::collections::{BTreeMap, HashSet};
use std::hash::Hash;
use std::iter;
use std::ops::RangeBounds;
use std::path::PathBuf;
use nonempty::NonEmpty;
use quickcheck::Arbitrary;
@ -12,7 +11,7 @@ use crate::crypto;
use crate::crypto::{KeyPair, PublicKey, Seed, Signer, Unverified, Verified};
use crate::git;
use crate::hash;
use crate::identity::{doc::Delegate, doc::Doc, Did, Id, Project};
use crate::identity::{doc::Delegate, doc::Doc, Did, Id};
use crate::storage;
use crate::storage::refs::{Refs, SignedRefs};
use crate::test::signer::MockSigner;
@ -67,23 +66,6 @@ impl Arbitrary for storage::Remotes<crypto::Verified> {
}
}
impl Arbitrary for Project {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let doc = Doc::<Verified>::arbitrary(g);
let (oid, _) = doc.encode().unwrap();
let id = Id::from(oid);
let remotes = storage::Remotes::arbitrary(g);
let path = PathBuf::arbitrary(g);
Self {
id,
doc,
remotes,
path,
}
}
}
impl Arbitrary for Did {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Self::from(PublicKey::arbitrary(g))

View File

@ -1,29 +1,42 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use git_url::Url;
use crate::crypto::{Signer, Verified};
use crate::identity::{Id, Project};
use crate::identity::doc::Doc;
use crate::identity::Id;
pub use crate::storage::*;
#[derive(Clone, Debug)]
pub struct MockStorage {
pub inventory: Vec<Project>,
pub path: PathBuf,
pub inventory: HashMap<Id, Doc<Verified>>,
}
impl MockStorage {
pub fn new(inventory: Vec<Project>) -> Self {
Self { inventory }
pub fn new(inventory: Vec<(Id, Doc<Verified>)>) -> Self {
Self {
path: PathBuf::default(),
inventory: inventory.into_iter().collect(),
}
}
pub fn empty() -> Self {
Self {
inventory: Vec::new(),
path: PathBuf::default(),
inventory: HashMap::new(),
}
}
}
impl ReadStorage for MockStorage {
fn url(&self) -> Url {
fn path(&self) -> &Path {
self.path.as_path()
}
fn url(&self, _proj: &Id) -> Url {
Url {
scheme: git_url::Scheme::Radicle,
host: Some("mock".to_string()),
@ -31,21 +44,12 @@ impl ReadStorage for MockStorage {
}
}
fn get(&self, _remote: &RemoteId, proj: &Id) -> Result<Option<Project>, Error> {
if let Some(proj) = self.inventory.iter().find(|p| p.id == *proj) {
return Ok(Some(proj.clone()));
}
Ok(None)
fn get(&self, _remote: &RemoteId, proj: &Id) -> Result<Option<Doc<Verified>>, Error> {
Ok(self.inventory.get(proj).cloned())
}
fn inventory(&self) -> Result<Inventory, Error> {
let inventory = self
.inventory
.iter()
.map(|proj| proj.id.clone())
.collect::<Vec<_>>();
Ok(inventory)
Ok(self.inventory.keys().cloned().collect::<Vec<_>>())
}
}
@ -122,7 +126,7 @@ impl ReadRepository<'_> for MockRepository {
todo!()
}
fn project(&self) -> Result<Project, Error> {
fn project(&self) -> Result<Doc<Verified>, Error> {
todo!()
}