├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── functions.sh ├── install.sh ├── reinstall.sh ├── screenshot.png ├── src ├── main.rs ├── parse.rs ├── print.rs └── wrap.rs └── uninstall.sh /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "dybuk" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "regex 0.1.55 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "term-painter 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "aho-corasick" 11 | version = "0.5.1" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | dependencies = [ 14 | "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 15 | ] 16 | 17 | [[package]] 18 | name = "kernel32-sys" 19 | version = "0.2.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | dependencies = [ 22 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "libc" 28 | version = "0.2.7" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | 31 | [[package]] 32 | name = "memchr" 33 | version = "0.1.10" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | dependencies = [ 36 | "libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 37 | ] 38 | 39 | [[package]] 40 | name = "regex" 41 | version = "0.1.55" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | dependencies = [ 44 | "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 45 | "memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 46 | "regex-syntax 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 47 | "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 48 | ] 49 | 50 | [[package]] 51 | name = "regex-syntax" 52 | version = "0.2.5" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | 55 | [[package]] 56 | name = "term" 57 | version = "0.4.4" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | dependencies = [ 60 | "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 62 | ] 63 | 64 | [[package]] 65 | name = "term-painter" 66 | version = "0.2.2" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | dependencies = [ 69 | "term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 70 | ] 71 | 72 | [[package]] 73 | name = "utf8-ranges" 74 | version = "0.1.3" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | 77 | [[package]] 78 | name = "winapi" 79 | version = "0.2.5" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | 82 | [[package]] 83 | name = "winapi-build" 84 | version = "0.1.1" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | 87 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dybuk" 3 | version = "0.1.0" 4 | authors = ["Ticki "] 5 | 6 | [dependencies] 7 | term-painter = "*" 8 | regex = "*" 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dybuk 2 | Dybuk cleans up the ugly Rustc messages (inspired by Elm). Dybuk supports some other compilers aswell. 3 | 4 | 5 | 6 | ## Usage 7 | 8 | First of all install dybuk. 9 | 10 | ``` 11 | cd /path/to/dybuk 12 | cargo install --path . 13 | ``` 14 | 15 | When that's done, you can pipe Rustc output through dybuk: 16 | 17 | ``` 18 | cargo build |& dybuk 19 | ``` 20 | -------------------------------------------------------------------------------- /functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | inline() { echo -en "$*"; } 4 | log_l() { inline ":: $*\n"; } 5 | log_n() { inline "N: $*\n"; } 6 | log_w() { inline "W: $*\n"; } 7 | log_e() { inline "E: $*\n"; } 1>&2 8 | 9 | function check-privileges() { 10 | [ "$EUID" = "0" ] 11 | return "$?" 12 | } 13 | 14 | function ask_yesno() { 15 | local READ_RESULT="" 16 | while [[ "$READ_RESULT" != "y" && "$READ_RESULT" != "n" ]]; do 17 | echo -n "$1 [yn]: " 18 | read READ_RESULT 19 | [ "$READ_RESULT" ] || READ_RESULT="yn" 20 | done 21 | 22 | [ "$READ_RESULT" == "y" ] && return "$?" 23 | ! [ "$READ_RESULT" == "n" ] || return "$?" 24 | } 25 | 26 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function config() { 4 | cd "$(dirname "$BASH_SOURCE")" 5 | 6 | [ "$DEBUG" ] && set -x 7 | source "functions.sh" 8 | 9 | : ${INSTALL:=install -Dm u=rwx,g=rx,o=rx} 10 | : ${BUILD:=cargo build --release} 11 | } 12 | 13 | function build() { 14 | log_n "Build command: \`$BUILD\`" 15 | 16 | $BUILD 17 | return "$?" 18 | } 19 | 20 | function try_to_install() { 21 | log_l "Install \`$1\` into \`$2\`" 22 | if ! [ "$EUID" = "0" ]; then 23 | "$(which sudo)" $INSTALL "$1" "$2" 24 | return "$?" 25 | fi 26 | 27 | $INSTALL "$1" "$2" 28 | return "$?" 29 | } 30 | 31 | function _install() { 32 | log_n "Install command: \`$INSTALL\`" 33 | 34 | cd "target/release" 35 | try_to_install "dybuk" "/usr/local/bin/dybuk" 36 | while read file; do 37 | try_to_install "deps/$file" "/usr/local/lib/rustlib/dybuk/$file" 38 | done < <(find deps \( -type f -or -type l \) -printf '%P\n') 39 | } 40 | 41 | function main() { 42 | config "$@" || return "$?" 43 | while shift; do :; done 44 | 45 | [[ ! -e "target/release/dybuk" || ! -e "target/release/deps" ]] 46 | case "$?" in 47 | 0) 48 | _install 49 | ;; 50 | 51 | 1) 52 | log_e "Generated files doesn't exists." 53 | if [ "$EUID" = "0" ]; then 54 | log_e "Building the project with root privileges is prohibited." 55 | return 1 56 | fi 57 | 58 | if ask_yesno "Build project"; then 59 | build && _install 60 | return "$?" 61 | fi 62 | ;; 63 | esac 64 | } 65 | 66 | main "$@" 67 | exit "$?" 68 | 69 | -------------------------------------------------------------------------------- /reinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function config() { 4 | set -e 5 | cd "$(dirname "$BASH_SOURCE")" 6 | 7 | source "functions.sh" 8 | return "$?" 9 | } 10 | 11 | function main() { 12 | config "$@" || return "$?" 13 | while shift; do :; done 14 | 15 | if check-privileges; then 16 | ./uninstall.sh 17 | ./install.sh 18 | else 19 | "$(which sudo)" ./uninstall.sh 20 | "$(which sudo)" ./install.sh 21 | fi 22 | } 23 | 24 | main "$@" 25 | exit "$?" 26 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ticki/dybuk/ef12906db51f30a1b45d3d69a3dd2b0bd9a4eb9a/screenshot.png -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate term_painter; 2 | extern crate regex; 3 | 4 | mod parse; 5 | mod print; 6 | mod wrap; 7 | 8 | use term_painter::ToStyle; 9 | use term_painter::Color::*; 10 | 11 | 12 | fn main() { 13 | let mut op = parse::MessageIter::new(); 14 | 15 | for i in &mut op { 16 | for l in i { 17 | l.print(); 18 | } 19 | } 20 | 21 | println!("\n~~~ {} : {} ~~~", Red.bold().paint(op.errors.to_string()), Yellow.bold().paint(op.warnings.to_string())); 22 | } 23 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use std::io::stdin; 2 | use std::io::prelude::*; 3 | use std::iter::once; 4 | use regex::Regex; 5 | 6 | pub struct MessageIter { 7 | buf: String, 8 | terminated: bool, 9 | pub errors: u16, 10 | pub warnings: u16, 11 | } 12 | 13 | pub enum Message { 14 | Header(String, String), 15 | Warning(String), 16 | Note(String), 17 | Error(String), 18 | Help(String), 19 | FollowUp(String), 20 | Source(String, String), 21 | Etc, 22 | Marker(String), 23 | NewLine, 24 | Wat, 25 | Aborting, 26 | } 27 | 28 | impl<'a> Iterator for &'a mut MessageIter { 29 | type Item = Vec; 30 | 31 | fn next(&mut self) -> Option> { 32 | use self::Message::*; 33 | 34 | if self.terminated { 35 | return None; 36 | } 37 | 38 | let mut res = Vec::new(); 39 | let mut file = String::new(); 40 | let mut stop = false; 41 | let si = stdin(); 42 | let stdin = si.lock().lines().map(|x| x.expect("Stdin failed")); 43 | 44 | for l in once(self.buf.clone()).chain(stdin) { 45 | let re_header = Regex::new(r"([0-9A-Za-z_\.\\/>< ]+):(\d+):\d+: .*(warning: |note: |error: |help: )(.*)").unwrap(); 46 | let re_source = Regex::new(r"(\d+) (.*)").unwrap(); 47 | if re_header.is_match(&l) { 48 | 49 | if !stop { 50 | stop = true; 51 | } else { 52 | self.buf = l.to_string(); 53 | return Some(res); 54 | } 55 | 56 | res.push(NewLine); 57 | 58 | let caps = re_header.captures(&l).unwrap(); 59 | file = caps.at(1).unwrap_or("?").to_string(); 60 | 61 | res.push(Header(file.clone(), caps.at(2).unwrap_or("?").to_string())); 62 | 63 | let msg = caps.at(4).unwrap_or("?").to_string(); 64 | 65 | // Warning, header or note? 66 | match caps.at(3).unwrap_or("?") { 67 | "warning: " => { 68 | self.warnings += 1; 69 | res.push(Warning(msg)); 70 | }, 71 | "note: " => res.push(Note(msg)), 72 | "error: " => { 73 | self.errors += 1; 74 | res.push(Error(msg)); 75 | }, 76 | "help: " => res.push(Help(msg)), 77 | _ => res.push(Wat), 78 | } 79 | 80 | } else if l.len() > file.len() && re_source.is_match(&l[file.len()..]) && is_not_cmd(&l) { 81 | let caps = re_source.captures(&l).unwrap(); 82 | 83 | res.push(Source(caps.at(1).unwrap_or("?").to_string(), caps.at(2).unwrap_or("????").to_string())); 84 | } else if l.starts_with(' ') && l.contains("^") { 85 | 86 | let offset = file.len() - 4; //+ 5 - 5; 87 | 88 | if offset < l.len() { 89 | res.push(Marker(l[offset..].to_string())); 90 | } 91 | } else if l.contains("aborting due to previous") || l.contains("Build failed") || l.contains("Could not compile") { 92 | res.push(Aborting); 93 | stop = true; 94 | self.terminated = true; 95 | } else if l.contains("Compilining ") || l.contains("file:///home/") || l.is_empty() { 96 | // todo 97 | } else if l.contains(" ...") { 98 | res.push(Etc); 99 | } else if is_not_cmd(&l){ 100 | res.push(FollowUp(l.to_string())); 101 | } 102 | } 103 | 104 | self.terminated = true; 105 | if stop { 106 | Some(res) 107 | } else { 108 | None 109 | } 110 | } 111 | } 112 | 113 | fn is_not_cmd(l: &str) -> bool { 114 | l.len() < 30 || !(l.starts_with("rustc ") || l.starts_with("cargo ") || l.starts_with("make ") || l.contains(" --") || l.contains(" --target=") || l.contains(" -C ") || l.contains(" -L ") || l.contains(" -A ") || l.contains(" -Z ") || l.contains(" -o ") || l.starts_with("sed ") || l.starts_with("mkdir ") || l.starts_with("cd ")) 115 | } 116 | 117 | impl MessageIter { 118 | pub fn new() -> Self { 119 | MessageIter { 120 | buf: String::new(), 121 | terminated: false, 122 | errors: 0, 123 | warnings: 0, 124 | } 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /src/print.rs: -------------------------------------------------------------------------------- 1 | use term_painter::ToStyle; 2 | use term_painter::Color::*; 3 | use term_painter::Attr::*; 4 | 5 | use parse::Message; 6 | use parse::Message::*; 7 | use wrap::wrap_msg; 8 | 9 | impl Message { 10 | 11 | pub fn print(self) { 12 | 13 | match self { 14 | Header(ref file, ref line) => println!("+---- {} : {} ----+", Blue.bold().paint(file), Blue.paint(line)), 15 | Warning(warn) => println!(" =====> {}{}", Yellow.bold().paint("warning: "), Bold.paint(&wrap_msg(warn, 9))), 16 | Note(note) => println!(" =====> {}{}", Green.bold().paint("note: "), Bold.paint(&wrap_msg(note, 6))), 17 | Error(err) => println!(" =====> {}{}", Red.bold().paint("error: "), Bold.paint(&wrap_msg(err, 7))), 18 | Help(err) => println!(" =====> {}{}", Green.bold().paint("help: "), Bold.paint(&wrap_msg(err, 6))), 19 | FollowUp(msg) => println!(" > {}", Bold.paint(msg)), 20 | Source(line, code) => println!(" {} {}", Magenta.paint(format!("{} |>", line)), code), 21 | Etc => println!(" {}", Magenta.paint("...")), 22 | Marker(ref mrk) => println!("{}", Yellow.paint(mrk)), 23 | NewLine => println!("\n"), 24 | Wat => println!("Dafuq?"), 25 | Aborting => println!("\n{}", Red.paint("Aborting due to previous errors")), 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/wrap.rs: -------------------------------------------------------------------------------- 1 | use std::iter::repeat; 2 | 3 | pub fn wrap_msg(s: String, n: usize) -> String { 4 | let split = s.split(' ').collect::>(); 5 | let mut chunks = split.chunks(8).map(|x| x.join(" ")); 6 | 7 | chunks.next().unwrap_or("?".to_string()) + &chunks.map(|ref x| { 8 | 9 | "\n > ".to_string() + &repeat(" ").take(n) 10 | .collect::() 11 | + x 12 | 13 | }).collect::() 14 | } 15 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function config() { 4 | cd "$(dirname "$BASH_SOURCE")" 5 | source "functions.sh" 6 | return "$?" 7 | } 8 | 9 | function rm_custom_format() { 10 | perl -pe "s/^r(.+) .(.+).$/:: R\1 '\2 '\./" 11 | } 12 | 13 | function main() { 14 | config "$@" || return "$?" 15 | while shift; do :; done 16 | 17 | if check-privileges; then 18 | rm -rvf /usr/local/bin/dybuk | rm_custom_format 19 | rm -rfv /usr/local/lib/rustlib/dybuk | rm_custom_format 20 | else 21 | exec "$(which "sudo")" "./$(basename "$BASH_SOURCE")" 22 | fi 23 | } 24 | 25 | main "$@" 26 | exit "$?" 27 | 28 | --------------------------------------------------------------------------------