aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2018-02-19 16:10:02 -0500
committerJesse Luehrs <doy@tozt.net>2018-02-19 16:10:02 -0500
commit0b3404a16019757c891b153485f0583c49f7089f (patch)
tree6b9a3ab625758b8f4d2d102f5c474b1e56ebd339
parent5e5157e196ad56b931ee6c052ed0c349a68c6bbf (diff)
downloadfancy-prompt-0b3404a16019757c891b153485f0583c49f7089f.tar.gz
fancy-prompt-0b3404a16019757c891b153485f0583c49f7089f.zip
start sketching out some things
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock355
-rw-r--r--Cargo.toml14
-rw-r--r--src/colors.rs126
-rw-r--r--src/main.rs54
-rw-r--r--src/power.rs137
-rw-r--r--src/prompt.rs310
-rw-r--r--src/system_info.rs46
8 files changed, 1045 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0196246
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+
+/target/
+**/*.rs.bk
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..04bd256
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,355 @@
+[[package]]
+name = "aho-corasick"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "atty"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (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 = "chrono"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "clap"
+version = "2.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.0 (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)",
+ "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)",
+ "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "users 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "hostname"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memchr"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_termios"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "same-file"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "strsim"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "term"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "term_size"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "termion"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread_local"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "time"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unreachable"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "users"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "utf8-ranges"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "vec_map"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "walkdir"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "same-file 1.0.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 = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winutil"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[metadata]
+"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 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
+"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 hostname 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "58fab6e177434b0bb4cd344a4dabaa5bd6d7a8d792b1885aebcae7af1091d1cb"
+"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 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 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"
+"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
+"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
+"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
+"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
+"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
+"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
+"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-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 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 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"
+"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
+"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+"checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..0040bae
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "fancy-prompt"
+version = "0.1.0"
+authors = ["Jesse Luehrs <doy@tozt.net>"]
+
+[dependencies]
+chrono = "0.4"
+clap = "2.30"
+hostname = "0.1"
+regex = "0.2"
+term = "0.4"
+term_size = "0.3"
+users = "0.6"
+walkdir = "2.1"
diff --git a/src/colors.rs b/src/colors.rs
new file mode 100644
index 0000000..09492e3
--- /dev/null
+++ b/src/colors.rs
@@ -0,0 +1,126 @@
+use std;
+use term;
+
+#[derive(Debug,Clone)]
+pub enum ShellType {
+ Unknown,
+ Bash,
+ Zsh,
+}
+
+pub struct Colors {
+ color_map: std::collections::HashMap<String, term::color::Color>,
+ unknown_color: term::color::Color,
+ shell_type: ShellType,
+}
+
+impl ShellType {
+ pub fn from_str(shell: &str) -> Self {
+ match shell {
+ "bash" => ShellType::Bash,
+ "zsh" => ShellType::Zsh,
+ _ => panic!("unknown shell {}", shell)
+ }
+ }
+}
+
+impl Colors {
+ pub fn new(shell_type: ShellType) -> Colors {
+ let mut color_map = std::collections::HashMap::new();
+
+ color_map.insert("battery_warn".to_string(), term::color::YELLOW);
+ color_map.insert("battery_crit".to_string(), term::color::RED);
+ color_map.insert("battery_emerg".to_string(), term::color::BRIGHT_RED);
+ color_map.insert("battery_full".to_string(), term::color::GREEN);
+ color_map.insert("battery_charging".to_string(), term::color::GREEN);
+
+ color_map.insert("default".to_string(), term::color::BRIGHT_BLACK);
+ color_map.insert("error".to_string(), term::color::RED);
+
+ let unknown_color = term::color::YELLOW;
+
+ Colors {
+ color_map: color_map,
+ unknown_color: unknown_color,
+ shell_type: shell_type,
+ }
+ }
+
+ pub fn print(&self, color: &str, text: &str) {
+ let color = self.color_map.get(color);
+ self.print_with_color(color, text);
+ }
+
+ pub fn pad(&self, len: usize) {
+ print!("{}", " ".repeat(len));
+ }
+
+ pub fn newline(&self) {
+ self.print_wrapped(|| {
+ print!("{}", "\n");
+ });
+ }
+
+ pub fn print_host(&self, host: &Option<String>, text: &str) {
+ let color = host
+ .clone()
+ .and_then(|hostname| {
+ self.color_map.get(&format!("host_{}", hostname))
+ });
+ self.print_with_color(color, text);
+ }
+
+ pub fn print_user(&self, user: &Option<String>, text: &str) {
+ let color = user
+ .clone()
+ .and_then(|username| {
+ self.color_map.get(&format!("user_{}", username))
+ });
+ self.print_with_color(color, text);
+ }
+
+ fn print_with_color(&self, color: Option<&term::color::Color>, text: &str) {
+ let mut t = term::stdout().unwrap();
+ self.print_color(&mut *t, color);
+ write!(t, "{}", text).unwrap();
+ t.reset().unwrap();
+ }
+
+ fn print_color(&self, t: &mut term::StdoutTerminal, color: Option<&term::color::Color>) {
+ self.print_wrapped(|| {
+ let real_color = *color.unwrap_or(&self.unknown_color);
+ t.fg(real_color).unwrap();
+ match real_color {
+ term::color::BRIGHT_BLACK
+ | term::color::BRIGHT_BLUE
+ | term::color::BRIGHT_CYAN
+ | term::color::BRIGHT_GREEN
+ | term::color::BRIGHT_MAGENTA
+ | term::color::BRIGHT_RED
+ | term::color::BRIGHT_WHITE
+ | term::color::BRIGHT_YELLOW => {
+ t.attr(term::Attr::Bold).unwrap()
+ },
+ _ => {},
+ }
+ })
+ }
+
+ fn print_wrapped<T>(&self, printer: T)
+ where T: FnOnce()
+ {
+ match self.shell_type {
+ ShellType::Bash => { print!("{}", "\\["); },
+ ShellType::Zsh => { print!("{}", "%{"); },
+ _ => {},
+ }
+
+ printer();
+
+ match self.shell_type {
+ ShellType::Bash => { print!("{}", "\\]"); },
+ ShellType::Zsh => { print!("{}", "%}"); },
+ _ => {},
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..471a3a2
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,54 @@
+extern crate chrono;
+extern crate clap;
+extern crate hostname;
+extern crate regex;
+extern crate term;
+extern crate term_size;
+extern crate users;
+extern crate walkdir;
+
+mod colors;
+mod power;
+mod prompt;
+mod system_info;
+
+fn collect_data() -> prompt::PromptData {
+ let matches = clap::App::new("fancy-prompt")
+ .about("Prints a fancy prompt")
+ // XXX author, version (extract from cargo)
+ .arg(clap::Arg::with_name("prompt-escape")
+ .long("prompt-escape")
+ .value_name("SHELL")
+ .help("Produces escape sequence wrappers for the given shell")
+ .takes_value(true))
+ .arg(clap::Arg::with_name("error-code")
+ .value_name("ERROR_CODE")
+ .help("The error code of the previously run command")
+ )
+ .get_matches();
+
+ prompt::PromptData {
+ shell: matches
+ .value_of("prompt-escape")
+ .map(|shell| colors::ShellType::from_str(shell))
+ .unwrap_or(colors::ShellType::Unknown),
+ error_code: matches
+ .value_of("error-code")
+ .map(|code| code.parse().expect("error code must be a u8"))
+ .unwrap_or(0),
+
+ hostname: system_info::hostname(),
+ terminal_cols: system_info::terminal_cols(),
+ pwd: system_info::pwd(),
+ home: system_info::home(),
+ user: system_info::user(),
+ is_root: system_info::is_root(),
+ time: system_info::time(),
+ power_info: system_info::power_info(),
+ }
+}
+
+fn main() {
+ let data = collect_data();
+ prompt::Prompt::new(data).display();
+}
diff --git a/src/power.rs b/src/power.rs
new file mode 100644
index 0000000..2a9dd31
--- /dev/null
+++ b/src/power.rs
@@ -0,0 +1,137 @@
+use std;
+use walkdir;
+
+use std::io::Read;
+
+// XXX maybe extract this out into a separate crate?
+
+#[derive(PartialEq,Eq,Debug,Clone)]
+enum PowerSupplyType {
+ AC,
+ Battery,
+}
+
+#[derive(Debug)]
+pub struct PowerInfo {
+ power_supplies: Vec<PowerSupplyInfo>,
+}
+
+#[derive(Debug,Clone)]
+struct PowerSupplyInfo {
+ name: String,
+ ty: PowerSupplyType,
+ energy_now: Option<u64>,
+ energy_full: Option<u64>,
+ online: Option<bool>,
+}
+
+impl PowerInfo {
+ pub fn new() -> PowerInfo {
+ let mut power_supplies = vec![];
+ for entry in walkdir::WalkDir::new("/sys/class/power_supply/").min_depth(1).max_depth(1).follow_links(true) {
+ let entry = entry.unwrap();
+
+ let name = entry.path()
+ .file_name()
+ .unwrap()
+ .to_string_lossy()
+ .into_owned();
+ let ty = slurp(entry.path().join("type"))
+ .map(|t: String| PowerSupplyType::from_str(&t))
+ .expect("couldn't find power supply type");
+ let full = slurp(entry.path().join("energy_full"));
+ let now = slurp(entry.path().join("energy_now"));
+ let online = slurp(entry.path().join("online"))
+ .map(|n: u8| n != 0);
+
+ power_supplies.push(
+ PowerSupplyInfo {
+ name: name,
+ ty: ty,
+ energy_now: now,
+ energy_full: full,
+ online: online,
+ }
+ )
+ }
+
+ PowerInfo {
+ power_supplies: power_supplies,
+ }
+ }
+
+ pub fn battery_usage(&self) -> Option<f64> {
+ let mut total_now = 0;
+ let mut total_full = 0;
+ for battery in self.batteries() {
+ if let Some(now) = battery.energy_now {
+ total_now += now;
+ }
+ else {
+ return None;
+ }
+ if let Some(full) = battery.energy_full {
+ total_full += full;
+ }
+ else {
+ return None;
+ }
+ }
+
+ if total_full > 0 {
+ Some((total_now as f64) / (total_full as f64))
+ }
+ else {
+ None
+ }
+ }
+
+ pub fn charging(&self) -> bool {
+ for mains in self.mains() {
+ if mains.online == Some(true) {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn batteries(&self) -> Vec<PowerSupplyInfo> {
+ self.power_supplies
+ .iter()
+ .cloned()
+ .filter(|p| p.ty == PowerSupplyType::Battery)
+ .collect()
+ }
+
+ fn mains(&self) -> Vec<PowerSupplyInfo> {
+ self.power_supplies
+ .iter()
+ .filter(|p| p.ty == PowerSupplyType::AC)
+ .cloned()
+ .collect()
+ }
+}
+
+impl PowerSupplyType {
+ fn from_str(ty: &str) -> Self {
+ match ty {
+ "Mains" => PowerSupplyType::AC,
+ "Battery" => PowerSupplyType::Battery,
+ _ => panic!("unknown power supply type {}", ty)
+ }
+ }
+}
+
+fn slurp<T, U>(path: U) -> Option<T>
+ where T: std::str::FromStr,
+ U: AsRef<std::path::Path>
+{
+ let mut contents = String::new();
+ std::fs::File::open(path)
+ .ok()
+ .and_then(|mut fh| {
+ fh.read_to_string(&mut contents)
+ .ok()
+ .and_then(|_| contents.trim().parse().ok())
+ })
+}
diff --git a/src/prompt.rs b/src/prompt.rs
new file mode 100644
index 0000000..b2e1471
--- /dev/null
+++ b/src/prompt.rs
@@ -0,0 +1,310 @@
+use chrono;
+use regex;
+use std;
+
+use colors;
+use power;
+
+pub struct Prompt {
+ components_line1: Vec<Box<PromptComponent>>,
+ components_line2: Vec<Box<PromptComponent>>,
+ data: PromptData,
+ colors: colors::Colors,
+}
+
+trait PromptComponent {
+ fn display(&self, colors: &colors::Colors, data: &PromptData);
+ fn length(&self, data: &PromptData) -> usize;
+}
+
+struct PromptPath {
+ braces: String,
+}
+
+struct PromptBorder {
+ border: String,
+}
+
+struct PromptBattery {
+ length: usize,
+
+ braces: String,
+
+ full: String,
+ empty: String,
+ charging: String,
+ discharging: String,
+ unknown: String,
+}
+
+struct PromptIdentity {}
+
+struct PromptTime {
+ braces: String,
+}
+
+struct PromptCommandError {}
+
+struct PromptPrompt {
+ user: String,
+ root: String,
+}
+
+#[derive(Debug)]
+pub struct PromptData {
+ pub shell: colors::ShellType,
+ pub error_code: u8,
+ pub hostname: Option<String>,
+ pub terminal_cols: Option<usize>,
+ pub pwd: Option<std::path::PathBuf>,
+ pub home: Option<std::path::PathBuf>,
+ pub user: Option<String>,
+ pub is_root: bool,
+ pub time: chrono::DateTime<chrono::Local>,
+ pub power_info: power::PowerInfo,
+}
+
+impl Prompt {
+ pub fn new(data: PromptData) -> Prompt {
+ let shell = data.shell.clone();
+ let components_line1: Vec<Box<PromptComponent>> = vec![
+ Box::new(PromptPath { braces: "()".to_string() }),
+ Box::new(PromptBorder { border: "-".to_string() }),
+ Box::new(PromptBattery {
+ length: 10,
+ braces: "{}".to_string(),
+ full: "=".to_string(),
+ empty: "-".to_string(),
+ charging: "<".to_string(),
+ discharging: ">".to_string(),
+ unknown: "?".to_string(),
+ }),
+ Box::new(PromptIdentity {}),
+ Box::new(PromptTime { braces: "[]".to_string() }),
+ ];
+ let components_line2: Vec<Box<PromptComponent>> = vec![
+ Box::new(PromptCommandError {}),
+ Box::new(PromptPrompt {
+ user: "$".to_string(),
+ root: "#".to_string(),
+ }),
+ ];
+
+ Prompt {
+ components_line1: components_line1,
+ components_line2: components_line2,
+ data: data,
+ colors: colors::Colors::new(shell),
+ }
+ }
+
+ pub fn display(&self) {
+ self.colors.pad(1);
+ for component in self.components_line1.iter() {
+ component.display(&self.colors, &self.data);
+ self.colors.pad(1);
+ }
+
+ self.colors.newline();
+
+ for component in self.components_line2.iter() {
+ component.display(&self.colors, &self.data);
+ self.colors.pad(1);
+ }
+ }
+}
+
+impl PromptComponent for PromptPath {
+ fn display(&self, colors: &colors::Colors, data: &PromptData) {
+ colors.print_host(
+ &data.hostname,
+ &self.braces[0..1]
+ );
+ if let Some(ref pwd) = data.pwd {
+ colors.print(
+ "default",
+ &compress_path(pwd, &data.home, 20)
+ );
+ }
+ else {
+ colors.print("error", "???");
+ }
+ colors.print_host(
+ &data.hostname,
+ &self.braces[1..2]
+ );
+ }
+
+ fn length(&self, data: &PromptData) -> usize {
+ 0
+ }
+}
+
+impl PromptComponent for PromptBorder {
+ fn display(&self, colors: &colors::Colors, data: &PromptData) {
+ colors.print("default", &self.border.repeat(20))
+ }
+
+ fn length(&self, data: &PromptData) -> usize {
+ 0
+ }
+}
+
+impl PromptComponent for PromptBattery {
+ fn display(&self, colors: &colors::Colors, data: &PromptData) {
+ colors.print_host(
+ &data.hostname,
+ &self.braces[0..1]
+ );
+
+ if let Some(usage) = data.power_info.battery_usage() {
+ let color = if usage >= 0.8 {
+ "battery_full"
+ }
+ else if data.power_info.charging() {
+ "default"
+ }
+ else if usage >= 0.4 {
+ "default"
+ }
+ else if usage >= 0.15 {
+ "battery_warn"
+ }
+ else if usage >= 0.05 {
+ "battery_crit"
+ }
+ else {
+ "battery_emerg"
+ };
+ let filled = (self.length as f64 * usage).ceil() as usize;
+ let unfilled = self.length - filled;
+
+ if unfilled > 0 {
+ colors.print(
+ color,
+ &self.empty.repeat(unfilled)
+ );
+ }
+
+ if data.power_info.charging() {
+ colors.print(
+ color,
+ &self.charging
+ )
+ }
+ else {
+ colors.print(
+ color,
+ &self.discharging
+ )
+ }
+
+ if filled > 1 {
+ colors.print(
+ color,
+ &self.full.repeat(filled - 1)
+ )
+ }
+ }
+ else {
+ colors.print(
+ "battery_emerg",
+ &self.unknown.repeat(self.length)
+ )
+ }
+
+ colors.print_host(
+ &data.hostname,
+ &self.braces[1..2]
+ );
+ }
+
+ fn length(&self, data: &PromptData) -> usize {
+ 0
+ }
+}
+
+impl PromptComponent for PromptIdentity {
+ fn display(&self, colors: &colors::Colors, data: &PromptData) {
+ colors.print_user(
+ &data.user,
+ &data.user.clone().unwrap_or("???".to_string())
+ );
+ colors.print("default", "@");
+ colors.print_host(
+ &data.hostname,
+ &data.hostname.clone().unwrap_or("???".to_string())
+ );
+ }
+
+ fn length(&self, data: &PromptData) -> usize {
+ 0
+ }
+}
+
+impl PromptComponent for PromptTime {
+ fn display(&self, colors: &colors::Colors, data: &PromptData) {
+ colors.print_host(
+ &data.hostname,
+ &self.braces[0..1]
+ );
+
+ colors.print(
+ "default",
+ &format!("{}", data.time.format("%H:%M:%S"))
+ );
+
+ colors.print_host(
+ &data.hostname,
+ &self.braces[1..2]
+ );
+ }
+
+ fn length(&self, data: &PromptData) -> usize {
+ 0
+ }
+}
+
+impl PromptComponent for PromptCommandError {
+ fn display(&self, colors: &colors::Colors, data: &PromptData) {
+ let color = if data.error_code == 0 {
+ "default"
+ }
+ else {
+ "error"
+ };
+ colors.print(color, &format!("{:03}", data.error_code));
+ }
+
+ fn length(&self, data: &PromptData) -> usize {
+ 0
+ }
+}
+
+impl PromptComponent for PromptPrompt {
+ fn display(&self, colors: &colors::Colors, data: &PromptData) {
+ let prompt = if data.is_root {
+ &self.root
+ }
+ else {
+ &self.user
+ };
+ colors.print_user(&data.user, prompt);
+ }
+
+ fn length(&self, data: &PromptData) -> usize {
+ 0
+ }
+}
+
+fn compress_path<T: AsRef<std::path::Path>, U: AsRef<std::path::Path>>(path: T, home: &Option<U>, max_len: u16) -> String {
+ let path_str = path.as_ref().to_string_lossy().into_owned();
+ if let &Some(ref home) = home {
+ let home_str = home.as_ref().to_string_lossy().into_owned();
+ let home_re = regex::Regex::new(&(r"^".to_string() + &regex::escape(&home_str))).unwrap();
+ home_re.replace(&path_str, "~").into_owned()
+ }
+ else {
+ path_str
+ }
+}
diff --git a/src/system_info.rs b/src/system_info.rs
new file mode 100644
index 0000000..2079ea7
--- /dev/null
+++ b/src/system_info.rs
@@ -0,0 +1,46 @@
+use chrono;
+use hostname;
+use term_size;
+use std;
+use users;
+
+use power;
+
+pub fn hostname() -> Option<String> {
+ hostname::get_hostname()
+}
+
+pub fn terminal_cols() -> Option<usize> {
+ if let Some((w, _h)) = term_size::dimensions() {
+ Some(w)
+ }
+ else {
+ None
+ }
+}
+
+pub fn pwd() -> Option<std::path::PathBuf> {
+ std::env::current_dir().ok()
+}
+
+pub fn home() -> Option<std::path::PathBuf> {
+ std::env::var("HOME")
+ .map(|dir| std::path::Path::new(&dir).to_path_buf())
+ .ok()
+}
+
+pub fn user() -> Option<String> {
+ users::get_current_username()
+}
+
+pub fn is_root() -> bool {
+ users::get_current_uid() == 0
+}
+
+pub fn time() -> chrono::DateTime<chrono::Local> {
+ chrono::Local::now()
+}
+
+pub fn power_info() -> power::PowerInfo {
+ power::PowerInfo::new()
+}