<commit_before>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;

use std::default::Default;

use rustbox::{Color, RustBox, Key};
use rustc_serialize::json;
use rustc_serialize::json::Json;

use hkg::utility::cache;
use hkg::model::ListTopicItem;
use hkg::model::ShowItem;

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let title = String::from("高登");
    let s = cache::readfile(String::from("topics.json"));
    let collection: Vec<ListTopicItem> = json::decode(&s).unwrap();

    let t = cache::readfile(String::from("view.json"));
    let item: ShowItem = json::decode(&t).unwrap();

    let mut status = String::from("> ");

    let mut list = hkg::screen::list::List::new(&rustbox);
    let mut show = hkg::screen::show::Show::new(&rustbox);

    loop {

        show.print(&title, &item);

        // list.print(&title, &collection);

        print_status(&rustbox, &status);

        // show UI
        rustbox.present();

        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => {
                        break;
                    }

                    Key::Up => {
                        let w = rustbox.width();
                        status = format_status(status, w, "U");
                        let tmp = list.get_selected_topic();
                        if tmp > 1 {
                            list.select_topic( tmp - 1 );
                        }
                    }
                    Key::Down => {
                        let w = rustbox.width();
                        status = format_status(status, w, "D");
                        let tmp = list.get_selected_topic();
                        if tmp < list.body_height() {
                            list.select_topic( tmp + 1 );
                        }
                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_status(rustbox : &rustbox::RustBox, status: &str)
{
    // for status bar only
    let w = rustbox.width();
    let h = rustbox.height();

    let status_width = if w > status.len() {
        w - status.len()
    } else {
        0
    };
    let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

    rustbox.print(0,
                  h - 1,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  &format!("{status}{status_spacing}",
                           status = status,
                           status_spacing = status_spacing));

}

fn format_status(status: String, w: usize, s: &str) -> String
{
    if status.len() >= w {
        String::from(format!("{}{}", &"> ", s))
    } else {
        String::from(format!("{}{}", &status, s))
    }
}

// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<commit_msg>date time operation example<commit_after>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;
extern crate chrono;

use std::default::Default;

use rustbox::{Color, RustBox, Key};
use rustc_serialize::json;
use rustc_serialize::json::Json;

use chrono::*;

use hkg::utility::cache;
use hkg::model::ListTopicItem;
use hkg::model::ShowItem;

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let title = String::from("高登");
    let s = cache::readfile(String::from("topics.json"));
    let collection: Vec<ListTopicItem> = json::decode(&s).unwrap();

    let t = cache::readfile(String::from("view.json"));
    let item: ShowItem = json::decode(&t).unwrap();

    let mut status = String::from("> ");

    let mut list = hkg::screen::list::List::new(&rustbox);
    let mut show = hkg::screen::show::Show::new(&rustbox);

    loop {

        date_operation_example(&rustbox);

        // show.print(&title, &item);

        // list.print(&title, &collection);

        print_status(&rustbox, &status);

        // show UI
        rustbox.present();

        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => {
                        break;
                    }

                    Key::Up => {
                        let w = rustbox.width();
                        status = format_status(status, w, "U");
                        let tmp = list.get_selected_topic();
                        if tmp > 1 {
                            list.select_topic( tmp - 1 );
                        }
                    }
                    Key::Down => {
                        let w = rustbox.width();
                        status = format_status(status, w, "D");
                        let tmp = list.get_selected_topic();
                        if tmp < list.body_height() {
                            list.select_topic( tmp + 1 );
                        }
                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_status(rustbox : &rustbox::RustBox, status: &str)
{
    // for status bar only
    let w = rustbox.width();
    let h = rustbox.height();

    let status_width = if w > status.len() {
        w - status.len()
    } else {
        0
    };
    let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

    rustbox.print(0,
                  h - 1,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  &format!("{status}{status_spacing}",
                           status = status,
                           status_spacing = status_spacing));

}

fn format_status(status: String, w: usize, s: &str) -> String
{
    if status.len() >= w {
        String::from(format!("{}{}", &"> ", s))
    } else {
        String::from(format!("{}{}", &status, s))
    }
}

fn date_operation_example(rustbox: &rustbox::RustBox){
    let now = Local::now();

    let dt1 = match Local.datetime_from_str("30/4/2016 9:22", "%d/%m/%Y %H:%M") {
        Ok(v) => v,
        Err(e) => Local::now(),
    };

    let dt2 = now.checked_sub(Duration::seconds(30)).unwrap();
    let diff =  (now - dt2).num_minutes();
    rustbox.print(0,
                  0,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  &format!("{:?} {:?} {:?}", dt2, now, diff));

}
// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<|endoftext|>


<commit_before>#[cfg(windows)]
extern crate winapi;

extern crate libloading;
use libloading::{Symbol, Library};

const LIBPATH: &'static str = "target/libtest_helpers.module";

fn make_helpers() {
    static ONCE: ::std::sync::Once = ::std::sync::Once::new();
    ONCE.call_once(|| {
        let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| { "rustc".into() });
        let mut cmd = ::std::process::Command::new(rustc);
        cmd
            .arg("src/test_helpers.rs")
            .arg("-o")
            .arg(LIBPATH);
        if let Some(target) = std::env::var_os("TARGET") {
            cmd.arg("--target").arg(target);
        } else {
            eprintln!("WARNING: $TARGET NOT SPECIFIED! BUILDING HELPER MODULE FOR NATIVE TARGET.");
        }
        assert!(cmd
            .status()
            .expect("could not compile the test helpers!")
            .success()
        );
    });
}

#[test]
fn test_id_u32() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        let f: Symbol<unsafe extern fn(u32) -> u32> = lib.get(b"test_identity_u32\0").unwrap();
        assert_eq!(42, f(42));
    }
}

#[repr(C)]
#[derive(Clone,Copy,PartialEq,Debug)]
struct S {
    a: u64,
    b: u32,
    c: u16,
    d: u8
}

#[test]
fn test_id_struct() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        assert_eq!(S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 }));
    }
}

#[test]
fn test_0_no_0() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        let f2: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct").unwrap();
        assert_eq!(*f, *f2);
    }
}

#[test]
fn wrong_name_fails() {
    unsafe {
        Library::new("target/this_location_is_definitely_non existent:^~").err().unwrap();
    }
}

#[test]
fn missing_symbol_fails() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap();
    }
}

#[test]
fn interior_null_fails() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test\0_does_not_exist\0").err().unwrap();
    }
}

#[test]
fn test_incompatible_type() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        assert!(match lib.get::<()>(b"test_identity_u32\0") {
           Err(libloading::Error::IncompatibleSize) => true,
           _ => false,
        })
    }
}

#[test]
fn test_incompatible_type_named_fn() {
    make_helpers();
    unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result<Symbol<'a, T>, libloading::Error> {
        l.get::<T>(b"test_identity_u32\0")
    }
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        assert!(match get(&lib, test_incompatible_type_named_fn) {
           Err(libloading::Error::IncompatibleSize) => true,
           _ => false,
        })
    }
}

#[test]
fn test_static_u32() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap();
        **var = 42;
        let help: Symbol<unsafe extern fn() -> u32> = lib.get(b"test_get_static_u32\0").unwrap();
        assert_eq!(42, help());
    }
}

#[test]
fn test_static_ptr() {
    make_helpers();
    unsafe {
        let lib = Library::new(LIBPATH).unwrap();
        let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap();
        **var = *var as *mut _;
        let works: Symbol<unsafe extern fn() -> bool> =
            lib.get(b"test_check_static_ptr\0").unwrap();
        assert!(works());
    }
}

#[test]
// Something about i686-pc-windows-gnu, makes dll initialisation code call abort when it is loaded
// and unloaded many times. So far it seems like an issue with mingw, not libloading, so ignoring
// the target. Especially since it is very unlikely to be fixed given the state of support its
// support.
#[cfg(not(all(target_arch="x86", target_os="windows", target_env="gnu")))]
fn manual_close_many_times() {
    make_helpers();
    let join_handles: Vec<_> = (0..16).map(|_| {
        std::thread::spawn(|| unsafe {
            for _ in 0..10000 {
                let lib = Library::new(LIBPATH).expect("open library");
                let _: Symbol<unsafe extern fn(u32) -> u32> =
                    lib.get(b"test_identity_u32").expect("get fn");
                lib.close().expect("close is successful");
            }
        })
    }).collect();
    for handle in join_handles {
        handle.join().expect("thread should succeed");
    }
}

#[cfg(unix)]
#[test]
fn library_this_get() {
    use libloading::os::unix::Library;
    make_helpers();
    // SAFE: functions are never called
    unsafe {
        let _lib = Library::new(LIBPATH).unwrap();
        let this = Library::this();
        // Library we loaded in `_lib` (should be RTLD_LOCAL).
        assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err());
        // Something obscure from libc...
        assert!(this.get::<unsafe extern "C" fn()>(b"freopen").is_ok());
    }
}

#[cfg(windows)]
#[test]
fn library_this() {
    use libloading::os::windows::Library;
    make_helpers();
    unsafe {
        // SAFE: well-known library without initialisers is loaded.
        let _lib = Library::new(LIBPATH).unwrap();
        let this = Library::this().expect("this library");
        // SAFE: functions are never called.
        // Library we loaded in `_lib`.
        assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err());
        // Something "obscure" from kernel32...
        assert!(this.get::<unsafe extern "C" fn()>(b"GetLastError").is_err());
    }
}

