cli: Implement tracking correctly
The tracking command was tracking a project when it should have been tracking peers. We also add the ability to pass an alias when tracking from the CLI. Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
46951b1b42
commit
a100f1c683
|
|
@ -84,7 +84,7 @@ pub fn clone(id: Id, _interactive: Interactive, ctx: impl term::Context) -> anyh
|
|||
let signer = term::signer(&profile)?;
|
||||
|
||||
// Track & fetch project.
|
||||
node.track(&id).context("track")?;
|
||||
node.track_repo(&id).context("track")?;
|
||||
node.fetch(&id).context("fetch")?;
|
||||
|
||||
// Create a local fork of the project, under our own id.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::ffi::OsString;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context as _};
|
||||
|
||||
use radicle::identity::project::Id;
|
||||
use radicle::node::Handle;
|
||||
use radicle::prelude::*;
|
||||
use radicle::storage::WriteStorage;
|
||||
|
|
@ -12,15 +12,16 @@ use crate::terminal::args::{Args, Error, Help};
|
|||
|
||||
pub const HELP: Help = Help {
|
||||
name: "track",
|
||||
description: "Manage project tracking relationships",
|
||||
description: "Manage project tracking policy",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad track [<id>] [--fetch]
|
||||
rad track <peer> [--fetch] [--alias <name>]
|
||||
|
||||
Options
|
||||
|
||||
--alias <name> Add an alias to this peer identifier
|
||||
--fetch Fetch the peer's refs into the working copy
|
||||
--verbose, -v Verbose output
|
||||
--help Print help
|
||||
|
|
@ -29,7 +30,8 @@ Options
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub id: Option<Id>,
|
||||
pub peer: NodeId,
|
||||
pub alias: Option<String>,
|
||||
pub fetch: bool,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
|
@ -39,21 +41,31 @@ impl Args for Options {
|
|||
use lexopt::prelude::*;
|
||||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut id: Option<Id> = None;
|
||||
let mut peer: Option<NodeId> = None;
|
||||
let mut alias: Option<String> = None;
|
||||
let mut fetch = true;
|
||||
let mut verbose = false;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match arg {
|
||||
Long("alias") => {
|
||||
let name = parser.value()?;
|
||||
let name = name
|
||||
.to_str()
|
||||
.to_owned()
|
||||
.ok_or_else(|| anyhow!("alias specified is not UTF-8"))?;
|
||||
|
||||
alias = Some(name.to_owned());
|
||||
}
|
||||
Long("no-fetch") => fetch = false,
|
||||
Long("verbose") | Short('v') => verbose = true,
|
||||
Value(val) if id.is_none() => {
|
||||
Value(val) if peer.is_none() => {
|
||||
let val = val.to_string_lossy();
|
||||
|
||||
if let Ok(val) = Id::from_human(&val) {
|
||||
id = Some(val);
|
||||
if let Ok(val) = NodeId::from_str(&val) {
|
||||
peer = Some(val);
|
||||
} else {
|
||||
return Err(anyhow!("invalid ID '{}'", val));
|
||||
return Err(anyhow!("invalid Node ID '{}'", val));
|
||||
}
|
||||
}
|
||||
Long("help") => {
|
||||
|
|
@ -65,18 +77,24 @@ impl Args for Options {
|
|||
}
|
||||
}
|
||||
|
||||
Ok((Options { id, fetch, verbose }, vec![]))
|
||||
Ok((
|
||||
Options {
|
||||
peer: peer.ok_or_else(|| anyhow!("a peer to track must be supplied"))?,
|
||||
alias,
|
||||
fetch,
|
||||
verbose,
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let id = options
|
||||
.id
|
||||
.or_else(|| radicle::rad::cwd().ok().map(|(_, id)| id))
|
||||
.context("current directory is not a git repository; please supply an `<id>`")?;
|
||||
let peer = options.peer;
|
||||
let profile = ctx.profile()?;
|
||||
let storage = &profile.storage;
|
||||
let Doc { payload, .. } = storage.repository(id)?.project_of(profile.id())?;
|
||||
let (_, rid) = radicle::rad::cwd().context("this command must be run within a project")?;
|
||||
let Doc { payload, .. } = storage.repository(rid)?.project_of(profile.id())?;
|
||||
let node = radicle::node::connect(&profile.node())?;
|
||||
|
||||
term::info!(
|
||||
|
|
@ -85,16 +103,22 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
);
|
||||
term::blank();
|
||||
|
||||
let tracked = node.track(&id)?;
|
||||
term::success!(
|
||||
"Tracking relationship for {} ({}) {}",
|
||||
term::format::tertiary(&payload.name),
|
||||
&id.to_human(),
|
||||
if tracked { "established" } else { "exists" }
|
||||
);
|
||||
let tracked = node.track_node(&peer, options.alias.as_deref())?;
|
||||
let outcome = if tracked { "established" } else { "exists" };
|
||||
|
||||
if let Some(alias) = options.alias {
|
||||
term::success!(
|
||||
"Tracking relationship with {} ({}) {}",
|
||||
term::format::tertiary(alias),
|
||||
peer,
|
||||
outcome
|
||||
);
|
||||
} else {
|
||||
term::success!("Tracking relationship with {} {}", peer, outcome);
|
||||
}
|
||||
|
||||
if options.fetch {
|
||||
node.fetch(&id)?;
|
||||
node.fetch(&rid)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -89,5 +89,5 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
|
||||
pub fn untrack(id: Id, profile: &Profile) -> anyhow::Result<bool> {
|
||||
let node = radicle::node::connect(profile.node())?;
|
||||
node.untrack(&id).map_err(|e| anyhow!(e))
|
||||
node.untrack_repo(&id).map_err(|e| anyhow!(e))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,15 +66,31 @@ impl<W: Waker> traits::Handle for Handle<W> {
|
|||
receiver.recv().map_err(Error::from)
|
||||
}
|
||||
|
||||
fn track(&mut self, id: Id) -> Result<bool, Error> {
|
||||
fn track_node(&mut self, id: NodeId, alias: Option<String>) -> Result<bool, Error> {
|
||||
let (sender, receiver) = chan::bounded(1);
|
||||
self.commands.send(service::Command::Track(id, sender))?;
|
||||
self.commands
|
||||
.send(service::Command::TrackNode(id, alias, sender))?;
|
||||
receiver.recv().map_err(Error::from)
|
||||
}
|
||||
|
||||
fn untrack(&mut self, id: Id) -> Result<bool, Error> {
|
||||
fn untrack_node(&mut self, id: NodeId) -> Result<bool, Error> {
|
||||
let (sender, receiver) = chan::bounded(1);
|
||||
self.commands.send(service::Command::Untrack(id, sender))?;
|
||||
self.commands
|
||||
.send(service::Command::UntrackNode(id, sender))?;
|
||||
receiver.recv().map_err(Error::from)
|
||||
}
|
||||
|
||||
fn track_repo(&mut self, id: Id) -> Result<bool, Error> {
|
||||
let (sender, receiver) = chan::bounded(1);
|
||||
self.commands
|
||||
.send(service::Command::TrackRepo(id, sender))?;
|
||||
receiver.recv().map_err(Error::from)
|
||||
}
|
||||
|
||||
fn untrack_repo(&mut self, id: Id) -> Result<bool, Error> {
|
||||
let (sender, receiver) = chan::bounded(1);
|
||||
self.commands
|
||||
.send(service::Command::UntrackRepo(id, sender))?;
|
||||
receiver.recv().map_err(Error::from)
|
||||
}
|
||||
|
||||
|
|
@ -146,9 +162,13 @@ pub mod traits {
|
|||
fn fetch(&mut self, id: Id) -> Result<FetchLookup, Error>;
|
||||
/// Start tracking the given project. Doesn't do anything if the project is already
|
||||
/// tracked.
|
||||
fn track(&mut self, id: Id) -> Result<bool, Error>;
|
||||
fn track_repo(&mut self, id: Id) -> Result<bool, Error>;
|
||||
/// Start tracking the given node.
|
||||
fn track_node(&mut self, id: NodeId, alias: Option<String>) -> Result<bool, Error>;
|
||||
/// Untrack the given project and delete it from storage.
|
||||
fn untrack(&mut self, id: Id) -> Result<bool, Error>;
|
||||
fn untrack_repo(&mut self, id: Id) -> Result<bool, Error>;
|
||||
/// Untrack the given node.
|
||||
fn untrack_node(&mut self, id: NodeId) -> Result<bool, Error>;
|
||||
/// Notify the client that a project has been updated.
|
||||
fn announce_refs(&mut self, id: Id) -> Result<(), Error>;
|
||||
/// Send a command to the command channel, and wake up the event loop.
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ fn drain<H: Handle>(stream: &UnixStream, handle: &mut H) -> Result<(), DrainErro
|
|||
return Err(DrainError::InvalidCommandArg(arg.to_owned()));
|
||||
}
|
||||
}
|
||||
Some(("track", arg)) => {
|
||||
Some(("track-repo", arg)) => {
|
||||
if let Ok(id) = arg.parse() {
|
||||
match handle.track(id) {
|
||||
match handle.track_repo(id) {
|
||||
Ok(updated) => {
|
||||
if updated {
|
||||
writeln!(writer, "{}", node::RESPONSE_OK)?;
|
||||
|
|
@ -101,9 +101,50 @@ fn drain<H: Handle>(stream: &UnixStream, handle: &mut H) -> Result<(), DrainErro
|
|||
return Err(DrainError::InvalidCommandArg(arg.to_owned()));
|
||||
}
|
||||
}
|
||||
Some(("untrack", arg)) => {
|
||||
Some(("untrack-repo", arg)) => {
|
||||
if let Ok(id) = arg.parse() {
|
||||
match handle.untrack(id) {
|
||||
match handle.untrack_repo(id) {
|
||||
Ok(updated) => {
|
||||
if updated {
|
||||
writeln!(writer, "{}", node::RESPONSE_OK)?;
|
||||
} else {
|
||||
writeln!(writer, "{}", node::RESPONSE_NOOP)?;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(DrainError::Client(e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(DrainError::InvalidCommandArg(arg.to_owned()));
|
||||
}
|
||||
}
|
||||
Some(("track-node", args)) => {
|
||||
let (peer, alias) = if let Some((peer, alias)) = args.split_once(' ') {
|
||||
(peer, Some(alias.to_owned()))
|
||||
} else {
|
||||
(args, None)
|
||||
};
|
||||
if let Ok(id) = peer.parse() {
|
||||
match handle.track_node(id, alias) {
|
||||
Ok(updated) => {
|
||||
if updated {
|
||||
writeln!(writer, "{}", node::RESPONSE_OK)?;
|
||||
} else {
|
||||
writeln!(writer, "{}", node::RESPONSE_NOOP)?;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(DrainError::Client(e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(DrainError::InvalidCommandArg(args.to_owned()));
|
||||
}
|
||||
}
|
||||
Some(("untrack-node", arg)) => {
|
||||
if let Ok(id) = arg.parse() {
|
||||
match handle.untrack_node(id) {
|
||||
Ok(updated) => {
|
||||
if updated {
|
||||
writeln!(writer, "{}", node::RESPONSE_OK)?;
|
||||
|
|
@ -214,7 +255,7 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::identity::Id;
|
||||
use crate::node::Handle;
|
||||
use crate::node::Node;
|
||||
use crate::node::{Node, NodeId};
|
||||
use crate::test;
|
||||
|
||||
#[test]
|
||||
|
|
@ -255,6 +296,7 @@ mod tests {
|
|||
let tmp = tempfile::tempdir().unwrap();
|
||||
let socket = tmp.path().join("node.sock");
|
||||
let proj = test::arbitrary::gen::<Id>(1);
|
||||
let peer = test::arbitrary::gen::<NodeId>(1);
|
||||
|
||||
thread::spawn({
|
||||
let socket = socket.clone();
|
||||
|
|
@ -269,9 +311,14 @@ mod tests {
|
|||
}
|
||||
};
|
||||
|
||||
assert!(handle.track(&proj).unwrap());
|
||||
assert!(!handle.track(&proj).unwrap());
|
||||
assert!(handle.untrack(&proj).unwrap());
|
||||
assert!(!handle.untrack(&proj).unwrap());
|
||||
assert!(handle.track_repo(&proj).unwrap());
|
||||
assert!(!handle.track_repo(&proj).unwrap());
|
||||
assert!(handle.untrack_repo(&proj).unwrap());
|
||||
assert!(!handle.untrack_repo(&proj).unwrap());
|
||||
|
||||
assert!(handle.track_node(&peer, Some("alice")).unwrap());
|
||||
assert!(!handle.track_node(&peer, Some("alice")).unwrap());
|
||||
assert!(handle.untrack_node(&peer).unwrap());
|
||||
assert!(!handle.untrack_node(&peer).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,9 +150,13 @@ pub enum Command {
|
|||
/// Fetch the given project from the network.
|
||||
Fetch(Id, chan::Sender<FetchLookup>),
|
||||
/// Track the given project.
|
||||
Track(Id, chan::Sender<bool>),
|
||||
TrackRepo(Id, chan::Sender<bool>),
|
||||
/// Untrack the given project.
|
||||
Untrack(Id, chan::Sender<bool>),
|
||||
UntrackRepo(Id, chan::Sender<bool>),
|
||||
/// Track the given node.
|
||||
TrackNode(NodeId, Option<String>, chan::Sender<bool>),
|
||||
/// Untrack the given node.
|
||||
UntrackNode(NodeId, chan::Sender<bool>),
|
||||
/// Query the internal service state.
|
||||
QueryState(Arc<QueryState>, chan::Sender<Result<(), CommandError>>),
|
||||
}
|
||||
|
|
@ -163,8 +167,10 @@ impl fmt::Debug for Command {
|
|||
Self::AnnounceRefs(id) => write!(f, "AnnounceRefs({})", id),
|
||||
Self::Connect(addr) => write!(f, "Connect({})", addr),
|
||||
Self::Fetch(id, _) => write!(f, "Fetch({})", id),
|
||||
Self::Track(id, _) => write!(f, "Track({})", id),
|
||||
Self::Untrack(id, _) => write!(f, "Untrack({})", id),
|
||||
Self::TrackRepo(id, _) => write!(f, "TrackRepo({})", id),
|
||||
Self::UntrackRepo(id, _) => write!(f, "UntrackRepo({})", id),
|
||||
Self::TrackNode(id, _, _) => write!(f, "TrackNode({})", id),
|
||||
Self::UntrackNode(id, _) => write!(f, "UntrackNode({})", id),
|
||||
Self::QueryState { .. } => write!(f, "QueryState(..)"),
|
||||
}
|
||||
}
|
||||
|
|
@ -290,20 +296,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Track a project.
|
||||
/// Track a repository.
|
||||
/// Returns whether or not the tracking policy was updated.
|
||||
pub fn track(&mut self, id: &Id, scope: tracking::Scope) -> Result<bool, tracking::Error> {
|
||||
pub fn track_repo(&mut self, id: &Id, scope: tracking::Scope) -> Result<bool, tracking::Error> {
|
||||
self.out_of_sync = self.tracking.track_repo(id, scope)?;
|
||||
self.filter.insert(id);
|
||||
|
||||
Ok(self.out_of_sync)
|
||||
}
|
||||
|
||||
/// Untrack a project.
|
||||
/// Untrack a repository.
|
||||
/// Returns whether or not the tracking policy was updated.
|
||||
/// Note that when untracking, we don't announce anything to the network. This is because by
|
||||
/// simply not announcing it anymore, it will eventually be pruned by nodes.
|
||||
pub fn untrack(&mut self, id: &Id) -> Result<bool, tracking::Error> {
|
||||
pub fn untrack_repo(&mut self, id: &Id) -> Result<bool, tracking::Error> {
|
||||
// Nb. This is potentially slow if we have lots of projects. We should probably
|
||||
// only re-compute the filter when we've untracked a certain amount of projects
|
||||
// and the filter is really out of date.
|
||||
|
|
@ -491,18 +497,32 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
Command::Track(id, resp) => {
|
||||
Command::TrackRepo(id, resp) => {
|
||||
let tracked = self
|
||||
.track(&id, tracking::Scope::All)
|
||||
.track_repo(&id, tracking::Scope::All)
|
||||
.expect("Service::command: error tracking repository");
|
||||
resp.send(tracked).ok();
|
||||
}
|
||||
Command::Untrack(id, resp) => {
|
||||
Command::UntrackRepo(id, resp) => {
|
||||
let untracked = self
|
||||
.untrack(&id)
|
||||
.untrack_repo(&id)
|
||||
.expect("Service::command: error untracking repository");
|
||||
resp.send(untracked).ok();
|
||||
}
|
||||
Command::TrackNode(id, alias, resp) => {
|
||||
let tracked = self
|
||||
.tracking
|
||||
.track_node(&id, alias.as_deref())
|
||||
.expect("Service::command: error tracking node");
|
||||
resp.send(tracked).ok();
|
||||
}
|
||||
Command::UntrackNode(id, resp) => {
|
||||
let untracked = self
|
||||
.tracking
|
||||
.untrack_node(&id)
|
||||
.expect("Service::command: error untracking node");
|
||||
resp.send(untracked).ok();
|
||||
}
|
||||
Command::AnnounceRefs(id) => {
|
||||
if let Err(err) = self.announce_refs(id) {
|
||||
error!("Error announcing refs: {}", err);
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@ use crate::client::handle::Error;
|
|||
use crate::identity::Id;
|
||||
use crate::service;
|
||||
use crate::service::FetchLookup;
|
||||
use crate::service::NodeId;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Handle {
|
||||
pub updates: Arc<Mutex<Vec<Id>>>,
|
||||
pub tracking: HashSet<Id>,
|
||||
pub tracking_repos: HashSet<Id>,
|
||||
pub tracking_nodes: HashSet<NodeId>,
|
||||
}
|
||||
|
||||
impl traits::Handle for Handle {
|
||||
|
|
@ -24,12 +26,20 @@ impl traits::Handle for Handle {
|
|||
Ok(FetchLookup::NotFound)
|
||||
}
|
||||
|
||||
fn track(&mut self, id: Id) -> Result<bool, Error> {
|
||||
Ok(self.tracking.insert(id))
|
||||
fn track_repo(&mut self, id: Id) -> Result<bool, Error> {
|
||||
Ok(self.tracking_repos.insert(id))
|
||||
}
|
||||
|
||||
fn untrack(&mut self, id: Id) -> Result<bool, Error> {
|
||||
Ok(self.tracking.remove(&id))
|
||||
fn untrack_repo(&mut self, id: Id) -> Result<bool, Error> {
|
||||
Ok(self.tracking_repos.remove(&id))
|
||||
}
|
||||
|
||||
fn track_node(&mut self, id: NodeId, _alias: Option<String>) -> Result<bool, Error> {
|
||||
Ok(self.tracking_nodes.insert(id))
|
||||
}
|
||||
|
||||
fn untrack_node(&mut self, id: NodeId) -> Result<bool, Error> {
|
||||
Ok(self.tracking_nodes.remove(&id))
|
||||
}
|
||||
|
||||
fn announce_refs(&mut self, id: Id) -> Result<(), Error> {
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ fn test_tracking() {
|
|||
let proj_id: identity::Id = test::arbitrary::gen(1);
|
||||
|
||||
let (sender, receiver) = chan::bounded(1);
|
||||
alice.command(Command::Track(proj_id, sender));
|
||||
alice.command(Command::TrackRepo(proj_id, sender));
|
||||
let policy_change = receiver
|
||||
.recv()
|
||||
.map_err(client::handle::Error::from)
|
||||
|
|
@ -362,7 +362,7 @@ fn test_tracking() {
|
|||
assert!(alice.tracking().is_repo_tracked(&proj_id).unwrap());
|
||||
|
||||
let (sender, receiver) = chan::bounded(1);
|
||||
alice.command(Command::Untrack(proj_id, sender));
|
||||
alice.command(Command::UntrackRepo(proj_id, sender));
|
||||
let policy_change = receiver
|
||||
.recv()
|
||||
.map_err(client::handle::Error::from)
|
||||
|
|
@ -556,9 +556,9 @@ fn test_refs_announcement_relay() {
|
|||
};
|
||||
let bob_inv = bob.inventory().unwrap();
|
||||
|
||||
alice.track(&bob_inv[0], tracking::Scope::All).unwrap();
|
||||
alice.track(&bob_inv[1], tracking::Scope::All).unwrap();
|
||||
alice.track(&bob_inv[2], tracking::Scope::All).unwrap();
|
||||
alice.track_repo(&bob_inv[0], tracking::Scope::All).unwrap();
|
||||
alice.track_repo(&bob_inv[1], tracking::Scope::All).unwrap();
|
||||
alice.track_repo(&bob_inv[2], tracking::Scope::All).unwrap();
|
||||
alice.connect_to(&bob);
|
||||
alice.connect_to(&eve);
|
||||
alice.receive(&eve.addr(), Message::Subscribe(Subscribe::all()));
|
||||
|
|
@ -598,7 +598,7 @@ fn test_refs_announcement_no_subscribe() {
|
|||
let eve = Peer::new("eve", [9, 9, 9, 9], MockStorage::empty());
|
||||
let id = arbitrary::gen(1);
|
||||
|
||||
alice.track(&id, tracking::Scope::All).unwrap();
|
||||
alice.track_repo(&id, tracking::Scope::All).unwrap();
|
||||
alice.connect_to(&bob);
|
||||
alice.connect_to(&eve);
|
||||
alice.receive(&bob.addr(), bob.refs_announcement(id));
|
||||
|
|
@ -857,11 +857,11 @@ fn test_push_and_pull() {
|
|||
|
||||
// Bob tracks Alice's project.
|
||||
let (sender, _) = chan::bounded(1);
|
||||
bob.command(service::Command::Track(proj_id, sender));
|
||||
bob.command(service::Command::TrackRepo(proj_id, sender));
|
||||
|
||||
// Eve tracks Alice's project.
|
||||
let (sender, _) = chan::bounded(1);
|
||||
eve.command(service::Command::Track(proj_id, sender));
|
||||
eve.command(service::Command::TrackRepo(proj_id, sender));
|
||||
|
||||
let mut sim = Simulation::new(
|
||||
LocalTime::now(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
mod features;
|
||||
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
|
@ -31,11 +30,15 @@ pub enum Error {
|
|||
pub trait Handle {
|
||||
/// Fetch a project from the network. Fails if the project isn't tracked.
|
||||
fn fetch(&self, id: &Id) -> Result<(), Error>;
|
||||
/// Start tracking the given project. Doesn't do anything if the project is already
|
||||
/// tracked.
|
||||
fn track(&self, id: &Id) -> Result<bool, Error>;
|
||||
/// Untrack the given project and delete it from storage.
|
||||
fn untrack(&self, id: &Id) -> Result<bool, Error>;
|
||||
/// Start tracking the given node. If the node is already tracked,
|
||||
/// updates the alias if necessary.
|
||||
fn track_node(&self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error>;
|
||||
/// Start tracking the given repository.
|
||||
fn track_repo(&self, id: &Id) -> Result<bool, Error>;
|
||||
/// Untrack the given node.
|
||||
fn untrack_node(&self, id: &NodeId) -> Result<bool, Error>;
|
||||
/// Untrack the given repository and delete it from storage.
|
||||
fn untrack_repo(&self, id: &Id) -> Result<bool, Error>;
|
||||
/// Notify the network that we have new refs.
|
||||
fn announce_refs(&self, id: &Id) -> Result<(), Error>;
|
||||
/// Ask the node to shutdown.
|
||||
|
|
@ -60,12 +63,17 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Call a command on the node.
|
||||
pub fn call<A: fmt::Display>(
|
||||
pub fn call<A: ToString>(
|
||||
&self,
|
||||
cmd: &str,
|
||||
arg: &A,
|
||||
args: &[A],
|
||||
) -> Result<impl Iterator<Item = Result<String, io::Error>> + '_, io::Error> {
|
||||
writeln!(&self.stream, "{cmd} {arg}")?;
|
||||
let args = args
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
writeln!(&self.stream, "{cmd} {args}")?;
|
||||
|
||||
Ok(BufReader::new(&self.stream).lines())
|
||||
}
|
||||
|
|
@ -73,16 +81,23 @@ impl Node {
|
|||
|
||||
impl Handle for Node {
|
||||
fn fetch(&self, id: &Id) -> Result<(), Error> {
|
||||
for line in self.call("fetch", id)? {
|
||||
for line in self.call("fetch", &[id])? {
|
||||
let line = line?;
|
||||
log::debug!("node: {}", line);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn track(&self, id: &Id) -> Result<bool, Error> {
|
||||
let mut line = self.call("track", id)?;
|
||||
let line = line.next().ok_or(Error::EmptyResponse { cmd: "track" })??;
|
||||
fn track_node(&self, id: &NodeId, alias: Option<&str>) -> Result<bool, Error> {
|
||||
let id = id.to_human();
|
||||
let mut line = if let Some(alias) = alias {
|
||||
self.call("track-node", &[id.as_str(), alias])
|
||||
} else {
|
||||
self.call("track-node", &[id.as_str()])
|
||||
}?;
|
||||
let line = line
|
||||
.next()
|
||||
.ok_or(Error::EmptyResponse { cmd: "track-node" })??;
|
||||
|
||||
log::debug!("node: {}", line);
|
||||
|
||||
|
|
@ -90,17 +105,17 @@ impl Handle for Node {
|
|||
RESPONSE_OK => Ok(true),
|
||||
RESPONSE_NOOP => Ok(false),
|
||||
_ => Err(Error::InvalidResponse {
|
||||
cmd: "track",
|
||||
cmd: "track-node",
|
||||
response: line,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn untrack(&self, id: &Id) -> Result<bool, Error> {
|
||||
let mut line = self.call("untrack", id)?;
|
||||
fn track_repo(&self, id: &Id) -> Result<bool, Error> {
|
||||
let mut line = self.call("track-repo", &[id])?;
|
||||
let line = line
|
||||
.next()
|
||||
.ok_or(Error::EmptyResponse { cmd: "untrack" })??;
|
||||
.ok_or(Error::EmptyResponse { cmd: "track-repo" })??;
|
||||
|
||||
log::debug!("node: {}", line);
|
||||
|
||||
|
|
@ -108,14 +123,50 @@ impl Handle for Node {
|
|||
RESPONSE_OK => Ok(true),
|
||||
RESPONSE_NOOP => Ok(false),
|
||||
_ => Err(Error::InvalidResponse {
|
||||
cmd: "untrack",
|
||||
cmd: "track-repo",
|
||||
response: line,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn untrack_node(&self, id: &NodeId) -> Result<bool, Error> {
|
||||
let mut line = self.call("untrack-node", &[id])?;
|
||||
let line = line.next().ok_or(Error::EmptyResponse {
|
||||
cmd: "untrack-node",
|
||||
})??;
|
||||
|
||||
log::debug!("node: {}", line);
|
||||
|
||||
match line.as_str() {
|
||||
RESPONSE_OK => Ok(true),
|
||||
RESPONSE_NOOP => Ok(false),
|
||||
_ => Err(Error::InvalidResponse {
|
||||
cmd: "untrack-node",
|
||||
response: line,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn untrack_repo(&self, id: &Id) -> Result<bool, Error> {
|
||||
let mut line = self.call("untrack-repo", &[id])?;
|
||||
let line = line.next().ok_or(Error::EmptyResponse {
|
||||
cmd: "untrack-repo",
|
||||
})??;
|
||||
|
||||
log::debug!("node: {}", line);
|
||||
|
||||
match line.as_str() {
|
||||
RESPONSE_OK => Ok(true),
|
||||
RESPONSE_NOOP => Ok(false),
|
||||
_ => Err(Error::InvalidResponse {
|
||||
cmd: "untrack-repo",
|
||||
response: line,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn announce_refs(&self, id: &Id) -> Result<(), Error> {
|
||||
for line in self.call("announce-refs", id)? {
|
||||
for line in self.call("announce-refs", &[id])? {
|
||||
let line = line?;
|
||||
log::debug!("node: {}", line);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ pub fn clone<P: AsRef<Path>, G: Signer, S: storage::WriteStorage, H: node::Handl
|
|||
storage: &S,
|
||||
handle: &H,
|
||||
) -> Result<git2::Repository, CloneError> {
|
||||
let _ = handle.track(&proj)?;
|
||||
let _ = handle.track_repo(&proj)?;
|
||||
let _ = handle.fetch(&proj)?;
|
||||
let _ = fork(proj, signer, storage)?;
|
||||
let working = checkout(proj, signer.public_key(), path, storage)?;
|
||||
|
|
|
|||
Loading…
Reference in New Issue