From 0b3404a16019757c891b153485f0583c49f7089f Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 19 Feb 2018 16:10:02 -0500 Subject: start sketching out some things --- .gitignore | 3 + Cargo.lock | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 +++ src/colors.rs | 126 +++++++++++++++++++ src/main.rs | 54 ++++++++ src/power.rs | 137 +++++++++++++++++++++ src/prompt.rs | 310 ++++++++++++++++++++++++++++++++++++++++++++++ src/system_info.rs | 46 +++++++ 8 files changed, 1045 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/colors.rs create mode 100644 src/main.rs create mode 100644 src/power.rs create mode 100644 src/prompt.rs create mode 100644 src/system_info.rs 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 "] + +[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, + 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, 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, 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(&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, +} + +#[derive(Debug,Clone)] +struct PowerSupplyInfo { + name: String, + ty: PowerSupplyType, + energy_now: Option, + energy_full: Option, + online: Option, +} + +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 { + 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 { + self.power_supplies + .iter() + .cloned() + .filter(|p| p.ty == PowerSupplyType::Battery) + .collect() + } + + fn mains(&self) -> Vec { + 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(path: U) -> Option + where T: std::str::FromStr, + U: AsRef +{ + 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>, + components_line2: Vec>, + 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, + pub terminal_cols: Option, + pub pwd: Option, + pub home: Option, + pub user: Option, + pub is_root: bool, + pub time: chrono::DateTime, + pub power_info: power::PowerInfo, +} + +impl Prompt { + pub fn new(data: PromptData) -> Prompt { + let shell = data.shell.clone(); + let components_line1: Vec> = 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> = 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, U: AsRef>(path: T, home: &Option, 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() + ®ex::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 { + hostname::get_hostname() +} + +pub fn terminal_cols() -> Option { + if let Some((w, _h)) = term_size::dimensions() { + Some(w) + } + else { + None + } +} + +pub fn pwd() -> Option { + std::env::current_dir().ok() +} + +pub fn home() -> Option { + std::env::var("HOME") + .map(|dir| std::path::Path::new(&dir).to_path_buf()) + .ok() +} + +pub fn user() -> Option { + users::get_current_username() +} + +pub fn is_root() -> bool { + users::get_current_uid() == 0 +} + +pub fn time() -> chrono::DateTime { + chrono::Local::now() +} + +pub fn power_info() -> power::PowerInfo { + power::PowerInfo::new() +} -- cgit v1.2.3-54-g00ecf