#[cfg(windows)]
#[test]
fn works_getlasterror() {
    use winapi::um::errhandlingapi;
    use winapi::shared::minwindef::DWORD;
    use libloading::os::windows::{Library, Symbol};

    unsafe {
        let lib = Library::new("kernel32.dll").unwrap();
        let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError").unwrap();
        errhandlingapi::SetLastError(42);
        assert_eq!(errhandlingapi::GetLastError(), gle())
    }
}

#[cfg(windows)]
#[test]
fn works_getlasterror0() {
    use winapi::um::errhandlingapi;
    use winapi::shared::minwindef::DWORD;
    use libloading::os::windows::{Library, Symbol};

    unsafe {
        let lib = Library::new("kernel32.dll").unwrap();
        let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError\0").unwrap();
        errhandlingapi::SetLastError(42);
        assert_eq!(errhandlingapi::GetLastError(), gle())
    }
}

#[cfg(windows)]
#[test]
fn library_open_already_loaded() {
    use libloading::os::windows::Library;

    // Present on Windows systems and NOT used by any other tests to prevent races.
    const LIBPATH: &str = "Msftedit.dll";

    // Not loaded yet.
    assert!(match Library::open_already_loaded(LIBPATH) {
        Err(libloading::Error::GetModuleHandleExW { .. }) => true,
        _ => false,
    });

    unsafe {
        let _lib = Library::new(LIBPATH).unwrap();
        // Loaded now.
        assert!(Library::open_already_loaded(LIBPATH).is_ok());
    }
}
<commit_msg>Honor Cargo's target directory when building LIBPATH<commit_after>#[cfg(windows)]
extern crate winapi;

extern crate libloading;
use libloading::{Symbol, Library};

const TARGET_DIR: Option<&'static str> = option_env!("CARGO_TARGET_DIR");
const TARGET_TMPDIR: Option<&'static str> = option_env!("CARGO_TARGET_TMPDIR");
static mut LIBPATH: String = String::new();

fn lib_path() -> &'static str {
    static INIT: ::std::sync::Once = ::std::sync::Once::new();

    unsafe {
        INIT.call_once(|| {
            LIBPATH = format!(
                "{}/libtest_helpers.module",
                TARGET_TMPDIR.unwrap_or(TARGET_DIR.unwrap_or("target"))
            );
        });
        &LIBPATH
    }
}

fn make_helpers() {
    static ONCE: ::std::sync::Once = ::std::sync::Once::new();
    ONCE.call_once(|| {
        let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| { "rustc".into() });
        let mut cmd = ::std::process::Command::new(rustc);
        cmd
            .arg("src/test_helpers.rs")
            .arg("-o")
            .arg(lib_path());
        if let Some(target) = std::env::var_os("TARGET") {
            cmd.arg("--target").arg(target);
        } else {
            eprintln!("WARNING: $TARGET NOT SPECIFIED! BUILDING HELPER MODULE FOR NATIVE TARGET.");
        }
        assert!(cmd
            .status()
            .expect("could not compile the test helpers!")
            .success()
        );
    });
}

#[test]
fn test_id_u32() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        let f: Symbol<unsafe extern fn(u32) -> u32> = lib.get(b"test_identity_u32\0").unwrap();
        assert_eq!(42, f(42));
    }
}

#[repr(C)]
#[derive(Clone,Copy,PartialEq,Debug)]
struct S {
    a: u64,
    b: u32,
    c: u16,
    d: u8
}

#[test]
fn test_id_struct() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        assert_eq!(S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 }));
    }
}

#[test]
fn test_0_no_0() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        let f2: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct").unwrap();
        assert_eq!(*f, *f2);
    }
}

#[test]
fn wrong_name_fails() {
    unsafe {
        Library::new("target/this_location_is_definitely_non existent:^~").err().unwrap();
    }
}

#[test]
fn missing_symbol_fails() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap();
    }
}

#[test]
fn interior_null_fails() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test\0_does_not_exist\0").err().unwrap();
    }
}

#[test]
fn test_incompatible_type() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        assert!(match lib.get::<()>(b"test_identity_u32\0") {
           Err(libloading::Error::IncompatibleSize) => true,
           _ => false,
        })
    }
}

#[test]
fn test_incompatible_type_named_fn() {
    make_helpers();
    unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result<Symbol<'a, T>, libloading::Error> {
        l.get::<T>(b"test_identity_u32\0")
    }
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        assert!(match get(&lib, test_incompatible_type_named_fn) {
           Err(libloading::Error::IncompatibleSize) => true,
           _ => false,
        })
    }
}

#[test]
fn test_static_u32() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap();
        **var = 42;
        let help: Symbol<unsafe extern fn() -> u32> = lib.get(b"test_get_static_u32\0").unwrap();
        assert_eq!(42, help());
    }
}

#[test]
fn test_static_ptr() {
    make_helpers();
    unsafe {
        let lib = Library::new(lib_path()).unwrap();
        let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap();
        **var = *var as *mut _;
        let works: Symbol<unsafe extern fn() -> bool> =
            lib.get(b"test_check_static_ptr\0").unwrap();
        assert!(works());
    }
}

#[test]
// Something about i686-pc-windows-gnu, makes dll initialisation code call abort when it is loaded
// and unloaded many times. So far it seems like an issue with mingw, not libloading, so ignoring
// the target. Especially since it is very unlikely to be fixed given the state of support its
// support.
#[cfg(not(all(target_arch="x86", target_os="windows", target_env="gnu")))]
fn manual_close_many_times() {
    make_helpers();
    let join_handles: Vec<_> = (0..16).map(|_| {
        std::thread::spawn(|| unsafe {
            for _ in 0..10000 {
                let lib = Library::new(lib_path()).expect("open library");
                let _: Symbol<unsafe extern fn(u32) -> u32> =
                    lib.get(b"test_identity_u32").expect("get fn");
                lib.close().expect("close is successful");
            }
        })
    }).collect();
    for handle in join_handles {
        handle.join().expect("thread should succeed");
    }
}

#[cfg(unix)]
#[test]
fn library_this_get() {
    use libloading::os::unix::Library;
    make_helpers();
    // SAFE: functions are never called
    unsafe {
        let _lib = Library::new(lib_path()).unwrap();
        let this = Library::this();
        // Library we loaded in `_lib` (should be RTLD_LOCAL).
        assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err());
        // Something obscure from libc...
        assert!(this.get::<unsafe extern "C" fn()>(b"freopen").is_ok());
    }
}

#[cfg(windows)]
#[test]
fn library_this() {
    use libloading::os::windows::Library;
    make_helpers();
    unsafe {
        // SAFE: well-known library without initialisers is loaded.
        let _lib = Library::new(lib_path()).unwrap();
        let this = Library::this().expect("this library");
        // SAFE: functions are never called.
        // Library we loaded in `_lib`.
        assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err());
        // Something "obscure" from kernel32...
        assert!(this.get::<unsafe extern "C" fn()>(b"GetLastError").is_err());
    }
}

#[cfg(windows)]
#[test]
fn works_getlasterror() {
    use winapi::um::errhandlingapi;
    use winapi::shared::minwindef::DWORD;
    use libloading::os::windows::{Library, Symbol};

    unsafe {
        let lib = Library::new("kernel32.dll").unwrap();
        let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError").unwrap();
        errhandlingapi::SetLastError(42);
        assert_eq!(errhandlingapi::GetLastError(), gle())
    }
}

#[cfg(windows)]
#[test]
fn works_getlasterror0() {
    use winapi::um::errhandlingapi;
    use winapi::shared::minwindef::DWORD;
    use libloading::os::windows::{Library, Symbol};

    unsafe {
        let lib = Library::new("kernel32.dll").unwrap();
        let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError\0").unwrap();
        errhandlingapi::SetLastError(42);
        assert_eq!(errhandlingapi::GetLastError(), gle())
    }
}

#[cfg(windows)]
#[test]
fn library_open_already_loaded() {
    use libloading::os::windows::Library;

    // Present on Windows systems and NOT used by any other tests to prevent races.
    const LIBPATH: &str = "Msftedit.dll";

    // Not loaded yet.
    assert!(match Library::open_already_loaded(LIBPATH) {
        Err(libloading::Error::GetModuleHandleExW { .. }) => true,
        _ => false,
    });

    unsafe {
        let _lib = Library::new(LIBPATH).unwrap();
        // Loaded now.
        assert!(Library::open_already_loaded(LIBPATH).is_ok());
    }
}
<|endoftext|>


<commit_before>// ZSnapMgr :: ZFS snapshot and backup manager
//
// Copyright (c) 2016 by William R. Fraser
//

use std::collections::btree_map::{BTreeMap, Entry, IterMut};
use std::env;
use std::iter::Iterator;
use std::io;
use std::io::{Read, Write};
use std::ops::Deref;
use std::process;

extern crate termios;
use termios::*;

extern crate zsnapmgr;
use zsnapmgr::ZSnapMgr;

mod table;
use table::Table;

static USE_SUDO: bool = true;

trait OptionDeref<T: Deref> {
    fn as_deref(&self) -> Option<&T::Target>;
}

impl<T: Deref> OptionDeref<T> for Option<T> {
    fn as_deref(&self) -> Option<&T::Target> {
        self.as_ref().map(Deref::deref)
    }
}

trait VecDeref<T: Deref> {
    fn as_deref(&self) -> Vec<&T::Target>;
}

impl<T: Deref> VecDeref<T> for Vec<T> {
    fn as_deref(&self) -> Vec<&T::Target> {
        self.iter().map(Deref::deref).collect()
    }
}

#[derive(Debug)]
struct Backup {
    filename_base: String,
    volume: String,
    start_snapshot: Option<String>,
    end_snapshot: Option<String>,
}

