use std::io; use std::io::IsTerminal; use std::path::Path; use std::process; use radicle::Profile; use radicle::explorer::ExplorerResource; use radicle::git; use radicle::node::Handle; use radicle::storage; use radicle_cli::node::{SyncError, SyncReporting, SyncSettings}; use radicle_cli::terminal as term; /// Abstraction for Git subprocess calls. pub(super) trait GitService { /// Run `git fetch-pack`. fn fetch_pack( &self, working: Option<&Path>, stored: &storage::git::Repository, oids: Vec, verbosity: git::Verbosity, ) -> io::Result; /// Run `git send-pack` (via `radicle::git::run`). fn send_pack(&self, working: Option<&Path>, args: &[String]) -> io::Result; } /// Production implementation using real Git subprocesses. pub(super) struct RealGitService; impl GitService for RealGitService { fn fetch_pack( &self, working: Option<&Path>, stored: &storage::git::Repository, oids: Vec, verbosity: git::Verbosity, ) -> io::Result { git::process::fetch_pack(working, stored, oids, verbosity) } fn send_pack(&self, working: Option<&Path>, args: &[String]) -> io::Result { git::run(working, args) } } /// Abstraction for Node interaction. pub(super) trait NodeSession { fn is_running(&self) -> bool; fn sync( &mut self, repo: &storage::git::Repository, updated: Vec, opts: crate::Options, profile: &Profile, ) -> Result<(), SyncError>; } pub(super) struct RealNodeSession { node: radicle::Node, } impl RealNodeSession { pub(super) fn new(profile: &Profile) -> Self { Self { node: radicle::Node::new(profile.socket_from_env()), } } } impl NodeSession for RealNodeSession { fn is_running(&self) -> bool { self.node.is_running() } fn sync( &mut self, repo: &storage::git::Repository, updated: Vec, opts: crate::Options, profile: &Profile, ) -> Result<(), SyncError> { let progress = if io::stderr().is_terminal() { term::PaintTarget::Stderr } else { term::PaintTarget::Hidden }; let result = radicle_cli::node::announce( repo, SyncSettings::default().with_profile(profile), SyncReporting { progress, completion: term::PaintTarget::Stderr, debug: opts.sync_debug, }, &mut self.node, profile, )?; let mut urls = Vec::new(); if let Some(result) = result { for seed in profile.config.preferred_seeds.iter() { if result.is_synced(&seed.id) { for resource in updated { let url = profile .config .public_explorer .url(seed.addr.host.clone(), repo.id) .resource(resource); urls.push(url); } break; } } } // Print URLs to the updated resources. if !urls.is_empty() { eprintln!(); for url in urls { eprintln!(" {}", term::format::dim(url)); } eprintln!(); } Ok(()) } }