#[macro_use] extern crate lazy_static;
extern crate libc;
extern crate term;
extern crate term_size;
use std::any::Any;
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::process::{Command, Stdio};
use std::str::from_utf8_unchecked;
use std::sync::Mutex;
pub fn filename<'a> (path: &'a str) -> &'a str {
match Path::new (path) .file_name() {
Some (filename) => match filename.to_str() {
Some (fstr) => if fstr.ends_with (".rs") {&fstr[0 .. fstr.len() - 3]} else {fstr},
None => path},
None => path}}
mod gstuff {pub fn filename<'a> (path: &'a str) -> &'a str {super::filename (path)}}
#[macro_export] macro_rules! try_s {
($e: expr) => {match $e {Ok (ok) => ok, Err (err) => {return Err (format! ("{}:{}] {}", ::gstuff::filename (file!()), line!(), err));}}}}
#[macro_export] macro_rules! try_f {
($e: expr) => {match $e {
Ok (ok) => ok,
Err (err) => {return Err (From::from (format! ("{}:{}] {}", ::gstuff::filename (file!()), line!(), err)));}}}}
#[macro_export] macro_rules! try_sp {
($e: expr) => {match $e {&Ok (ref ok) => ok,
&Err (ref err) => {return Err (From::from (format! ("{}:{}] {:?}", ::gstuff::filename (file!()), line!(), err)));}}}}
#[macro_export] macro_rules! try_fu {
($e: expr) => {match $e {
Ok (ok) => ok,
Err (err) => {return Box::new (futures::future::err (From::from (err)))}}}}
#[macro_export] macro_rules! try_fus {
($e: expr) => {match $e {
Ok (ok) => ok,
Err (err) => {return Box::new (futures::future::err (ERRL! ("{}", err)))}}}}
#[macro_export] macro_rules! ERRL {
($format: expr, $($args: tt)+) => {format! (concat! ("{}:{}] ", $format), ::gstuff::filename (file!()), line!(), $($args)+)};
($format: expr) => {format! (concat! ("{}:{}] ", $format), ::gstuff::filename (file!()), line!())}}
#[macro_export] macro_rules! ERR {
($format: expr, $($args: tt)+) => {Err (ERRL! ($format, $($args)+))};
($format: expr) => {Err (ERRL! ($format))}}
lazy_static! {
static ref STATUS_LINE: Mutex<String> = Mutex::new (String::new());
pub static ref ISATTY: bool = unsafe {libc::isatty (1)} != 0;}
pub fn status_line (file: &str, line: u32, status: String) {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
if let Some (mut stdout) = term::stdout() {
if let Ok (mut status_line) = STATUS_LINE.lock() {
let old_hash = {let mut hasher = DefaultHasher::new(); hasher.write (status_line.as_bytes()); hasher.finish()};
status_line.clear();
use std::fmt::Write;
let _ = write! (&mut *status_line, "{}:{}] {}", filename (file), line, status);
let new_hash = {let mut hasher = DefaultHasher::new(); hasher.write (status_line.as_bytes()); hasher.finish()};
if old_hash != new_hash {
match term_size::dimensions() {
Some ((w, _)) if status_line.chars().count() >= w => {
let mut tmp = String::with_capacity (w - 1);
for ch in status_line.chars().take (w - 1) {tmp.push (ch)}
let _ = stdout.get_mut().write (tmp.as_bytes());},
_ => {let _ = stdout.get_mut().write (status_line.as_bytes());}};
let _ = stdout.get_mut().write (b"\x1B[K");
let _ = stdout.carriage_return();
let _ = stdout.get_mut().flush();}}}}
pub fn status_line_clear() {
if let Ok (mut status_line) = STATUS_LINE.lock() {
if *ISATTY && !status_line.is_empty() {
if let Some (mut stdout) = term::stdout() {
status_line.clear();
let _ = stdout.get_mut().write (b"\x1B[K"); let _ = stdout.get_mut().flush();}}}}
pub fn with_status_line (code: &Fn()) {
if let Ok (status_line) = STATUS_LINE.lock() {
if !*ISATTY || status_line.is_empty() {
code()
} else if let Some (mut stdout) = term::stdout() {
let _ = stdout.get_mut().write (b"\x1B[K"); let _ = stdout.get_mut().flush(); code();
let _ = stdout.get_mut().write (status_line.as_bytes());
let _ = stdout.carriage_return();
let _ = stdout.get_mut().flush();}}}
#[test] fn test_status_line() {
with_status_line (&|| println! ("hello world"));}
#[macro_export] macro_rules! gstring {($array: ident, $code: block) => {{
let end = {
let mut $array = ::std::io::Cursor::new (&mut $array[..]);
let $array = &mut $array;
$code;
$array.position() as usize};
unsafe {::std::str::from_utf8_unchecked (&$array[0..end])}}}}
pub fn netstring (at: &[u8]) -> Result<(&[u8], &[u8]), String> {
let length_end = match at.iter().position (|&ch| ch < b'0' || ch > b'9') {Some (l) if l > 0 => l, _ => return ERR! ("No len.")};
match at.get (length_end) {Some (&ch) if ch == b':' => (), _ => return ERR! ("No colon.")};
let length = unsafe {from_utf8_unchecked (&at[0 .. length_end])};
let length: usize = try_s! (length.parse());
let bulk_pos = 0 + length_end + 1;
let bulk_end = bulk_pos + length;
match at.get (bulk_end) {Some (&ch) if ch == b',' => (), _ => return ERR! ("No comma.")}
Ok ((&at[bulk_pos .. bulk_end], &at[bulk_end + 1 ..]))}
#[cfg(unix)]
pub fn with_hostname (visitor: &mut FnMut (&[u8])) -> Result<(), std::io::Error> {
use libc::{size_t, gethostname}; use std::ffi::CStr;
use std::io;
let mut buf = [0; 128];
let rc = unsafe {gethostname (buf.as_mut_ptr(), (buf.len() - 1) as size_t)};
if rc == 0 {
let cs = unsafe {CStr::from_ptr (buf.as_ptr())};
Ok (visitor (cs.to_bytes()))
} else {
Err (io::Error::last_os_error())}}
#[cfg(unix)] #[test] fn test_hostname() {
let mut hostname = String::new();
with_hostname (&mut |bytes| hostname = String::from_utf8_lossy (bytes) .into_owned()) .unwrap();}
pub fn slurp (path: &AsRef<Path>) -> Vec<u8> {
let mut file = match fs::File::open (path) {
Ok (f) => f,
Err (ref err) if err.kind() == io::ErrorKind::NotFound => return Vec::new(),
Err (err) => panic! ("Can't open {:?}: {}", path.as_ref(), err)};
let mut buf = Vec::new();
file.read_to_end (&mut buf) .expect ("!read");
buf}
pub fn slurp_prog (command: &str) -> Result<String, String> {
let output = match Command::new ("dash") .arg ("-c") .arg (command) .output() {
Ok (output) => output,
Err (ref err) if err.kind() == io::ErrorKind::NotFound => { try_s! (Command::new ("sh") .arg ("-c") .arg (command) .output())},
Err (err) => return ERR! ("{}", err)};
let combined_output: String = if output.stderr.is_empty() {
try_s! (String::from_utf8 (output.stdout))
} else if output.stdout.is_empty() {
try_s! (String::from_utf8 (output.stderr))
} else {
let mut buf = String::with_capacity (output.stderr.len() + output.stdout.len());
buf.push_str (try_s! (std::str::from_utf8 (&output.stderr[..])));
buf.push_str (try_s! (std::str::from_utf8 (&output.stdout[..])));
buf};
if output.status.success() {Ok (combined_output)} else {Err (combined_output)}}
#[test] fn test_slurp_prog() {
let foo = match slurp_prog ("echo foo") {Ok (foo) => foo, Err (err) => panic! ("{}", err)};
assert_eq! (foo.trim(), "foo");}
pub fn cmd (cmd: &str) -> Result<(), String> {
println! ("$ {}", cmd);
let status = try_s! (Command::new ("dash") .arg ("-c") .arg (cmd) .stdout (Stdio::inherit()) .stderr (Stdio::inherit()) .status());
if !status.success() {Err (format! ("Command returned an error status: {}", status))} else {Ok(())}}
pub fn any_to_str<'a> (message: &'a Any) -> Option<&'a str> {
if let Some (message) = message.downcast_ref::<&str>() {return Some (message)}
if let Some (message) = message.downcast_ref::<String>() {return Some (&message[..])}
return None}
#[macro_export]
macro_rules! take_until_parse_s (
($i: expr, $remainder: ident! ($($args:tt)*)) => ({
let input: &str = $i;
let mut ret = IResult::Error (error_position! (nom::ErrorKind::Custom (0), input));
for (pos, _) in $i.char_indices() {
match $remainder! (&input[pos..], $($args)*) {
IResult::Done (i,o) => {ret = IResult::Done (i, (&input[0..pos], o)); break}, IResult::Error(_) => continue, IResult::Incomplete(_) => continue}}
if !ret.is_done() {
if let IResult::Done (i,o) = $remainder! ("", $($args)*) {
ret = IResult::Done (i, (input, o))}}
ret});
($i: expr, $f: expr) => (take_until_parse_s! ($i, call! ($f));););
#[macro_export]
macro_rules! parse_replace_s {
($i: expr, $submac: ident! ($($args:tt)*)) => ({
let input: &str = $i;
let mut output = String::with_capacity (input.len() * 2 + 32);
let mut pos = input;
while let IResult::Done (tail, (head, evaluation)) = take_until_parse_s! (pos, $submac! ($($args)*)) {
output.push_str (head);
output.push_str (&evaluation);
pos = tail;}
output.push_str (pos);
output});
($i: expr, $f: expr) => (parse_replace_s! ($i, call! ($f)););}
#[macro_export]
macro_rules! take_until_find_parse_s (
($i: expr, $starts: expr, $remainder: ident! ($($args:tt)*)) => ({
let input: &str = $i;
let mut ret = IResult::Error (error_position! (nom::ErrorKind::Custom (0), input));
for (pos, _) in $i.match_indices ($starts) {
match $remainder! (&input[pos..], $($args)*) {
IResult::Done (i,o) => {ret = IResult::Done (i, (&input[0..pos], o)); break}, IResult::Error(_) => continue, IResult::Incomplete(_) => continue}} ret});
($i: expr, $starts: expr, $f: expr) => (take_until_find_parse_s! ($i, $starts, call! ($f));););
#[macro_export]
macro_rules! find_parse_replace_s {
($i: expr, $starts: expr, $submac: ident! ($($args:tt)*)) => ({
let input: &str = $i;
let mut output = String::with_capacity (input.len() * 2 + 32);
let mut pos = input;
while let IResult::Done (tail, (head, evaluation)) =
take_until_find_parse_s! (pos, $starts, $submac! ($($args)*)) {
output.push_str (head);
output.push_str (&evaluation);
pos = tail;}
output.push_str (pos);
output});
($i: expr, $starts: expr, $f: expr) => (find_parse_replace_s! ($i, $starts, call! ($f)););}