struct Backups {
    backups_by_volume: BTreeMap<String, Backup>,
}

struct BackupsIterMut<'a> {
    iter_mut: IterMut<'a, String, Backup>,
}

impl<'a> Iterator for BackupsIterMut<'a> {
    type Item = &'a mut Backup;
    fn next(&mut self) -> Option<&'a mut Backup> {
        match self.iter_mut.next() {
            Some((_, backup)) => Some(backup),
            None => None,
        }
    }
}

impl Backups {
    pub fn new() -> Backups {
        Backups { backups_by_volume: BTreeMap::new() }
    }

    pub fn insert(&mut self,
                  filename_base: String,
                  volume: String,
                  start_snapshot: Option<String>) {

        match self.backups_by_volume.entry(volume.clone()) {
            Entry::Occupied(ref mut entry) => {
                let backup = entry.get_mut();
                if start_snapshot.is_some() &&
                   (backup.start_snapshot.is_none() ||
                    start_snapshot.as_ref().unwrap() > backup.start_snapshot.as_ref().unwrap()) {
                    backup.start_snapshot = start_snapshot;
                    backup.filename_base = filename_base;
                }
            }
            Entry::Vacant(entry) => {
                entry.insert(Backup {
                    filename_base: filename_base,
                    volume: volume,
                    start_snapshot: start_snapshot,
                    end_snapshot: None,
                });
            }
        }
    }

    pub fn values(self) -> Vec<Backup> {
        let mut vec: Vec<Backup> = Vec::new();
        for (_k, v) in self.backups_by_volume.into_iter() {
            if v.end_snapshot.is_some() {
                vec.push(v);
            }
        }
        vec
    }

    fn iter_mut<'a>(&'a mut self) -> BackupsIterMut<'a> {
        BackupsIterMut { iter_mut: self.backups_by_volume.iter_mut() }
    }
}

fn enumerate_files(path: &str) -> Box<Iterator<Item = String>> {
    let out = std::fs::read_dir(std::path::Path::new(path))
                  .expect("error reading directory")
                  .map(|entry_result| entry_result.expect("error enumerating files"))
                  .filter(|entry| {
                      match entry.file_type() {
                          Ok(ft) => ft.is_file(),
                          Err(e) => {
                              println!("error getting file type of \"{}\": {}",
                                       entry.path().to_string_lossy(),
                                       e);
                              false
                          }
                      }
                  })
                  .map(|entry| entry.file_name().to_string_lossy().into_owned());
    Box::new(out)
}

fn gather_volumes(path: &str) -> Vec<Backup> {
    let z = ZSnapMgr::new(USE_SUDO);
    let snapshots: Vec<String> = match z.get_snapshots(None) {
        Ok(s) => s,
        Err(e) => {
            println!("Error getting snapshots from ZFS: {}", e);
            return vec![];
        }
    };

    let volumes: Vec<String> = match z.get_volumes() {
        Ok(v) => v,
        Err(e) => {
            println!("Error getting volumes from ZFS: {}", e);
            return vec![];
        }
    };

    let mut backups = Backups::new();

    for filename in enumerate_files(path) {
        if let Some(zfs_pos) = filename.find(".zfs") {
            if !(&filename).ends_with("_partial") {
                let parts = filename[0..zfs_pos].splitn(2, '@').collect::<Vec<&str>>();
                let volume_name = parts[0].replace("_", "/");
                let backup_snap = parts[1];

                if volumes.binary_search(&volume_name).is_ok() {
                    backups.insert(parts[0].to_string(),
                                   volume_name.to_string(),
                                   Some(backup_snap.to_string()));
                } else {
                    let volume_name_mod = "/".to_string() + &volume_name;
                    let matches: Vec<&str> = volumes.iter()
                                                    .filter(|ref vol| {
                                                        vol.ends_with(&volume_name_mod)
                                                    })
                                                    .map(Deref::deref)
                                                    .collect();

                    if matches.len() == 1 {
                        backups.insert(matches[0].to_string(),
                                       volume_name.to_string(),
                                       Some(backup_snap.to_string()));
                    } else {
                        print!("Backup filename \"{}\" ", filename);
                        if matches.len() > 1 {
                            println!("matches more than one volume.\nIt could be any of: {:?}",
                                     matches);
                        } else {
                            println!("doesn't match any volumes.");
                        }
                        println!("Skipping it.\n");
                    }
                }
            }
        }
    }

    // Now fill in the latest snapshot available for each volume in the proposed backups.
    for backup in backups.iter_mut() {
        let volume_at = backup.volume.clone() + "@";
        let volume_snaps: Vec<&str> = snapshots.iter()
                                               .filter(|snap| snap.starts_with(&volume_at))
                                               .map(Deref::deref)
                                               .collect();

        if backup.start_snapshot.is_some() {
            // Check that the start snapshot still exists.
            let start_snapshot = volume_at.clone() + backup.start_snapshot.as_ref().unwrap();

            if !volume_snaps.binary_search(&start_snapshot.deref()).is_ok() {
                println!("Snapshot \"{}\" doesn't exist any more; doing full backup instead.\n",
                         start_snapshot);
                backup.start_snapshot = None;
            }
        }

        let last_snapshot: &str = volume_snaps.last().unwrap().splitn(2, '@').last().unwrap();
        println!("last snapshot: {}", last_snapshot);

        if backup.start_snapshot
                 .as_ref()
                 .and_then(|start| Some(start != last_snapshot))
                 .unwrap_or(false) {
            backup.end_snapshot = Some(last_snapshot.to_string());
        } else {
            println!("Backup of \"{}\" is up to date (@{}). Skipping.\n",
                     backup.volume,
                     backup.start_snapshot.as_ref().unwrap());
            backup.end_snapshot = None;
        }
    }

    backups.values()
}

fn getpass(prompt: &str) -> io::Result<String> {
    let mut termios = Termios::from_fd(0).expect("failed to get termios settings");

    let old = termios.c_lflag;
    termios.c_lflag &= !ECHO;   // disable echo
    termios.c_lflag &= !ICANON; // disable line-buffering
    tcsetattr(0, TCSAFLUSH, &mut termios).expect("failed to set termios settings");

    let mut stdout = io::stdout();
    write!(stdout, "{}", prompt).unwrap();
    stdout.flush().unwrap();

    let stdin = io::stdin();
    let mut bytes = stdin.lock().bytes();
    let mut line = String::new();
    let mut utf8 = Vec::<u8>::new();
    loop {
        match bytes.next().or(Some(Err(io::Error::new(io::ErrorKind::Other, "EOF in getpass!")))) {
            Some(Ok(byte)) => {
                // 0x4 is EOT; aka ctrl-D
                if byte == 0x4 && utf8.is_empty() {
                    return Err(io::Error::new(io::ErrorKind::Other, "EOF in getpass!"));
                }

                utf8.push(byte);

                let mut valid_utf8 = false;
                if let Ok(c) = std::str::from_utf8(&utf8) {
                    if c == "\n" {
                        write!(stdout, "\n").unwrap();
                        stdout.flush().unwrap();
                        break;
                    } else {
                        valid_utf8 = true;
                        line.push_str(c);
                        write!(stdout, "*").unwrap();
                        stdout.flush().unwrap();
                    }
                }

                if valid_utf8 {
                    utf8.clear();
                }
            }
            Some(Err(e)) => {
                return Err(e);
            }
            _ => unreachable!(),
        }
    }

    termios.c_lflag = old;
    tcsetattr(0, TCSAFLUSH, &mut termios).expect("failed to reset termios settings");

    Ok(line)
}

fn do_backups(backups: &Vec<Backup>, path: &str) {
    if backups.is_empty() {
        return;
    }

    let passphrase: String;
    loop {
        let pass1 = getpass("GPG passphrase: ").unwrap();
        let pass2 = getpass("again: ").unwrap();
        if pass1 != pass2 {
            println!("Passphrases do not match.");
        } else {
            passphrase = pass1;
            break;
        }
    }

    for backup in backups {
        let z = ZSnapMgr::new(USE_SUDO);

        let snapshot = format!("{}@{}",
                               backup.volume,
                               backup.end_snapshot.as_deref().unwrap());
        z.backup(path,
                 &snapshot,
                 &passphrase,
                 backup.start_snapshot.as_deref())
         .err()
         .and_then(|e| {
             println!("failed backup of {}: {}", backup.volume, e);
             Some(())
         });
    }
}

