From 83f89188161b38046b39124ffb45bbbd6fcb572f Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 20 Feb 2018 01:48:17 -0500 Subject: add vcs support --- Cargo.lock | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 3 + src/prompt.rs | 115 ++++++++++++++++++++++++++++++++-- src/system_info.rs | 5 ++ src/vcs/git.rs | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vcs/mod.rs | 35 +++++++++++ 7 files changed, 500 insertions(+), 6 deletions(-) create mode 100644 src/vcs/git.rs create mode 100644 src/vcs/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 04bd256..2d1cd08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,11 +21,21 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cc" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "chrono" version = "0.4.0" @@ -49,12 +59,35 @@ dependencies = [ "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cmake" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "curl-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fancy-prompt" version = "0.1.0" dependencies = [ "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "hostname 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -63,6 +96,19 @@ dependencies = [ "walkdir 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "git2" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hostname" version = "0.1.4" @@ -72,6 +118,16 @@ dependencies = [ "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "idna" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -91,6 +147,49 @@ name = "libc" version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libgit2-sys" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "curl-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libz-sys" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memchr" version = "2.0.1" @@ -131,6 +230,32 @@ name = "num-traits" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pkg-config" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "redox_syscall" version = "0.1.37" @@ -230,6 +355,19 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-width" version = "0.1.4" @@ -243,6 +381,16 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "url" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "users" version = "0.6.0" @@ -256,6 +404,11 @@ name = "utf8-ranges" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vcpkg" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vec_map" version = "0.8.0" @@ -316,18 +469,32 @@ dependencies = [ "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" "checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0" "checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" "checksum clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c07b9257a00f3fc93b7f3c417fc15607ec7a56823bc2c37ec744e266387de5b" +"checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb" +"checksum curl-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e49c7125131f5afaded06944d6888b55cbdf8eba05dae73c954019b907961" +"checksum git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee5b4bb7cd2a44e6e5ee3a26ba6a9ca10d4ce2771cdc3839bbc54b47b7d1be84" "checksum hostname 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "58fab6e177434b0bb4cd344a4dabaa5bd6d7a8d792b1885aebcae7af1091d1cb" +"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" +"checksum libgit2-sys 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6eeae66e7b1c995de45cb4e65c5ab438a96a7b4077e448645d4048dc753ad357" +"checksum libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0db4ec23611747ef772db1c4d650f8bd762f07b461727ec998f953c614024b75" +"checksum libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "87f737ad6cc6fd6eefe3d9dc5412f1573865bded441300904d2f42269e140f16" +"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" "checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" "checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5a41ce2f5f2d939c80decde8fcfcf5837c203ca6c06a553510a2fcb84fa3ef1" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5be5347bde0c48cfd8c3fdc0766cdfe9d8a755ef84d620d6794c778c91de8b2b" @@ -340,10 +507,14 @@ dependencies = [ "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2" "checksum users 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "99ab1b53affc9f75f57da4a8b051a188e84d20d43bea0dd9bd8db71eebbca6da" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" +"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b167e9a4420d8dddb260e70c90a4a375a1e5691f21f70e715553da87b6c2503a" diff --git a/Cargo.toml b/Cargo.toml index 0040bae..b875d9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Jesse Luehrs "] [dependencies] chrono = "0.4" clap = "2.30" +git2 = "0.6" hostname = "0.1" regex = "0.2" term = "0.4" diff --git a/src/main.rs b/src/main.rs index 471a3a2..85b4627 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ extern crate chrono; extern crate clap; +extern crate git2; extern crate hostname; extern crate regex; extern crate term; @@ -11,6 +12,7 @@ mod colors; mod power; mod prompt; mod system_info; +mod vcs; fn collect_data() -> prompt::PromptData { let matches = clap::App::new("fancy-prompt") @@ -45,6 +47,7 @@ fn collect_data() -> prompt::PromptData { is_root: system_info::is_root(), time: system_info::time(), power_info: system_info::power_info(), + vcs_info: system_info::vcs_info(), } } diff --git a/src/prompt.rs b/src/prompt.rs index 2679bc9..12a7ee5 100644 --- a/src/prompt.rs +++ b/src/prompt.rs @@ -2,15 +2,17 @@ use chrono; use regex; use std; +use std::fmt::Write; + use colors; use power; +use vcs; pub struct Prompt { colors: colors::Colors, data: PromptData, } -#[derive(Debug)] pub struct PromptData { pub shell: colors::ShellType, pub error_code: u8, @@ -22,6 +24,7 @@ pub struct PromptData { pub is_root: bool, pub time: chrono::DateTime, pub power_info: power::PowerInfo, + pub vcs_info: Option>, } impl Prompt { @@ -36,13 +39,22 @@ impl Prompt { let user = self.data.user.clone().unwrap_or(String::from("???")); let host = self.data.hostname.clone().unwrap_or(String::from("???")); + let max_vcs_len = 20; // "g*+?:mybr...nch:+1-1" + let (vcs, vcs_err) = self.format_vcs(); + let vcs = vcs.map(|vcs| { + compress_vcs(&vcs, max_vcs_len) + }); + let battery_len = 10; let cols = self.data.terminal_cols.unwrap_or(80); - // " (~/a/...cde) ---- {--<=======} doy@lance [19:40:50] " + // " (~/a/...cde|g*+?:mybr:+1-1) -- {--<=======} doy@lance [19:40:50] " let max_path_len = cols - 1 // " " - - 2 // "()" + - vcs + .as_ref() + .map(|vcs| vcs.len() + 1) // "|g*+?:mybr:+1-1" + .unwrap_or(0) - 2 // "()" - 1 // " " - 1 // "-" - 1 // " " @@ -65,9 +77,10 @@ impl Prompt { &self.data.home, max_path_len ); + let path_err = false; // XXX self.colors.pad(1); - self.display_path(&path); + self.display_path(&path, path_err, &vcs, vcs_err); self.colors.pad(1); self.display_border(max_path_len - path.len() + 1); @@ -91,9 +104,19 @@ impl Prompt { self.colors.pad(1); } - fn display_path(&self, path: &str) { + fn display_path( + &self, + path: &str, + path_err: bool, + vcs: &Option, + vcs_err: bool + ) { self.colors.print_host(&self.data.hostname, "("); - self.colors.print("default", path); + self.colors.print(if path_err { "error" } else { "default" }, path); + if let &Some(ref vcs) = vcs { + self.colors.print_host(&self.data.hostname, "|"); + self.colors.print(if vcs_err { "error" } else { "default" }, &vcs); + } self.colors.print_host(&self.data.hostname, ")"); } @@ -159,6 +182,64 @@ impl Prompt { let prompt = if self.data.is_root { "#" } else { "$" }; self.colors.print_user(&self.data.user, prompt); } + + fn format_vcs(&self) -> (Option, bool) { + (self.data.vcs_info.as_ref().map(|vcs_info| { + let mut vcs = String::new(); + + write!(vcs, "{}", vcs_id(vcs_info.vcs())).unwrap(); + + if vcs_info.has_modified_files() { + write!(vcs, "*").unwrap(); + } + if vcs_info.has_staged_files() { + write!(vcs, "+").unwrap(); + } + if vcs_info.has_new_files() { + write!(vcs, "?").unwrap(); + } + if !vcs_info.has_commits() { + write!(vcs, "!").unwrap(); + } + + let branch = vcs_info.branch().map(|branch| { + if branch == "master" { + String::new() + } + else { + branch + } + }).unwrap_or(String::from("???")); + if branch != "" { + write!(vcs, ":").unwrap(); + } + write!(vcs, "{}", branch).unwrap(); + + if let Some((local, remote)) = vcs_info.remote_branch_diff() { + if local > 0 || remote > 0 { + write!(vcs, ":").unwrap(); + } + if local > 0 { + write!(vcs, "+{}", local).unwrap(); + } + if remote > 0 { + write!(vcs, "-{}", remote).unwrap(); + } + } + else { + write!(vcs, ":-").unwrap(); + } + + match vcs_info.active_operation() { + vcs::ActiveOperation::None => {}, + op => { + write!(vcs, "({})", active_operation_id(op)).unwrap(); + } + } + + vcs + }), false) // XXX + } } fn battery_discharge_color(usage: f64, charging: bool) -> &'static str { @@ -226,3 +307,25 @@ fn compress_path( String::from("???") } } + +fn compress_vcs(vcs: &str, _len: usize) -> String { + // XXX + String::from(vcs) +} + +fn vcs_id(vcs: vcs::VcsType) -> String { + match vcs { + vcs::VcsType::Git => String::from("g"), + } +} + +fn active_operation_id(op: vcs::ActiveOperation) -> String { + match op { + vcs::ActiveOperation::None => String::new(), + vcs::ActiveOperation::Merge => String::from("m"), + vcs::ActiveOperation::Revert => String::from("v"), + vcs::ActiveOperation::CherryPick => String::from("c"), + vcs::ActiveOperation::Bisect => String::from("b"), + vcs::ActiveOperation::Rebase => String::from("r"), + } +} diff --git a/src/system_info.rs b/src/system_info.rs index 2079ea7..47ed0f9 100644 --- a/src/system_info.rs +++ b/src/system_info.rs @@ -5,6 +5,7 @@ use std; use users; use power; +use vcs; pub fn hostname() -> Option { hostname::get_hostname() @@ -44,3 +45,7 @@ pub fn time() -> chrono::DateTime { pub fn power_info() -> power::PowerInfo { power::PowerInfo::new() } + +pub fn vcs_info() -> Option> { + vcs::detect() +} diff --git a/src/vcs/git.rs b/src/vcs/git.rs new file mode 100644 index 0000000..749d498 --- /dev/null +++ b/src/vcs/git.rs @@ -0,0 +1,176 @@ +use git2; +use std; + +use std::fmt::Write; + +#[derive(Debug)] +pub struct GitInfo { + modified_files: bool, + staged_files: bool, + new_files: bool, + commits: bool, + active_operation: super::ActiveOperation, + branch: Option, + remote_branch_diff: Option<(usize, usize)>, +} + +impl GitInfo { + pub fn new(git: git2::Repository) -> GitInfo { + let mut modified_statuses = git2::Status::empty(); + modified_statuses.insert(git2::STATUS_WT_DELETED); + modified_statuses.insert(git2::STATUS_WT_MODIFIED); + modified_statuses.insert(git2::STATUS_WT_RENAMED); + modified_statuses.insert(git2::STATUS_WT_TYPECHANGE); + let mut staged_statuses = git2::Status::empty(); + staged_statuses.insert(git2::STATUS_INDEX_DELETED); + staged_statuses.insert(git2::STATUS_INDEX_MODIFIED); + staged_statuses.insert(git2::STATUS_INDEX_NEW); + staged_statuses.insert(git2::STATUS_INDEX_RENAMED); + staged_statuses.insert(git2::STATUS_INDEX_TYPECHANGE); + let mut new_statuses = git2::Status::empty(); + new_statuses.insert(git2::STATUS_WT_NEW); + + let mut status_options = git2::StatusOptions::new(); + status_options.include_untracked(true); + if true { // XXX + status_options.update_index(true); + } + else { + status_options.update_index(false); + status_options.no_refresh(true); + } + let status = git.statuses(Some(&mut status_options)); + let mut modified_files = false; + let mut staged_files = false; + let mut new_files = false; + for status in status.iter() { + for file in status.iter() { + if file.status().intersects(modified_statuses) { + modified_files = true; + } + if file.status().intersects(staged_statuses) { + staged_files = true; + } + if file.status().intersects(new_statuses) { + new_files = true; + } + } + } + + let head = git.head(); + let commits = head.is_ok(); + let branch = head.ok() + .and_then(|head| { + if head.is_branch() { + head.shorthand().map(|s| s.to_string()) + } + else { + head.resolve().ok() + .and_then(|head| head.target()) + .map(|oid| { + let mut sha = String::new(); + for b in oid.as_bytes().iter() { + write!(sha, "{:02x}", b).unwrap(); + } + sha.truncate(7); + sha + }) + } + }); + + let active_operation = match git.state() { + git2::RepositoryState::Merge + => super::ActiveOperation::Merge, + git2::RepositoryState::Revert + | git2::RepositoryState::RevertSequence + => super::ActiveOperation::Revert, + git2::RepositoryState::CherryPick + | git2::RepositoryState::CherryPickSequence + => super::ActiveOperation::CherryPick, + git2::RepositoryState::Bisect + => super::ActiveOperation::Bisect, + git2::RepositoryState::Rebase + | git2::RepositoryState::RebaseInteractive + | git2::RepositoryState::RebaseMerge + => super::ActiveOperation::Rebase, + _ => super::ActiveOperation::None, + }; + + let remote_branch_diff = git.head().ok() + .and_then(|head| if head.is_branch() { Some(head) } else { None }) + .and_then(|head| { + head.resolve().ok() + }) + .map(|head| { + (head.target(), head.shorthand().map(|s| s.to_string())) + }) + .and_then(|(head_id, name)| { + head_id.and_then(|head_id| { + name.and_then(|name| { + git.refname_to_id( + &(String::from("refs/remotes/origin/") + &name) + ).ok().and_then(|remote_id| { + git.graph_ahead_behind(head_id, remote_id).ok() + }) + }) + }) + }); + + GitInfo { + modified_files: modified_files, + staged_files: staged_files, + new_files: new_files, + commits: commits, + active_operation: active_operation, + branch: branch, + remote_branch_diff: remote_branch_diff, + } + } +} + +impl super::VcsInfo for GitInfo { + fn vcs(&self) -> super::VcsType { + super::VcsType::Git + } + + fn has_modified_files(&self) -> bool { + self.modified_files + } + + fn has_staged_files(&self) -> bool { + self.staged_files + } + + fn has_new_files(&self) -> bool { + self.new_files + } + + fn has_commits(&self) -> bool { + self.commits + } + + fn active_operation(&self) -> super::ActiveOperation { + self.active_operation + } + + fn branch(&self) -> Option { + self.branch.clone() + } + + fn remote_branch_diff(&self) -> Option<(usize, usize)> { + self.remote_branch_diff + } +} + +pub fn detect() -> Option> { + let git = std::env::current_dir().ok().and_then(|pwd| { + git2::Repository::discover(pwd).ok() + }); + + if let Some(git) = git { + Some(Box::new(GitInfo::new(git))) + } + else { + None + } +} diff --git a/src/vcs/mod.rs b/src/vcs/mod.rs new file mode 100644 index 0000000..02c2eb7 --- /dev/null +++ b/src/vcs/mod.rs @@ -0,0 +1,35 @@ +mod git; + +pub enum VcsType { + Git, +} + +#[derive(Debug,Copy,Clone)] +pub enum ActiveOperation { + None, + Merge, + Revert, + CherryPick, + Bisect, + Rebase, +} + +pub trait VcsInfo { + fn vcs(&self) -> VcsType; + fn has_modified_files(&self) -> bool; + fn has_staged_files(&self) -> bool; + fn has_new_files(&self) -> bool; + fn has_commits(&self) -> bool; + fn active_operation(&self) -> ActiveOperation; + fn branch(&self) -> Option; + fn remote_branch_diff(&self) -> Option<(usize, usize)>; +} + +pub fn detect() -> Option> { + if let Some(git) = git::detect() { + Some(git) + } + else { + None + } +} -- cgit v1.2.3-54-g00ecf