aboutsummaryrefslogtreecommitdiff
path: root/crates/divina_git/src/lib.rs
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-02-07 12:26:07 +0000
committerFuwn <[email protected]>2025-06-09 07:46:03 +0000
commit4ace7cad20545d585d2ce8c5e93e4dc43358801b (patch)
tree043b73bdd939f955f2b4d1e6309c8b697d6e0544 /crates/divina_git/src/lib.rs
downloaddivina-4ace7cad20545d585d2ce8c5e93e4dc43358801b.tar.xz
divina-4ace7cad20545d585d2ce8c5e93e4dc43358801b.zip
feat(divina): pre-release :star:
Diffstat (limited to 'crates/divina_git/src/lib.rs')
-rw-r--r--crates/divina_git/src/lib.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/crates/divina_git/src/lib.rs b/crates/divina_git/src/lib.rs
new file mode 100644
index 0000000..e73623f
--- /dev/null
+++ b/crates/divina_git/src/lib.rs
@@ -0,0 +1,112 @@
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+//! Much help from:
+//!
+//! - <https://github.com/rust-lang/git2-rs/blob/master/examples/clone.rs>
+
+#![deny(
+ warnings,
+ nonstandard_style,
+ unused,
+ future_incompatible,
+ rust_2018_idioms,
+ unsafe_code
+)]
+#![deny(clippy::all, clippy::nursery, clippy::pedantic)]
+#![recursion_limit = "128"]
+#![doc(
+ html_logo_url = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/twitter/282/ribbon_1f380.png",
+ html_favicon_url = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/twitter/282/ribbon_1f380.png"
+)]
+
+use std::{io::Write, path};
+
+struct State {
+ progress: Option<git2::Progress<'static>>,
+ total: usize,
+ current: usize,
+ path: Option<path::PathBuf>,
+ newline: bool,
+}
+
+fn print(state: &mut State) {
+ let git_stats = state.progress.as_ref().unwrap();
+ let network_pct = (100 * git_stats.received_objects()) / git_stats.total_objects();
+ let index_pct = (100 * git_stats.indexed_objects()) / git_stats.total_objects();
+ let co_pct = if state.total > 0 {
+ (100 * state.current) / state.total
+ } else {
+ 0
+ };
+ let kbytes = git_stats.received_bytes() / 1024;
+ if git_stats.received_objects() == git_stats.total_objects() {
+ if !state.newline {
+ println!();
+ state.newline = true;
+ }
+ print!(
+ "Resolving deltas {}/{}\r",
+ git_stats.indexed_deltas(),
+ git_stats.total_deltas()
+ );
+ } else {
+ print!(
+ "net {:3}% ({:4} kb, {:5}/{:5}) / idx {:3}% ({:5}/{:5}) / chk {:3}% ({:4}/{:4}) {}\r",
+ network_pct,
+ kbytes,
+ git_stats.received_objects(),
+ git_stats.total_objects(),
+ index_pct,
+ git_stats.indexed_objects(),
+ git_stats.total_objects(),
+ co_pct,
+ state.current,
+ state.total,
+ state
+ .path
+ .as_ref()
+ .map(|s| s.to_string_lossy().into_owned())
+ .unwrap_or_default()
+ );
+ }
+ std::io::stdout().flush().unwrap();
+}
+
+/// # Errors
+/// never
+pub fn clone(url: &str, path: &str) -> Result<(), git2::Error> {
+ let state = std::cell::RefCell::new(State {
+ progress: None,
+ total: 0,
+ current: 0,
+ path: None,
+ newline: false,
+ });
+ let mut cb = git2::RemoteCallbacks::new();
+ cb.transfer_progress(|stats| {
+ let mut state = state.borrow_mut();
+ state.progress = Some(stats.to_owned());
+ print(&mut *state);
+ true
+ });
+
+ let mut co = git2::build::CheckoutBuilder::new();
+ co.progress(|path, cur, total| {
+ let mut state = state.borrow_mut();
+ state.path = path.map(path::Path::to_path_buf);
+ state.current = cur;
+ state.total = total;
+ print(&mut *state);
+ });
+
+ let mut fo = git2::FetchOptions::new();
+ fo.remote_callbacks(cb);
+ git2::build::RepoBuilder::new()
+ .fetch_options(fo)
+ .with_checkout(co)
+ .clone(url, path::Path::new(path))?;
+ println!();
+
+ Ok(())
+}