fn interactive_backup(backups_dir: &str) {
    let mut volumes: Vec<Backup> = gather_volumes(backups_dir);
    loop {
        let mut table = Table::new(&vec!["_", "volume", "incremental", "snapshot date"]);
        for i in 0..volumes.len() {
            let start = if volumes[i].start_snapshot.is_none() {
                "full backup".to_string()
            } else {
                volumes[i].start_snapshot.as_ref().unwrap().clone()
            };

            table.push(vec![(i + 1).to_string(),
                            volumes[i].volume.clone(),
                            start,
                            volumes[i].end_snapshot.as_ref().unwrap().clone()]);
        }

        println!("Volumes to backup:\n{}", table);

        println!(concat!("Enter a number to make changes,\n",
                         "\t'+' to add a volume,\n",
                         "\t'-' to remove one,\n",
                         "\t'd' to change all dates,\n",
                         "\tor <return> to start backup: "));
        io::stdout().flush().unwrap();

        let mut input = String::new();
        match io::stdin().read_line(&mut input) {
            Ok(bytes_read) => {
                if bytes_read == 0 {
                    panic!("EOF");
                }
                input.pop();
            }
            Err(e) => panic!(e),
        }

        if input == "+" {
            // TODO
            println!("ADD");
        } else if input.starts_with("-") {
            // TODO
            println!("REMOVE");
        } else if input.starts_with("d") || input.starts_with("D") {
            // TODO
            println!("DATE");
        } else if input.is_empty() {
            println!("Starting backups.\n");
            do_backups(&volumes, backups_dir);
            break;
        } else {
            let index: usize;

            match input.parse::<usize>() {
                Ok(n) => {
                    index = n;
                }
                Err(e) => {
                    println!("Invalid number: {}", e);
                    continue;
                }
            }

            if volumes.len() < index {
                println!("Number out of range.");
                continue;
            }

            let vol = &volumes[index - 1];

            // TODO
            println!("CHANGE: {:?}", vol);
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program_name = &args[0].rsplitn(2, '/').next().unwrap();

    let command = if args.len() < 2 {
        "help"
    } else {
        args[1].as_ref()
    };

    match command {
        "backup" => {
            if args.len() == 3 {
                interactive_backup(&args[2]);
            } else {
                println!("usage: {} backup <backups_location>", program_name);
                process::exit(-1);
            }
        }
        "automanage" => {
            println!("snapshot automanage is not yet implemented.");
            process::exit(-1);
        }
        _ => {
            if command != "help" {
                println!("unknown command \"{}\"", command);
            }
            println!("usage: {} <backup | automanage> [options]", program_name);
            process::exit(-1);
        }
    }
}
<commit_msg>implement the rest of the interactive loop<commit_after>// ZSnapMgr :: ZFS snapshot and backup manager
//
// Copyright (c) 2016 by William R. Fraser
//

use std::collections::btree_map::{BTreeMap, Entry, IterMut};
use std::env;
use std::iter::Iterator;
use std::io;
use std::io::{Read, Write};
use std::ops::Deref;
use std::process;

extern crate termios;
use termios::*;

extern crate zsnapmgr;
use zsnapmgr::ZSnapMgr;

mod table;
use table::Table;

static USE_SUDO: bool = true;

trait OptionDeref<T: Deref> {
    fn as_deref(&self) -> Option<&T::Target>;
}

impl<T: Deref> OptionDeref<T> for Option<T> {
    fn as_deref(&self) -> Option<&T::Target> {
        self.as_ref().map(Deref::deref)
    }
}

trait VecDeref<T: Deref> {
    fn as_deref(&self) -> Vec<&T::Target>;
}

impl<T: Deref> VecDeref<T> for Vec<T> {
    fn as_deref(&self) -> Vec<&T::Target> {
        self.iter().map(Deref::deref).collect()
    }
}

#[derive(Debug)]
struct Backup {
    filename_base: String,
    volume: String,
    start_snapshot: Option<String>,
    end_snapshot: Option<String>,
}

struct Backups {
    backups_by_volume: BTreeMap<String, Backup>,
}

struct BackupsIterMut<'a> {
    iter_mut: IterMut<'a, String, Backup>,
}

impl<'a> Iterator for BackupsIterMut<'a> {
    type Item = &'a mut Backup;
    fn next(&mut self) -> Option<&'a mut Backup> {
        match self.iter_mut.next() {
            Some((_, backup)) => Some(backup),
            None => None,
        }
    }
}

impl Backups {
    pub fn new() -> Backups {
        Backups { backups_by_volume: BTreeMap::new() }
    }

    pub fn insert(&mut self,
                  filename_base: String,
                  volume: String,
                  start_snapshot: Option<String>) {

        match self.backups_by_volume.entry(volume.clone()) {
            Entry::Occupied(ref mut entry) => {
                let backup = entry.get_mut();
                if start_snapshot.is_some() &&
                   (backup.start_snapshot.is_none() ||
                    start_snapshot.as_ref().unwrap() > backup.start_snapshot.as_ref().unwrap()) {
                    backup.start_snapshot = start_snapshot;
                    backup.filename_base = filename_base;
                }
            }
            Entry::Vacant(entry) => {
                entry.insert(Backup {
                    filename_base: filename_base,
                    volume: volume,
                    start_snapshot: start_snapshot,
                    end_snapshot: None,
                });
            }
        }
    }

    pub fn values(self) -> Vec<Backup> {
        let mut vec: Vec<Backup> = Vec::new();
        for (_k, v) in self.backups_by_volume.into_iter() {
            if v.end_snapshot.is_some() {
                vec.push(v);
            }
        }
        vec
    }

    fn iter_mut<'a>(&'a mut self) -> BackupsIterMut<'a> {
        BackupsIterMut { iter_mut: self.backups_by_volume.iter_mut() }
    }
}

fn enumerate_files(path: &str) -> Box<Iterator<Item = String>> {
    let out = std::fs::read_dir(std::path::Path::new(path))
                  .expect("error reading directory")
                  .map(|entry_result| entry_result.expect("error enumerating files"))
                  .filter(|entry| {
                      match entry.file_type() {
                          Ok(ft) => ft.is_file(),
                          Err(e) => {
                              println!("error getting file type of \"{}\": {}",
                                       entry.path().to_string_lossy(),
                                       e);
                              false
                          }
                      }
                  })
                  .map(|entry| entry.file_name().to_string_lossy().into_owned());
    Box::new(out)
}

fn gather_volumes(path: &str) -> Vec<Backup> {
    let z = ZSnapMgr::new(USE_SUDO);
    let snapshots: Vec<String> = match z.get_snapshots(None) {
        Ok(s) => s,
        Err(e) => {
            println!("Error getting snapshots from ZFS: {}", e);
            return vec![];
        }
    };

    let volumes: Vec<String> = match z.get_volumes() {
        Ok(v) => v,
        Err(e) => {
            println!("Error getting volumes from ZFS: {}", e);
            return vec![];
        }
    };

    let mut backups = Backups::new();

    for filename in enumerate_files(path) {
        if let Some(zfs_pos) = filename.find(".zfs") {
            if !(&filename).ends_with("_partial") {
                let parts = filename[0..zfs_pos].splitn(2, '@').collect::<Vec<&str>>();
                let volume_name = parts[0].replace("_", "/");
                let backup_snap = parts[1];

                if volumes.binary_search(&volume_name).is_ok() {
                    backups.insert(parts[0].to_string(),
                                   volume_name.to_string(),
                                   Some(backup_snap.to_string()));
                } else {
                    let volume_name_mod = "/".to_string() + &volume_name;
                    let matches: Vec<&str> = volumes.iter()
                                                    .filter(|ref vol| {
                                                        vol.ends_with(&volume_name_mod)
                                                    })
                                                    .map(Deref::deref)
                                                    .collect();

                    if matches.len() == 1 {
                        backups.insert(matches[0].to_string(),
                                       volume_name.to_string(),
                                       Some(backup_snap.to_string()));
                    } else {
                        print!("Backup filename \"{}\" ", filename);
                        if matches.len() > 1 {
                            println!("matches more than one volume.\nIt could be any of: {:?}",
                                     matches);
                        } else {
                            println!("doesn't match any volumes.");
                        }
                        println!("Skipping it.\n");
                    }
                }
            }
        }
    }

    // Now fill in the latest snapshot available for each volume in the proposed backups.
    for backup in backups.iter_mut() {
        let volume_at = backup.volume.clone() + "@";
        let volume_snaps: Vec<&str> = snapshots.iter()
                                               .filter(|snap| snap.starts_with(&volume_at))
                                               .map(Deref::deref)
                                               .collect();

        if backup.start_snapshot.is_some() {
            // Check that the start snapshot still exists.
            let start_snapshot = volume_at.clone() + backup.start_snapshot.as_ref().unwrap();

            if !volume_snaps.binary_search(&start_snapshot.deref()).is_ok() {
                println!("Snapshot \"{}\" doesn't exist any more; doing full backup instead.\n",
                         start_snapshot);
                backup.start_snapshot = None;
            }
        }

        let last_snapshot: &str = volume_snaps.last().unwrap().splitn(2, '@').last().unwrap();
        println!("last snapshot: {}", last_snapshot);

        if backup.start_snapshot
                 .as_ref()
                 .and_then(|start| Some(start != last_snapshot))
                 .unwrap_or(false) {
            backup.end_snapshot = Some(last_snapshot.to_string());
        } else {
            println!("Backup of \"{}\" is up to date (@{}). Skipping.\n",
                     backup.volume,
                     backup.start_snapshot.as_ref().unwrap());
            backup.end_snapshot = None;
        }
    }

    backups.values()
}

fn getpass(prompt: &str) -> io::Result<String> {
    let mut termios = Termios::from_fd(0).expect("failed to get termios settings");

    let old = termios.c_lflag;
    termios.c_lflag &= !ECHO;   // disable echo
    termios.c_lflag &= !ICANON; // disable line-buffering
    tcsetattr(0, TCSAFLUSH, &mut termios).expect("failed to set termios settings");

    let mut stdout = io::stdout();
    write!(stdout, "{}", prompt).unwrap();
    stdout.flush().unwrap();

    let stdin = io::stdin();
    let mut bytes = stdin.lock().bytes();
    let mut line = String::new();
    let mut utf8 = Vec::<u8>::new();
    loop {
        match bytes.next().or(Some(Err(io::Error::new(io::ErrorKind::Other, "EOF in getpass!")))) {
            Some(Ok(byte)) => {
                // 0x4 is EOT; aka ctrl-D
                if byte == 0x4 && utf8.is_empty() {
                    return Err(io::Error::new(io::ErrorKind::Other, "EOF in getpass!"));
                }

                utf8.push(byte);

                let mut valid_utf8 = false;
                if let Ok(c) = std::str::from_utf8(&utf8) {
                    if c == "\n" {
                        write!(stdout, "\n").unwrap();
                        stdout.flush().unwrap();
                        break;
                    } else {
                        valid_utf8 = true;
                        line.push_str(c);
                        write!(stdout, "*").unwrap();
                        stdout.flush().unwrap();
                    }
                }

                if valid_utf8 {
                    utf8.clear();
                }
            }
            Some(Err(e)) => {
                return Err(e);
            }
            _ => unreachable!(),
        }
    }

    termios.c_lflag = old;
    tcsetattr(0, TCSAFLUSH, &mut termios).expect("failed to reset termios settings");

    Ok(line)
}

fn do_backups(backups: &Vec<Backup>, path: &str) {
    if backups.is_empty() {
        println!("Nothing to do.");
        return;
    }

    let passphrase: String;
    loop {
        let pass1 = getpass("GPG passphrase: ").unwrap();
        let pass2 = getpass("again: ").unwrap();
        if pass1 != pass2 {
            println!("Passphrases do not match.");
        } else {
            passphrase = pass1;
            break;
        }
    }

    for backup in backups {
        let z = ZSnapMgr::new(USE_SUDO);

        let snapshot = format!("{}@{}",
                               backup.volume,
                               backup.end_snapshot.as_deref().unwrap());
        z.backup(path,
                 &snapshot,
                 &passphrase,
                 backup.start_snapshot.as_deref())
         .err()
         .and_then(|e| {
             println!("failed backup of {}: {}", backup.volume, e);
             Some(())
         });
    }
}

fn interactive_backup(backups_dir: &str) {
    let z = ZSnapMgr::new(USE_SUDO);
    let mut volumes: Vec<Backup> = gather_volumes(backups_dir);
    loop {
        let mut table = Table::new(&vec!["_", "volume", "incremental", "snapshot date"]);
        for i in 0..volumes.len() {
            let start = if volumes[i].start_snapshot.is_none() {
                "full backup".to_string()
            } else {
                volumes[i].start_snapshot.as_ref().unwrap().clone()
            };

            table.push(vec![(i + 1).to_string(),
                            volumes[i].volume.clone(),
                            start,
                            volumes[i].end_snapshot.as_ref().unwrap().clone()]);
        }

        println!("Volumes to backup:\n{}", table);

        print!(concat!("Enter a number to make changes,\n",
                         "\t'+' to add a volume,\n",
                         "\t'-' to remove one,\n",
                         "\t'd' to change all dates,\n",
                         "\tor <return> to start backup: "));
        io::stdout().flush().unwrap();

        let mut input = String::new();
        match io::stdin().read_line(&mut input) {
            Ok(bytes_read) => {
                if bytes_read == 0 {
                    panic!("EOF");
                }
                input.pop();
            }
            Err(e) => panic!(e),
        }

        if input == "+" {
            print!("Volume: ");
            io::stdout().flush().unwrap();

            let mut vol = String::new();
            io::stdin().read_line(&mut vol).unwrap();
            vol.pop();

            let latest_snap: String;
            match z.get_snapshots(Some(&vol)).unwrap().pop() {
                Some(snap) => {
                    latest_snap = snap;
                },
                None       => {
                    println!("No snapshots available for that volume.\n");
                    continue;
                }
            }

            volumes.push(Backup {
                filename_base: vol.replace("/", "_").to_string(),
                volume: vol.clone(),
                start_snapshot: None,
                end_snapshot: Some(latest_snap),
            });

        } else if input.starts_with("-") {

            let index: usize;

            if input.len() > 1 {
                match input[1..].parse::<usize>() {
                    Ok(n) => {
                        index = n;
                    },
                    Err(e) => {
                        println!("Invalid number: {}\n", e);
                        continue;
                    }
                }
            } else {
                print!("Remove which one?: ");
                io::stdout().flush().unwrap();

                input.clear();
                io::stdin().read_line(&mut input).unwrap();
                input.pop();

                match input.parse::<usize>() {
                    Ok(n) => {
                        index = n;
                    },
                    Err(e) => {
                        println!("Invalid number: {}\n", e);
                        continue;
                    }
                }
            }

            if volumes.len() < index || index == 0 {
                println!("Number out of range.\n");
                continue;
            }

            volumes.remove(index - 1);

        } else if input.starts_with("d") || input.starts_with("D") {

            print!("Snapshot date (yyyy-MM-dd): ");
            io::stdout().flush().unwrap();

            let mut date = String::new();
            io::stdin().read_line(&mut date).unwrap();
            date.pop();

            for volume in volumes.iter_mut() {
                volume.end_snapshot = Some(date.clone());
                //TODO: check that the snapshot exists for that volume
            }

        } else if input.is_empty() {
            println!("Starting backups.\n");
            do_backups(&volumes, backups_dir);
            break;
        } else {
            let index: usize;

            match input.parse::<usize>() {
                Ok(n) => {
                    index = n;
                }
                Err(e) => {
                    println!("Invalid number: {}\n", e);
                    continue;
                }
            }

            if volumes.len() < index || index == 0 {
                println!("Number out of range.\n");
                continue;
            }

            let vol = volumes.get_mut(index - 1).unwrap();

            print!("Change (I)ncremental starting snapshot, (S)napshot date: ");
            io::stdout().flush().unwrap();

            input.clear();
            io::stdin().read_line(&mut input).unwrap();
            input.pop();

            let start: bool;
            if input == "I" || input == "i" {
                start = true;
            } else if input == "S" || input == "s" {
                start = false;
            } else {
                println!("Invalid selection.\n");
                continue;
            }

            print!("Date (yyyy-MM-dd): ");
            io::stdout().flush().unwrap();

            input.clear();
            io::stdin().read_line(&mut input).unwrap();
            input.pop();

            if start {
                vol.start_snapshot = Some(input.clone());
            } else {
                vol.end_snapshot = Some(input.clone());
            }
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program_name = &args[0].rsplitn(2, '/').next().unwrap();

    let command = if args.len() < 2 {
        "help"
    } else {
        args[1].as_ref()
    };

    match command {
        "backup" => {
            if args.len() == 3 {
                interactive_backup(&args[2]);
            } else {
                println!("usage: {} backup <backups_location>", program_name);
                process::exit(-1);
            }
        }
        "automanage" => {
            println!("snapshot automanage is not yet implemented.");
            process::exit(-1);
        }
        _ => {
            if command != "help" {
                println!("unknown command \"{}\"", command);
            }
            println!("usage: {} <backup | automanage> [options]", program_name);
            process::exit(-1);
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate "rustc-serialize" as rustc_serialize;

use rgtk::*;
use std::collections::HashSet;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;

mod ffi;
mod projects;
mod ui;
mod utils;

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;
    let mut quit_app = false;

    // create the window

    let title = format!("SolidOak {}.{}.{}",
                        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_MINOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_PATCH").unwrap());
    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title(title.as_slice());
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(&mut |_| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        quit_app = true;
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let selection = project_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&project_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    project_tree.append_column(&column);

    let mut project_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_buttons.set_size_request(-1, -1);
    build_buttons.add(&run_button);
    build_buttons.add(&build_button);
    build_buttons.add(&test_button);
    build_buttons.add(&clean_button);
    build_buttons.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_buttons, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
        is_refreshing_tree: false
    };

    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut project_tree);
    ::projects::set_selection(&mut state, write_fd);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::new_project(&mut state, &mut project_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::import_project(&mut state, &mut project_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(&mut || {
        ::projects::set_selection(&mut state, write_fd);
    }));
    project_tree.connect(gtk::signals::RowCollapsed::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    project_tree.connect(gtk::signals::RowExpanded::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();

    // start communicating with nvim

    ffi::nvim_attach(write_fd);
    ffi::nvim_execute(write_fd, "au BufEnter * call rpcnotify(1, \"bufenter\", bufname(\"\"))");

    // make read_fd non-blocking so we can check it while also checking for GUI events

    unsafe { ffi::fcntl(read_fd, ffi::F_SETFL, ffi::O_NONBLOCK) };

    // loop over GUI events and respond to messages from nvim

    loop {
        gtk::main_iteration_do(false);

        if let Some(recv_arr) = ffi::receive_message(read_fd) {
            if let Some(neovim::Object::String(event_name)) = recv_arr.get(1) {
                match event_name.as_slice() {
                    "bufenter" => {
                        if let Some(neovim::Object::Array(event_args)) = recv_arr.get(2) {
                            if let Some(neovim::Object::String(path_str)) = event_args.get(0) {
                                state.selection = Some(path_str);
                                ::ui::update_project_tree(&mut state, &mut project_tree);
                            }
                        }
                    },
                    _ => (),
                }
            }
        }

        if quit_app {
            break;
        }
    }
}

fn main() {
    // create data dir
    let home_dir = ::utils::get_home_dir();
    let data_dir = home_dir.join(::utils::DATA_DIR);
    if !data_dir.exists() {
        match fs::mkdir(&data_dir, ::std::old_io::USER_DIR) {
            Ok(_) => {
                for res in ::utils::DATA_CONTENT.iter() {
                    let res_path = data_dir.join_many(res.path);
                    ::std::old_io::fs::mkdir_recursive(&res_path.dir_path(), ::std::old_io::USER_DIR).ok();
                    ::std::old_io::File::create(&res_path).write_all(res.data.as_bytes()).ok();
                }
                println!("Created data dir at {}", data_dir.as_str().unwrap());
            },
            Err(e) => { println!("Error creating data dir: {}", e) }
        }
    }

    // set $VIM to the data dir if it isn't already set
    if ::std::env::var("VIM").is_none() {
        ::std::env::set_var("VIM", data_dir.as_str().unwrap());
    }

    // create config file
    let config_file = home_dir.join(::utils::CONFIG_FILE);
    if !config_file.exists() {
        match ::std::old_io::File::create(&config_file).write_all(::utils::CONFIG_CONTENT.as_bytes()) {
            Ok(_) => { println!("Created config file at {}", config_file.as_str().unwrap()) },
            Err(e) => { println!("Error creating config file: {}", e) }
        }
    }

    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_gui : [ffi::c_int; 2] = [0; 2]; // to nvim from gui
    let mut gui_nvim : [ffi::c_int; 2] = [0; 2]; // to gui from nvim
    unsafe {
        ffi::pipe(nvim_gui.as_mut_ptr());
        ffi::pipe(gui_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        gui_main(&mut pty, gui_nvim[0], nvim_gui[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // start nvim
        let mut args = Vec::new();
        for arg in ::std::env::args() {
            args.push(arg.into_string().unwrap());
        }
        args.push_all(&["-u".to_string(), config_file.as_str().unwrap().to_string()]);
        neovim::main_setup(&args);
        neovim::channel_from_fds(nvim_gui[0], gui_nvim[1]);
        neovim::main_loop();
    }
}
<commit_msg>Update for latest version of Rust<commit_after>extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate "rustc-serialize" as rustc_serialize;

use rgtk::*;
use std::collections::HashSet;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;

mod ffi;
mod projects;
mod ui;
mod utils;

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;
    let mut quit_app = false;

    // create the window

    let title = format!("SolidOak {}.{}.{}",
                        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_MINOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_PATCH").unwrap());
    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title(title.as_slice());
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(&mut |_| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        quit_app = true;
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let selection = project_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&project_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    project_tree.append_column(&column);

    let mut project_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_buttons.set_size_request(-1, -1);
    build_buttons.add(&run_button);
    build_buttons.add(&build_button);
    build_buttons.add(&test_button);
    build_buttons.add(&clean_button);
    build_buttons.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_buttons, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
        is_refreshing_tree: false
    };

    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut project_tree);
    ::projects::set_selection(&mut state, write_fd);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::new_project(&mut state, &mut project_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::import_project(&mut state, &mut project_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(&mut || {
        ::projects::set_selection(&mut state, write_fd);
    }));
    project_tree.connect(gtk::signals::RowCollapsed::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    project_tree.connect(gtk::signals::RowExpanded::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();

    // start communicating with nvim

    ffi::nvim_attach(write_fd);
    ffi::nvim_execute(write_fd, "au BufEnter * call rpcnotify(1, \"bufenter\", bufname(\"\"))");

    // make read_fd non-blocking so we can check it while also checking for GUI events

    unsafe { ffi::fcntl(read_fd, ffi::F_SETFL, ffi::O_NONBLOCK) };

    // loop over GUI events and respond to messages from nvim

    loop {
        gtk::main_iteration_do(false);

        if let Some(recv_arr) = ffi::receive_message(read_fd) {
            if let Some(neovim::Object::String(event_name)) = recv_arr.get(1) {
                match event_name.as_slice() {
                    "bufenter" => {
                        if let Some(neovim::Object::Array(event_args)) = recv_arr.get(2) {
                            if let Some(neovim::Object::String(path_str)) = event_args.get(0) {
                                state.selection = Some(path_str);
                                ::ui::update_project_tree(&mut state, &mut project_tree);
                            }
                        }
                    },
                    _ => (),
                }
            }
        }

        if quit_app {
            break;
        }
    }
}

fn main() {
    // create data dir
    let home_dir = ::utils::get_home_dir();
    let data_dir = home_dir.join(::utils::DATA_DIR);
    if !data_dir.exists() {
        match fs::mkdir(&data_dir, ::std::old_io::USER_DIR) {
            Ok(_) => {
                for res in ::utils::DATA_CONTENT.iter() {
                    let res_path = data_dir.join_many(res.path);
                    ::std::old_io::fs::mkdir_recursive(&res_path.dir_path(), ::std::old_io::USER_DIR).ok();
                    ::std::old_io::File::create(&res_path).write_all(res.data.as_bytes()).ok();
                }
                println!("Created data dir at {}", data_dir.as_str().unwrap());
            },
            Err(e) => { println!("Error creating data dir: {}", e) }
        }
    }

    // set $VIM to the data dir if it isn't already set
    if ::std::env::var("VIM").is_err() {
        ::std::env::set_var("VIM", data_dir.as_str().unwrap());
    }

    // create config file
    let config_file = home_dir.join(::utils::CONFIG_FILE);
    if !config_file.exists() {
        match ::std::old_io::File::create(&config_file).write_all(::utils::CONFIG_CONTENT.as_bytes()) {
            Ok(_) => { println!("Created config file at {}", config_file.as_str().unwrap()) },
            Err(e) => { println!("Error creating config file: {}", e) }
        }
    }

    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_gui : [ffi::c_int; 2] = [0; 2]; // to nvim from gui
    let mut gui_nvim : [ffi::c_int; 2] = [0; 2]; // to gui from nvim
    unsafe {
        ffi::pipe(nvim_gui.as_mut_ptr());
        ffi::pipe(gui_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        gui_main(&mut pty, gui_nvim[0], nvim_gui[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // start nvim
        let mut args = Vec::new();
        for arg in ::std::env::args() {
            args.push(arg);
        }
        args.push_all(&["-u".to_string(), config_file.as_str().unwrap().to_string()]);
        neovim::main_setup(&args);
        neovim::channel_from_fds(nvim_gui[0], gui_nvim[1]);
        neovim::main_loop();
    }
}
<|endoftext|>


<commit_before>extern crate discord;

use discord::{Discord, ChannelRef, State};
use discord::model::Event;

fn rot13(c: char) -> char {
    let base: u8 = match c {
        'a'...'z' => 'a' as u8,
        'A'...'Z' => 'A' as u8,
        _ => return c
    };

    let ordinal = c as u8 - base;
    let rot = (ordinal + 13) % 26;
    (rot + base) as char
}

fn main() {
    use std::env;
    println!("The Rotting 13!");

    let discord = Discord::from_bot_token(
        &env::var("DISCORD_TOKEN").expect("Expected token")
    ).expect("login failed!");

    let (mut connection, ready) = discord.connect().expect("connect failed");
    let mut state = State::new(ready);

    println!("Connected!");

    loop {
        let event = match connection.recv_event() {
            Ok(event) => event,
            Err(discord::Error::Closed(code, body)) => {
                println!("[Error] Connection closed with status {:?}: {}", code, body);
                break
            },
            Err(err) => {
                println!("[Warning] Receive error: {:?}", err);
                continue
            }
        };
        state.update(&event);

        match event {
            Event::MessageCreate(message) => {
                match state.find_channel(&message.channel_id) {
                    Some(ChannelRef::Private(channel)) => {
                        if message.author.name != channel.recipient.name {
                            continue
                        }

                        let original_message = message.content;
                        let mut new_message = String::new();
                        for chr in original_message.chars() {
                            new_message.push(rot13(chr));
                        }

                        let _ = discord.send_message(&message.channel_id, &new_message, "", false);
                    },
                    None => println!("Got a message from an unknown channel??? From {} saying {}", message.author.name, message.content),
                    _ => {},
                }
            },
            _ => {}
        }
    }
}<commit_msg>Moved self-message prevention out of the DM block<commit_after>extern crate discord;

use discord::{Discord, ChannelRef, State};
use discord::model::Event;

fn rot13(c: char) -> char {
    let base: u8 = match c {
        'a'...'z' => 'a' as u8,
        'A'...'Z' => 'A' as u8,
        _ => return c
    };

    let ordinal = c as u8 - base;
    let rot = (ordinal + 13) % 26;
    (rot + base) as char
}

fn main() {
    use std::env;
    println!("The Rotting 13!");

    let discord = Discord::from_bot_token(
        &env::var("DISCORD_TOKEN").expect("Expected token")
    ).expect("login failed!");

    let (mut connection, ready) = discord.connect().expect("connect failed");
    let mut state = State::new(ready);

    println!("Connected!");

    loop {
        let event = match connection.recv_event() {
            Ok(event) => event,
            Err(discord::Error::Closed(code, body)) => {
                println!("[Error] Connection closed with status {:?}: {}", code, body);
                break
            },
            Err(err) => {
                println!("[Warning] Receive error: {:?}", err);
                continue
            }
        };
        state.update(&event);

        match event {
            Event::MessageCreate(message) => {
                if message.author.id != state.user().id {
                    continue
                }

                match state.find_channel(&message.channel_id) {
                    Some(ChannelRef::Private(channel)) => {
                        let original_message = message.content;
                        let mut new_message = String::new();
                        for chr in original_message.chars() {
                            new_message.push(rot13(chr));
                        }

                        let _ = discord.send_message(&message.channel_id, &new_message, "", false);
                    },
                    Some(ChannelRef::Public(server, channel)) => {

                    },
                    None => println!("Got a message from an unknown channel??? From {} saying {}", message.author.name, message.content),
                    _ => {},
                }
            },
            _ => {}
        }
    }
}<|endoftext|>


<commit_before>//! A simple evolutionary algorithm written in Rust.

extern crate rand;

use rand::Rng;
use std::collections::HashMap;

fn main() {
    let target = "METHINKS IT IS LIKE A WEASEL".to_string();
    println!("{}", target);

    let mut parent = String::new();
    generate_first_sentence(&mut parent);
    println!("{}", parent);

    let nb_copy = 400;
    let mutation_rate: f64 = 0.05;
    let mut counter = 0;

    while fitness(&target, &parent) != 0 {
        counter += 1;
        let mut sentences = HashMap::new();
        let mut f_min: u32 = 30;
        for _ in 0..nb_copy {
            let sentence = mutate(&mut parent, mutation_rate);
            let f = fitness(&target, &sentence);
            sentences.insert(f, sentence);

            if f < f_min {
                f_min = f
            }
        }

        if fitness(&target, &parent) > f_min {
            match sentences.get(&f_min) {
                Some(s) => {
                    parent = s.clone();
                    println!("{} : {}", parent, counter);
                }
                None => println!("Error, fitness minimum but no sentence."),
            }
        }
    }
}

fn fitness(target: &String, sentence: &String) -> u32 {
    let mut fitness = 0;
    for (c1, c2) in target.chars().zip(sentence.chars()) {
        if c1 != c2 {
            fitness += 1
        };
    }
    fitness
}

fn mutate(sentence: &mut String, mutation_rate: f64) -> String {
    let mut rng = rand::thread_rng();

    let mut mutation = String::new();
    for c in sentence.chars() {
        match mutation_rate > rng.gen_range(0f64, 1f64) {
            false => mutation.push(c),               // no mutation
            true => mutation.push(random_char()),   // mutation
        }
    }
    mutation
}

fn generate_first_sentence(parent: &mut String) {
    for _ in 0..28 {
        parent.push(random_char());
    }
}

fn random_char() -> char {
    match rand::thread_rng().gen_range('A' as u8, '\\' as u8) as char {
        '[' => ' ',
        c @ _ => c,
    }
}
<commit_msg>remove unneeded type annotations<commit_after>//! A simple evolutionary algorithm written in Rust.

extern crate rand;

use rand::Rng;
use std::collections::HashMap;

fn main() {
    let target = "METHINKS IT IS LIKE A WEASEL".to_string();
    println!("{}", target);

    let mut parent = String::new();
    generate_first_sentence(&mut parent);
    println!("{}", parent);

    let nb_copy = 400;
    let mutation_rate = 0.05;
    let mut counter = 0;

    while fitness(&target, &parent) != 0 {
        counter += 1;
        let mut sentences = HashMap::new();
        let mut f_min = 30;
        for _ in 0..nb_copy {
            let sentence = mutate(&mut parent, mutation_rate);
            let f = fitness(&target, &sentence);
            sentences.insert(f, sentence);

            if f < f_min {
                f_min = f
            }
        }

        if fitness(&target, &parent) > f_min {
            match sentences.get(&f_min) {
                Some(s) => {
                    parent = s.clone();
                    println!("{} : {}", parent, counter);
                }
                None => println!("Error, fitness minimum but no sentence."),
            }
        }
    }
}

fn fitness(target: &String, sentence: &String) -> u32 {
    let mut fitness = 0;
    for (c1, c2) in target.chars().zip(sentence.chars()) {
        if c1 != c2 {
            fitness += 1
        };
    }
    fitness
}

fn mutate(sentence: &mut String, mutation_rate: f64) -> String {
    let mut rng = rand::thread_rng();

    let mut mutation = String::new();
    for c in sentence.chars() {
        match mutation_rate > rng.gen_range(0f64, 1f64) {
            false => mutation.push(c),               // no mutation
            true => mutation.push(random_char()),   // mutation
        }
    }
    mutation
}

fn generate_first_sentence(parent: &mut String) {
    for _ in 0..28 {
        parent.push(random_char());
    }
}

fn random_char() -> char {
    match rand::thread_rng().gen_range('A' as u8, '\\' as u8) as char {
        '[' => ' ',
        c @ _ => c,
    }
}
<|endoftext|>


<commit_before>// Parse ANSI attr code
use curses::{attr_t, register_ansi, Window};
use regex::Regex;
use std::default::Default;
use std::iter::Enumerate;
use std::iter::Peekable;

pub struct ANSIParser {
    re: &'static Regex,
    last_attr: Option<attr_t>,
}

lazy_static! {
    static ref ANSI_RE: Regex =
        Regex::new(r"\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))").unwrap();
}

impl Default for ANSIParser {
    fn default() -> Self {
        ANSIParser {
            re: &ANSI_RE,
            last_attr: None,
        }
    }
}

#[derive(Clone, Debug)]
// named like to not clash with ANSIString from ansi_term crate
pub struct AnsiString {
    pub stripped: String,
    pub ansi_states: Vec<(usize, attr_t)>,
}

impl AnsiString {

    pub fn new_empty() -> AnsiString {
        AnsiString {
            stripped: "".to_string(),
            ansi_states: Vec::new(),
        }
    }

    pub fn into_inner(self) -> String {
        self.stripped
    }

    pub fn print(&self, curses: &mut Window) {
        for (c, attrs) in self.iter() {
            for (_, a) in attrs {
                curses.attr_on(*a)
            }
            curses.addch(c);
        }
    }

    pub fn iter<'a>(&'a self) -> AnsiStringIterator<'a> {
        AnsiStringIterator {
            ansi_states: &self.ansi_states,
            it_text: Box::new(self.stripped.chars().enumerate()),
            pk_ansi_states: self.ansi_states.iter().enumerate().peekable(),
        }
    }

    pub fn has_attrs(&self) -> bool {
        self.ansi_states.len() != 0
    }
}

pub struct AnsiStringIterator<'a> {
    ansi_states: &'a Vec<(usize, attr_t)>,
    it_text: Box<Enumerate<std::str::Chars<'a>>>,
    pk_ansi_states: Peekable<Enumerate<std::slice::Iter<'a, (usize, attr_t)>>>,
}

impl<'a> Iterator for AnsiStringIterator<'a> {
    type Item = (char, &'a [(usize, attr_t)]);

    fn next(&mut self) -> Option<Self::Item> {
        let mut as_range: Option<(usize, usize)> = None;
        let (ch_idx, ch) = match self.it_text.next() {
            Some((ch_idx, ch)) => (ch_idx, ch),
            None => {
                return None;
            }
        };

        while let Some(&(states_idx, &(ansi_idx, _))) = self.pk_ansi_states.peek() {
            if ch_idx == ansi_idx {
                let _ = self.pk_ansi_states.next();
                as_range = match as_range {
                    Some((start, end)) => Some((start, end + 1)),
                    None => Some((states_idx, states_idx)),
                }
            } else if ch_idx > ansi_idx {
                let _ = self.pk_ansi_states.next();
            } else {
                break;
            }
        }
        if let Some((start, end)) = as_range {
            return Some((ch, &self.ansi_states[start..end + 1]));
        } else {
            return Some((ch, &[]));
        }
    }
}

impl ANSIParser {
    pub fn parse_ansi(&mut self, text: &str) -> AnsiString {
        let mut strip_string = String::new();
        let mut colors = Vec::new();

        // assume parse_ansi is called linewise.
        // Because ANSI color code can affect text of next lines. We will save the last attribute and
        // add it to the newest line if no new color is specified.
        match self.re.find(text) {
            Some(mat) if mat.start() == 0 => {}
            Some(_) | None => {
                self.last_attr.map(|attr| {
                    colors.push((0, attr));
                });
            }
        }

        let mut num_chars = 0;
        let mut last = 0;
        for mat in self.re.find_iter(text) {
            let (start, end) = (mat.start(), mat.end());
            strip_string.push_str(&text[last..start]);
            num_chars += (&text[last..start]).chars().count();

            last = end;

            let attr = self.interpret_code(&text[start..end]);
            attr.map(|attr| {
                colors.push((num_chars, attr));
            });
            self.last_attr = attr;
        }

        strip_string.push_str(&text[last..text.len()]);

        AnsiString {
            stripped: strip_string,
            ansi_states: colors,
        }
    }

    fn interpret_code(&self, code: &str) -> Option<attr_t> {
        if code == "\x1B[K" || code == "\x1B[2J" {
            // clear screen & clear line
            None
        } else {
            let key = register_ansi(code.to_owned());
            Some(key)
        }

        //let mut state256 = 0;
        //let mut attr = 0;
        //let mut fg = -1;
        //let mut bg = -1;
        //let mut use_fg = true;

        //let code = &code[2..code.len()-1]; // ^[[1;30;40m -> 1;30;40
        //if code.is_empty() {
        //return Some(A_NORMAL());
        //}

        //for num in code.split(';').map(|x| x.parse::<i16>()) {
        //match state256 {
        //0 => {
        //match num.unwrap_or(0) {
        //0 => {attr = 0;}
        //1 => {attr |= A_BOLD();}
        //4 => {attr |= A_UNDERLINE();}
        //5 => {attr |= A_BLINK();}
        //7 => {attr |= A_REVERSE();}
        //8 => {attr |= A_INVIS();}
        //38 => {
        //use_fg = true;
        //state256 += 1;
        //}
        //48 => {
        //use_fg = false;
        //state256 += 1;
        //}
        //39 => {
        //fg = -1;
        //}
        //49 => {
        //bg = -1;
        //}
        //num if num >= 30 && num <= 37 => {
        //fg = num - 30;
        //}
        //num if num >= 40 && num <= 47 => {
        //bg = num - 40;
        //}
        //_ => {
        //}
        //}
        //}
        //1 => {
        //match num.unwrap_or(0) {
        //5 => { state256 += 1; }
        //_ => { state256 = 0; }
        //}
        //}
        //2 => {
        //if use_fg {
        //fg = num.unwrap_or(-1);
        //} else {
        //bg = num.unwrap_or(-1);
        //}
        //}
        //_ => {}
        //}
        //}

        //if fg != -1 || bg != -1 {
        //attr |= get_color_pair(fg, bg);
        //}

        //Some(attr)
    }
}
<commit_msg>Add test for ansi iterator<commit_after>// Parse ANSI attr code
use curses::{attr_t, register_ansi, Window};
use regex::Regex;
use std::default::Default;
use std::iter::Enumerate;
use std::iter::Peekable;

pub struct ANSIParser {
    re: &'static Regex,
    last_attr: Option<attr_t>,
}

lazy_static! {
    static ref ANSI_RE: Regex =
        Regex::new(r"\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))").unwrap();
}

impl Default for ANSIParser {
    fn default() -> Self {
        ANSIParser {
            re: &ANSI_RE,
            last_attr: None,
        }
    }
}

#[derive(Clone, Debug)]
// named like to not clash with ANSIString from ansi_term crate
pub struct AnsiString {
    pub stripped: String,
    pub ansi_states: Vec<(usize, attr_t)>,
}

impl AnsiString {

    pub fn new_empty() -> AnsiString {
        AnsiString {
            stripped: "".to_string(),
            ansi_states: Vec::new(),
        }
    }

    pub fn into_inner(self) -> String {
        self.stripped
    }

    pub fn print(&self, curses: &mut Window) {
        for (c, attrs) in self.iter() {
            for (_, a) in attrs {
                curses.attr_on(*a)
            }
            curses.addch(c);
        }
    }

    pub fn iter<'a>(&'a self) -> AnsiStringIterator<'a> {
        AnsiStringIterator {
            ansi_states: &self.ansi_states,
            it_text: Box::new(self.stripped.chars().enumerate()),
            pk_ansi_states: self.ansi_states.iter().enumerate().peekable(),
        }
    }

    pub fn has_attrs(&self) -> bool {
        self.ansi_states.len() != 0
    }
}

pub struct AnsiStringIterator<'a> {
    ansi_states: &'a Vec<(usize, attr_t)>,
    it_text: Box<Enumerate<std::str::Chars<'a>>>,
    pk_ansi_states: Peekable<Enumerate<std::slice::Iter<'a, (usize, attr_t)>>>,
}

impl<'a> Iterator for AnsiStringIterator<'a> {
    type Item = (char, &'a [(usize, attr_t)]);

    fn next(&mut self) -> Option<Self::Item> {
        let mut as_range: Option<(usize, usize)> = None;
        let (ch_idx, ch) = match self.it_text.next() {
            Some((ch_idx, ch)) => (ch_idx, ch),
            None => {
                return None;
            }
        };

        while let Some(&(states_idx, &(ansi_idx, _))) = self.pk_ansi_states.peek() {
            if ch_idx == ansi_idx {
                let _ = self.pk_ansi_states.next();
                as_range = match as_range {
                    Some((start, end)) => Some((start, end + 1)),
                    None => Some((states_idx, states_idx)),
                }
            } else if ch_idx > ansi_idx {
                let _ = self.pk_ansi_states.next();
            } else {
                break;
            }
        }
        if let Some((start, end)) = as_range {
            return Some((ch, &self.ansi_states[start..end + 1]));
        } else {
            return Some((ch, &[]));
        }
    }
}

impl ANSIParser {
    pub fn parse_ansi(&mut self, text: &str) -> AnsiString {
        let mut strip_string = String::new();
        let mut colors = Vec::new();

        // assume parse_ansi is called linewise.
        // Because ANSI color code can affect text of next lines. We will save the last attribute and
        // add it to the newest line if no new color is specified.
        match self.re.find(text) {
            Some(mat) if mat.start() == 0 => {}
            Some(_) | None => {
                self.last_attr.map(|attr| {
                    colors.push((0, attr));
                });
            }
        }

        let mut num_chars = 0;
        let mut last = 0;
        for mat in self.re.find_iter(text) {
            let (start, end) = (mat.start(), mat.end());
            strip_string.push_str(&text[last..start]);
            num_chars += (&text[last..start]).chars().count();

            last = end;

            let attr = self.interpret_code(&text[start..end]);
            attr.map(|attr| {
                colors.push((num_chars, attr));
            });
            self.last_attr = attr;
        }

        strip_string.push_str(&text[last..text.len()]);

        AnsiString {
            stripped: strip_string,
            ansi_states: colors,
        }
    }

    fn interpret_code(&self, code: &str) -> Option<attr_t> {
        if code == "\x1B[K" || code == "\x1B[2J" {
            // clear screen & clear line
            None
        } else {
            let key = register_ansi(code.to_owned());
            Some(key)
        }

        //let mut state256 = 0;
        //let mut attr = 0;
        //let mut fg = -1;
        //let mut bg = -1;
        //let mut use_fg = true;

        //let code = &code[2..code.len()-1]; // ^[[1;30;40m -> 1;30;40
        //if code.is_empty() {
        //return Some(A_NORMAL());
        //}

        //for num in code.split(';').map(|x| x.parse::<i16>()) {
        //match state256 {
        //0 => {
        //match num.unwrap_or(0) {
        //0 => {attr = 0;}
        //1 => {attr |= A_BOLD();}
        //4 => {attr |= A_UNDERLINE();}
        //5 => {attr |= A_BLINK();}
        //7 => {attr |= A_REVERSE();}
        //8 => {attr |= A_INVIS();}
        //38 => {
        //use_fg = true;
        //state256 += 1;
        //}
        //48 => {
        //use_fg = false;
        //state256 += 1;
        //}
        //39 => {
        //fg = -1;
        //}
        //49 => {
        //bg = -1;
        //}
        //num if num >= 30 && num <= 37 => {
        //fg = num - 30;
        //}
        //num if num >= 40 && num <= 47 => {
        //bg = num - 40;
        //}
        //_ => {
        //}
        //}
        //}
        //1 => {
        //match num.unwrap_or(0) {
        //5 => { state256 += 1; }
        //_ => { state256 = 0; }
        //}
        //}
        //2 => {
        //if use_fg {
        //fg = num.unwrap_or(-1);
        //} else {
        //bg = num.unwrap_or(-1);
        //}
        //}
        //_ => {}
        //}
        //}

        //if fg != -1 || bg != -1 {
        //attr |= get_color_pair(fg, bg);
        //}

        //Some(attr)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_ansi_iterator() {
        let input = "\x1B[48;2;5;10;15m\x1B[38;2;70;130;180mhi\x1B[0m";
        let ansistring = ANSIParser::default().parse_ansi(input);
        let mut it = ansistring.iter();
        let arr: Vec<(usize, u16)> = vec![(0, 11), (0, 12)];
        assert_eq!(Some(('h', &arr[..2])), it.next());
        assert_eq!(Some(('i', &arr[..0])), it.next());
    }
}
<|endoftext|>


<commit_before>use pdb::{PDB, FallibleIterator, Rva};

#[test]
fn test_module_lines() {
    let file = std::fs::File::open("fixtures/self/foo.pdb").expect("opening file");
    let mut pdb = PDB::open(file).expect("parse pdb");

    let address_map = pdb.address_map().expect("address map");
    let string_table = pdb.string_table().expect("string table");

    let dbi = pdb.debug_information().expect("dbi");
    let mut modules = dbi.modules().expect("modules");
    let module = modules.next().expect("parse module").expect("no module");
    let module_info = pdb.module_info(&module).expect("module info");

    let line_program = module_info.line_program().expect("line program");
    let mut lines = line_program.lines();
    let line_info = lines.next().expect("parse line info").expect("no lines");

    let rva = line_info.offset.to_rva(&address_map).expect("line rva");
    let file_info = line_program.get_file_info(line_info.file_index).expect("file info");
    let file_name = file_info.name.to_string_lossy(&string_table).expect("file name");

    assert_eq!(line_info.line_start, 29);
    assert_eq!(line_info.column_start, Some(0)); // looks like useless column info
    assert_eq!(rva, Rva(0x64f0));
    assert_eq!(file_name, "c:\\users\\user\\desktop\\self\\foo.cpp");
}
<commit_msg>Run rustfmt on the tests<commit_after>use pdb::{FallibleIterator, Rva, PDB};

#[test]
fn test_module_lines() {
    let file = std::fs::File::open("fixtures/self/foo.pdb").expect("opening file");
    let mut pdb = PDB::open(file).expect("parse pdb");

    let address_map = pdb.address_map().expect("address map");
    let string_table = pdb.string_table().expect("string table");

    let dbi = pdb.debug_information().expect("dbi");
    let mut modules = dbi.modules().expect("modules");
    let module = modules.next().expect("parse module").expect("no module");
    let module_info = pdb.module_info(&module).expect("module info");

    let line_program = module_info.line_program().expect("line program");
    let mut lines = line_program.lines();
    let line_info = lines.next().expect("parse line info").expect("no lines");

    let rva = line_info.offset.to_rva(&address_map).expect("line rva");
    let file_info = line_program
        .get_file_info(line_info.file_index)
        .expect("file info");
    let file_name = file_info
        .name
        .to_string_lossy(&string_table)
        .expect("file name");

    assert_eq!(line_info.line_start, 29);
    assert_eq!(line_info.column_start, Some(0)); // looks like useless column info
    assert_eq!(rva, Rva(0x64f0));
    assert_eq!(file_name, "c:\\users\\user\\desktop\\self\\foo.cpp");
}
<|endoftext|>