<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() {
        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);
        }
    }
}
<commit_msg>handle invalid volumes in the 'add' interactive operation<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))
                   .and_then(|ref mut snaps| {
                       Ok(snaps.pop())
                    }) {
                Ok(Some(snap)) => {
                    latest_snap = snap;
                }
                Ok(None) => {
                    println!("No snapshots available for that volume.\n");
                    continue;
                }
                Err(e) => {
                    println!("Error listing snapshots: {}\n", e);
                    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>#![feature(process)]

extern crate docopt;

#[macro_use]
extern crate standard_error;

use docopt::Docopt;
use standard_error::StandardResult as Result;
use std::rc::Rc;

mod git;
mod author;
mod config;

use author::Author;
use config::Config;

const CONFIG_PATH: &'static str = "./partners.cfg";
const CFG: git::Config = git::Config::File(CONFIG_PATH);

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: partners info
       partners list
       partners add --nick=<nick> --name=<name> [--email=<email>]
       partners add
       partners current
       partners set <nick>

Options:
    -a, --archive  Copy everything.
";

fn print_author_list(list: &[Author]) {
    for item in list.iter() {
        println!("{}:", item.nick);
        println!("  Name:  {}", item.name);
        println!("  Email: {}", item.get_email());
    }
}

fn parse_author_line(config: &Rc<Config>, line: &str) -> Result<Author> {
    let mut parts = line.splitn(1, ' ');
    let nick = parts.next().unwrap().split('.').nth(1).unwrap().to_string();
    let name = parts.next().unwrap().to_string();
    let email = CFG.get(&format!("author.{}.email", nick)).ok();

    Ok(Author { config: config.clone(), nick: nick, name: name, email: email })
}

fn get_authors(config: &Rc<Config>) -> Result<Vec<Author>> {
    let lines = try!(CFG.list("author.\\w+.name"));
    lines.iter().map(|line| parse_author_line(config, &line)).collect()
}

fn write_author(author: &Author) -> Result<()> {
    try!(CFG.set(&format!("author.{}.name", author.nick), &*author.name));
    if let Some(ref email) = author.email {
        try!(CFG.set(&format!("author.{}.email", author.nick), &**email));
    }
    Ok(())
}

fn print_current() {
    println!("Nick:  {}", git::Config::None.get("partners.current").unwrap());
    println!("Name:  {}", git::Config::None.get("user.name").unwrap());
    println!("Email: {}", git::Config::None.get("user.email").unwrap());
}

fn main() {
    let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());

    let config = Rc::new(Config {
        domain: CFG.get("config.domain").unwrap_or_else(|_| "example.com".to_string()),
        prefix: CFG.get("config.prefix").unwrap_or_else(|_| "dev".to_string()),
        separator: CFG.get("config.separator").unwrap_or_else(|_| "+".to_string()),
    });

    if args.get_bool("list") {
        let authors = get_authors(&config).unwrap();
        print_author_list(&authors);
    } else if args.get_bool("add") {
        let email = args.get_str("--email");
        let nick = args.get_str("--nick");
        let name = args.get_str("--name");

        let email = if email.is_empty() { None } else { Some(email.to_string()) };

        let author = Author { config: config.clone(), nick: nick.to_string(), name: name.to_string(), email: email };
        write_author(&author).unwrap();
    } else if args.get_bool("current") {
        print_current();
    } else if args.get_bool("set") {
        let nick = args.get_str("<nick>");

        match get_authors(&config).unwrap().iter().find(|a| a.nick == nick) {
            Some(author) => {
                git::Config::Global.set("partners.current", &*author.nick).unwrap();
                git::Config::Global.set("user.name", &*author.name).unwrap();
                git::Config::Global.set("user.email", &*author.get_email()).unwrap();
                print_current();
            }
            None => {
                println!("no such author");
            }
        }
    }
}
<commit_msg>Don't print stuff that isn't there<commit_after>#![feature(process)]

extern crate docopt;

#[macro_use]
extern crate standard_error;

use docopt::Docopt;
use standard_error::StandardResult as Result;
use std::rc::Rc;

mod git;
mod author;
mod config;

use author::Author;
use config::Config;

const CONFIG_PATH: &'static str = "./partners.cfg";
const CFG: git::Config = git::Config::File(CONFIG_PATH);

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: partners list
       partners add --nick=<nick> --name=<name> [--email=<email>]
       partners add
       partners current
       partners set <nick>

Options:
    -a, --archive  Copy everything.
";

fn print_author_list(list: &[Author]) {
    for item in list.iter() {
        println!("{}:", item.nick);
        println!("  Name:  {}", item.name);
        println!("  Email: {}", item.get_email());
    }
}

fn parse_author_line(config: &Rc<Config>, line: &str) -> Result<Author> {
    let mut parts = line.splitn(1, ' ');
    let nick = parts.next().unwrap().split('.').nth(1).unwrap().to_string();
    let name = parts.next().unwrap().to_string();
    let email = CFG.get(&format!("author.{}.email", nick)).ok();

    Ok(Author { config: config.clone(), nick: nick, name: name, email: email })
}

fn get_authors(config: &Rc<Config>) -> Result<Vec<Author>> {
    let lines = try!(CFG.list("author.\\w+.name"));
    lines.iter().map(|line| parse_author_line(config, &line)).collect()
}

fn write_author(author: &Author) -> Result<()> {
    try!(CFG.set(&format!("author.{}.name", author.nick), &*author.name));
    if let Some(ref email) = author.email {
        try!(CFG.set(&format!("author.{}.email", author.nick), &**email));
    }
    Ok(())
}

fn print_current() {
    if let Ok(nick) = git::Config::None.get("partners.current") {
        println!("Nick:  {}", nick);
    }
    if let Ok(name) = git::Config::None.get("user.name") {
        println!("Name:  {}", name);
    }
    if let Ok(email) = git::Config::None.get("user.email") {
        println!("Email: {}", email);
    }
}

fn main() {
    let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());

    let config = Rc::new(Config {
        domain: CFG.get("config.domain").unwrap_or_else(|_| "example.com".to_string()),
        prefix: CFG.get("config.prefix").unwrap_or_else(|_| "dev".to_string()),
        separator: CFG.get("config.separator").unwrap_or_else(|_| "+".to_string()),
    });

    if args.get_bool("list") {
        let authors = get_authors(&config).unwrap();
        print_author_list(&authors);
    } else if args.get_bool("add") {
        let email = args.get_str("--email");
        let nick = args.get_str("--nick");
        let name = args.get_str("--name");

        let email = if email.is_empty() { None } else { Some(email.to_string()) };

        let author = Author { config: config.clone(), nick: nick.to_string(), name: name.to_string(), email: email };
        write_author(&author).unwrap();
    } else if args.get_bool("current") {
        print_current();
    } else if args.get_bool("set") {
        let nick = args.get_str("<nick>");

        match get_authors(&config).unwrap().iter().find(|a| a.nick == nick) {
            Some(author) => {
                git::Config::Global.set("partners.current", &*author.nick).unwrap();
                git::Config::Global.set("user.name", &*author.name).unwrap();
                git::Config::Global.set("user.email", &*author.get_email()).unwrap();
                print_current();
            }
            None => {
                println!("no such author");
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate to;

use to::{ToResult, ToError};
use clap::{App, Arg, ArgGroup};
use std::path::PathBuf;
use std::env;
use std::fs;

fn main() {
    let version = crate_version!();
    let actions = [
        Arg::with_name("get")
            .help("Show bookmark information")
            .long("info")
            .short("i"),

        // to --save,--put # Save current dir as a bookmark
        // to --save,--put foo # Save current directory as bookmark foo
        Arg::with_name("put")
            .help("Save bookmark")
            .long("save")
            .short("s"),

        // to --delete foo # Delete bookmark foo
        Arg::with_name("delete")
            .help("Delete bookmark")
            .long("delete")
            .short("d"),

        Arg::with_name("list")
            .help("List all bookmarks")
            .long("list")
            .short("l"),

        Arg::with_name("last")
            .help("Change current working directory to last")
            .long("last"),
    ];
    let matches = App::new("to")
                    .about("Bookmark directories")
                    .version(version)
                    .author("Jason Campbell <jason@artifact.sh>")
                    .arg(Arg::with_name("name")
                        .help("Name of the bookamrk")
                        .index(1))
                    .arg(Arg::with_name("directory")
                        .help("Path of the bookamrk")
                        .index(2))
                    .args(&actions)
                    .get_matches();

    let request = match to::cli::parse_matches(matches) {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    println!("CLI Request: {:?}", request);
}
<commit_msg>Adds note about next step.<commit_after>#[macro_use]
extern crate clap;
extern crate to;

use to::{ToResult, ToError};
use clap::{App, Arg, ArgGroup};
use std::path::PathBuf;
use std::env;
use std::fs;

fn main() {
    let version = crate_version!();
    let actions = [
        Arg::with_name("get")
            .help("Show bookmark information")
            .long("info")
            .short("i"),

        // to --save,--put # Save current dir as a bookmark
        // to --save,--put foo # Save current directory as bookmark foo
        Arg::with_name("put")
            .help("Save bookmark")
            .long("save")
            .short("s"),

        // to --delete foo # Delete bookmark foo
        Arg::with_name("delete")
            .help("Delete bookmark")
            .long("delete")
            .short("d"),

        Arg::with_name("list")
            .help("List all bookmarks")
            .long("list")
            .short("l"),

        Arg::with_name("last")
            .help("Change current working directory to last")
            .long("last"),
    ];
    let matches = App::new("to")
                    .about("Bookmark directories")
                    .version(version)
                    .author("Jason Campbell <jason@artifact.sh>")
                    .arg(Arg::with_name("name")
                        .help("Name of the bookamrk")
                        .index(1))
                    .arg(Arg::with_name("directory")
                        .help("Path of the bookamrk")
                        .index(2))
                    .args(&actions)
                    .get_matches();

    let request = match to::cli::parse_matches(matches) {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    println!("CLI Request: {:?}", request);

    // NEXT: Store get, put, delete, list
}
<|endoftext|>


<commit_before>extern crate mmap;
extern crate time;
extern crate timely;
extern crate getopts;

extern crate graph_map;
extern crate graph_layout;
extern crate radix_sort;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::nested::Summary::Local;
use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::dataflow::channels::pact::{Pipeline, Exchange};
use timely::drain::DrainExt;

use graph_layout::layout::*;
use graph_layout::compression::*;

use graph_map::graph_map::GraphMMap;

use radix_sort::*;

fn main () {

    /*
        The plan here is to partition the rows of the matrix into #processes parts, and the columns
        into #threads parts. This could be generally #row_parts and #col_parts, but that requires
        more thinking about things.
    */

    let threads: u32 = ::std::env::args().nth(1).unwrap().parse().unwrap();
    let filename = ::std::env::args().nth(2).unwrap();
    let nodes: u32 = ::std::env::args().nth(3).unwrap().parse().unwrap();

    timely::execute_from_args(::std::env::args().skip(4), move |root| {

        let index = root.index() as u32;
        let peers = root.peers() as u32;
        let processes = (peers / threads) as u32;
        let dst_index = index % threads;

        let start = time::precise_time_s();
        let mut going = start;

        let mut counter = 0;
        let mut sorter = RadixSorter::new();
        let hilbert = Hilbert::new();
        let mut decoder = BytewiseCached::new();
        let mut compressed = vec![];

        // let nodes: u32 = 3_563_602_788 + 1;

        let local_srcs = (nodes / processes) + 1;
        let local_dsts = (nodes / threads) + 1;

        // degree is ignored at the moment.
        // mostly just a pain to put together.
        let deg = vec![1.0f32; local_srcs as usize];
        let mut src = vec![1.0f32; local_srcs as usize];
        let mut dst = vec![0.0f32; local_dsts as usize];

        let mut input = root.scoped(|builder| {

            let (input, edges) = builder.new_input::<(u32, u32)>();
            let (cycle, ranks) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

            let ranks = edges.binary_notify(&ranks,
                                Exchange::new(|x: &(u32,u32)| x.0 as u64),
                                Pipeline,   // data are partitioned as part of the broadcast step.
                                "hilbrank",
                                vec![RootTimestamp::new(0)],
                                move |input1, input2, output, notificator| {

                // receive incoming edges (should only be iter 0)
                while let Some((_index, data)) = input1.next() {
                    counter += data.len();
                    for &(src,dst) in data.iter() {
                        let shrunk = (src / processes, dst / threads);
                        sorter.push(hilbert.entangle(shrunk), &|&x| x);
                    }

                    // should be 1GB per worker
                    if counter > (1 << 27) {
                        let mut results = sorter.finish(&|&x| x);
                        let mut compressor = Compressor::with_capacity(counter);
                        for vec in results.iter_mut() {
                            for elem in vec.drain_temp() {
                                compressor.push(elem);
                            }
                        }
                        sorter.recycle(results);
                        compressed.push(compressor.done());
                        counter = 0;
                    }
                }

                if counter > 0 {
                    // round out any remaining edges
                    let mut results = sorter.finish(&|&x| x);
                    let mut compressor = Compressor::with_capacity(counter);
                    for vec in results.iter_mut() {
                        for elem in vec.drain_temp() {
                            compressor.push(elem);
                        }
                    }
                    compressed.push(compressor.done());
                    counter = 0;
                }

                // all inputs received for iter, commence multiplication
                while let Some((iter, _)) = notificator.next() {

                    let now = time::now();

                    if index == 0 { println!("{}:{}:{}.{} starting iteration {}", now.tm_hour, now.tm_min, now.tm_sec, now.tm_nsec, iter.inner); }

                    // if the very first iteration, prepare some stuff.
                    if iter.inner == 0 {
                        // now we would merge the compressed representations, if we want to.
                        // let's measure both doing this and not doing it. :)

                    }

                    // record some timings in order to estimate per-iteration times
                    // if iter.inner == 0  && index == 0 { println!("src: {}, dst: {}, edges: {}", src.len(), rev.len(), trn.len()); }
                    if iter.inner == 10 && index == 0 { going = time::precise_time_s(); }
                    if iter.inner == 20 && index == 0 { println!("average: {}", (time::precise_time_s() - going) / 10.0 ); }

                    // prepare src for transmitting to destinations
                    for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }
                    for d in 0..dst.len() { dst[d] = 0.0; }

                    // do the multiplication for each compressed sequence
                    for sequence in &compressed {
                        for element in sequence.decompress() {
                            let (s,d) = decoder.detangle(element);
                            dst[d as usize] += src[s as usize];
                        }
                    }

                    for s in 0..src.len() { src[s] = 0.0; }

                    // now we need to send some stuff to other threads.
                    // TODO : This could be done as blocks, as they mostly go to the same place.
                    let mut session = output.session(&iter);
                    for (index, &value) in dst.iter().enumerate() {
                        if value != 0.0 {
                            session.give((index as u32 * threads + dst_index, value));
                        }
                    }
                }

                // receive data from threads, accumulate in src
                while let Some((iter, data)) = input2.next() {
                    notificator.notify_at(&iter);
                    for &(node, rank) in data.iter() {
                        src[(node / processes) as usize] += rank;
                    }
                }
            });

            let mut aggregates = vec![0.0f32; (nodes / peers) as usize];
            // want to do aggregation so that aggregates are local to the folks who will need them.
            // that is, (node, rank) goes to some worker on machine#: name % processes
            let aggregated = ranks.unary_notify(
                Exchange::new(move |&(n,_): &(u32,f32)| (((n % processes) * threads) + ((n / processes) % threads)) as u64),
                "aggregation",
                vec![],
                move |input, output, notificator | {

                while let Some((time, data)) = input.next() {
                    notificator.notify_at(time);
                    for &(node, rank) in data.iter() {
                        aggregates[(node / peers) as usize] += rank;
                    }
                }

                while let Some((time, _)) = notificator.next() {
                    let mut session = output.session(&time);
                    for (node, &rank) in aggregates.iter().enumerate() {
                        if rank != 0.0 {
                            session.give((node as u32 * peers, rank));
                        }
                    }
                }
            });

            // data are now aggregated, and only need to be broadcast out to the intended recipients
            // ideally, none of this actually ends up hitting the network. also, it seems like it
            // shouldn't even require looking at `node`, on account of already putting all the data
            // on the right processes. We should be able to use (index / threads) * threads, I think.
            let mut exchanged = aggregated.exchange(move |&(node, _)| ((node % processes) * threads) as u64);
            for i in 1..threads {
                exchanged = aggregated.exchange(move |&(node, _)| (((node % processes) * threads) + i) as u64)
                                      .concat(&exchanged);
            }

            exchanged.connect_loop(cycle);

            input
        });

        // introduce edges into the computation;
        // allow mmaped file to drop
        {
            let graph = GraphMMap::new(&filename);
            for node in 0..graph.nodes() {
                if node as u32 % peers == index {
                    for dst in graph.edges(node) {
                        input.send((node as u32, *dst as u32));
                    }
                }
            }
        }
        input.close();
        while root.step() { }

        if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
    });

}
<commit_msg>lolwoops<commit_after>extern crate mmap;
extern crate time;
extern crate timely;
extern crate getopts;

extern crate graph_map;
extern crate graph_layout;
extern crate radix_sort;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::nested::Summary::Local;
use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::dataflow::channels::pact::{Pipeline, Exchange};
use timely::drain::DrainExt;

use graph_layout::layout::*;
use graph_layout::compression::*;

use graph_map::graph_map::GraphMMap;

use radix_sort::*;

fn main () {

    /*
        The plan here is to partition the rows of the matrix into #processes parts, and the columns
        into #threads parts. This could be generally #row_parts and #col_parts, but that requires
        more thinking about things.
    */

    let threads: u32 = ::std::env::args().nth(1).unwrap().parse().unwrap();
    let filename = ::std::env::args().nth(2).unwrap();
    let nodes: u32 = ::std::env::args().nth(3).unwrap().parse().unwrap();

    timely::execute_from_args(::std::env::args().skip(4), move |root| {

        let index = root.index() as u32;
        let peers = root.peers() as u32;
        let processes = (peers / threads) as u32;
        let dst_index = index % threads;

        let start = time::precise_time_s();
        let mut going = start;

        let mut counter = 0;
        let mut sorter = RadixSorter::new();
        let hilbert = Hilbert::new();
        let mut decoder = BytewiseCached::new();
        let mut compressed = vec![];

        // let nodes: u32 = 3_563_602_788 + 1;

        let local_srcs = (nodes / processes) + 1;
        let local_dsts = (nodes / threads) + 1;

        // degree is ignored at the moment.
        // mostly just a pain to put together.
        let deg = vec![1.0f32; local_srcs as usize];
        let mut src = vec![1.0f32; local_srcs as usize];
        let mut dst = vec![0.0f32; local_dsts as usize];

        let mut input = root.scoped(|builder| {

            let (input, edges) = builder.new_input::<(u32, u32)>();
            let (cycle, ranks) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

            let ranks = edges.binary_notify(&ranks,
                                Exchange::new(|x: &(u32,u32)| ((x.0 % machines) * threads + (x.1 % threads)) as u64),
                                Pipeline,   // data are partitioned as part of the broadcast step.
                                "hilbrank",
                                vec![RootTimestamp::new(0)],
                                move |input1, input2, output, notificator| {

                // receive incoming edges (should only be iter 0)
                while let Some((_index, data)) = input1.next() {
                    counter += data.len();
                    for &(src,dst) in data.iter() {
                        let shrunk = (src / processes, dst / threads);
                        sorter.push(hilbert.entangle(shrunk), &|&x| x);
                    }

                    // should be 1GB per worker
                    if counter > (1 << 27) {
                        let mut results = sorter.finish(&|&x| x);
                        let mut compressor = Compressor::with_capacity(counter);
                        for vec in results.iter_mut() {
                            for elem in vec.drain_temp() {
                                compressor.push(elem);
                            }
                        }
                        sorter.recycle(results);
                        compressed.push(compressor.done());
                        counter = 0;
                    }
                }

                if counter > 0 {
                    // round out any remaining edges
                    let mut results = sorter.finish(&|&x| x);
                    let mut compressor = Compressor::with_capacity(counter);
                    for vec in results.iter_mut() {
                        for elem in vec.drain_temp() {
                            compressor.push(elem);
                        }
                    }
                    compressed.push(compressor.done());
                    counter = 0;
                }

                // all inputs received for iter, commence multiplication
                while let Some((iter, _)) = notificator.next() {

                    let now = time::now();

                    if index == 0 { println!("{}:{}:{}.{} starting iteration {}", now.tm_hour, now.tm_min, now.tm_sec, now.tm_nsec, iter.inner); }

                    // if the very first iteration, prepare some stuff.
                    if iter.inner == 0 {
                        // now we would merge the compressed representations, if we want to.
                        // let's measure both doing this and not doing it. :)

                    }

                    // record some timings in order to estimate per-iteration times
                    // if iter.inner == 0  && index == 0 { println!("src: {}, dst: {}, edges: {}", src.len(), rev.len(), trn.len()); }
                    if iter.inner == 10 && index == 0 { going = time::precise_time_s(); }
                    if iter.inner == 20 && index == 0 { println!("average: {}", (time::precise_time_s() - going) / 10.0 ); }

                    // prepare src for transmitting to destinations
                    for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }
                    for d in 0..dst.len() { dst[d] = 0.0; }

                    // do the multiplication for each compressed sequence
                    for sequence in &compressed {
                        for element in sequence.decompress() {
                            let (s,d) = decoder.detangle(element);
                            dst[d as usize] += src[s as usize];
                        }
                    }

                    for s in 0..src.len() { src[s] = 0.0; }

                    // now we need to send some stuff to other threads.
                    // TODO : This could be done as blocks, as they mostly go to the same place.
                    let mut session = output.session(&iter);
                    for (index, &value) in dst.iter().enumerate() {
                        if value != 0.0 {
                            session.give((index as u32 * threads + dst_index, value));
                        }
                    }
                }

                // receive data from threads, accumulate in src
                while let Some((iter, data)) = input2.next() {
                    notificator.notify_at(&iter);
                    for &(node, rank) in data.iter() {
                        src[(node / processes) as usize] += rank;
                    }
                }
            });

            let mut aggregates = vec![0.0f32; (nodes / peers) as usize];
            // want to do aggregation so that aggregates are local to the folks who will need them.
            // that is, (node, rank) goes to some worker on machine#: name % processes
            let aggregated = ranks.unary_notify(
                Exchange::new(move |&(n,_): &(u32,f32)| (((n % processes) * threads) + ((n / processes) % threads)) as u64),
                "aggregation",
                vec![],
                move |input, output, notificator | {

                while let Some((time, data)) = input.next() {
                    notificator.notify_at(time);
                    for &(node, rank) in data.iter() {
                        aggregates[(node / peers) as usize] += rank;
                    }
                }

                while let Some((time, _)) = notificator.next() {
                    let mut session = output.session(&time);
                    for (node, &rank) in aggregates.iter().enumerate() {
                        if rank != 0.0 {
                            session.give((node as u32 * peers, rank));
                        }
                    }
                }
            });

            // data are now aggregated, and only need to be broadcast out to the intended recipients
            // ideally, none of this actually ends up hitting the network. also, it seems like it
            // shouldn't even require looking at `node`, on account of already putting all the data
            // on the right processes. We should be able to use (index / threads) * threads, I think.
            let mut exchanged = aggregated.exchange(move |&(node, _)| ((node % processes) * threads) as u64);
            for i in 1..threads {
                exchanged = aggregated.exchange(move |&(node, _)| (((node % processes) * threads) + i) as u64)
                                      .concat(&exchanged);
            }

            exchanged.connect_loop(cycle);

            input
        });

        // introduce edges into the computation;
        // allow mmaped file to drop
        {
            let graph = GraphMMap::new(&filename);
            for node in 0..graph.nodes() {
                if node as u32 % peers == index {
                    for dst in graph.edges(node) {
                        input.send((node as u32, *dst as u32));
                    }
                }
            }
        }
        input.close();
        while root.step() { }

        if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
    });

}
<|endoftext|>


<commit_before>extern crate crypto;
extern crate num;
extern crate rustc_serialize;
extern crate time;
extern crate protobuf;

use address::Address;
use node::{Node, Deserialize, Serialize, UdpNode};
use server::Server;
use std::env;
use std::sync::mpsc;

mod address;
mod messages;
mod node;
mod node_bucket;
mod routing_table;
mod server;

fn main() {
    let args: Vec<String> = env::args().collect();
    let secret = args[1].clone();
    let address = Address::for_content(secret.as_str());
    let port = args[2].clone().parse::<u16>().unwrap();

    let (tx, messages) = mpsc::channel();
    server::UdpServer::new(port).start(tx);

    let mut my_origin = messages::Node::new();
    my_origin.set_id(address.to_str());
    my_origin.set_ip_address(vec![127, 0, 0, 1]);
    my_origin.set_port(port as u32);

    let mut routing_table = routing_table::RoutingTable::new(8, address);

    if let Some(bootstrap) = args.get(3) {
        println!("Bootstrapping");
        let mut find_node_query = messages::FindNodeQuery::new();
        find_node_query.set_origin(my_origin.clone());
        find_node_query.set_target(address.to_str());
        let mut envelope = messages::Envelope::new();
        envelope.set_message_type(messages::Envelope_Type::FIND_NODE_QUERY);
        envelope.set_find_node_query(find_node_query);
        let node = UdpNode::new(Address::null(), ("127.0.0.1", bootstrap.clone().parse::<u16>().unwrap()));
        node.send(envelope);
    }

    loop {
        let (size, buf) = messages.recv().unwrap();
        let message = protobuf::parse_from_bytes::<messages::Envelope>(&buf[0..size]).unwrap();
        println!("Message: {:?}", message);

        match message.get_message_type() {
            messages::Envelope_Type::FIND_NODE_QUERY => {
                let find_node_query = message.get_find_node_query();
                let origin = node::UdpNode::deserialize(find_node_query.get_origin());
                let target = Address::from_str(find_node_query.get_target());

                {
                    let nodes: Vec<&Box<node::Node>> = routing_table.nearest_to(&target);
                    println!("Nearest nodes: {:?}", nodes);
                    let nodes: Vec<messages::Node> = nodes.iter().map(|n| n.serialize()).collect();
                    let mut find_node_response = messages::FindNodeResponse::new();
                    find_node_response.set_origin(my_origin.clone());
                    find_node_response.set_nodes(
                        protobuf::RepeatedField::from_slice(nodes.as_slice())
                    );
                    let mut response = messages::Envelope::new();
                    response.set_message_type(messages::Envelope_Type::FIND_NODE_RESPONSE);
                    response.set_find_node_response(find_node_response);

                    origin.send(response);
                }

                routing_table.insert(origin);
            }
            messages::Envelope_Type::FIND_NODE_RESPONSE => {
                let find_node_response = message.get_find_node_response();
                let origin = node::UdpNode::deserialize(find_node_response.get_origin());
                routing_table.insert(origin);

                for node in find_node_response.get_nodes() {
                    let node = node::UdpNode::deserialize(node);
                    routing_table.insert(node);
                }
            }
        }
        println!("Routing Table: {:?}", routing_table);
    }
}
<commit_msg>Put bootstrap address in variable<commit_after>extern crate crypto;
extern crate num;
extern crate rustc_serialize;
extern crate time;
extern crate protobuf;

use address::Address;
use node::{Node, Deserialize, Serialize, UdpNode};
use server::Server;
use std::env;
use std::sync::mpsc;

mod address;
mod messages;
mod node;
mod node_bucket;
mod routing_table;
mod server;

fn main() {
    let args: Vec<String> = env::args().collect();
    let secret = args[1].clone();
    let address = Address::for_content(secret.as_str());
    let port = args[2].clone().parse::<u16>().unwrap();

    let (tx, messages) = mpsc::channel();
    server::UdpServer::new(port).start(tx);

    let mut my_origin = messages::Node::new();
    my_origin.set_id(address.to_str());
    my_origin.set_ip_address(vec![127, 0, 0, 1]);
    my_origin.set_port(port as u32);

    let mut routing_table = routing_table::RoutingTable::new(8, address);

    if let Some(bootstrap) = args.get(3) {
        println!("Bootstrapping");
        let bootstrap_address = ("127.0.0.1", bootstrap.clone().parse::<u16>().unwrap());
        let mut find_node_query = messages::FindNodeQuery::new();
        find_node_query.set_origin(my_origin.clone());
        find_node_query.set_target(address.to_str());
        let mut envelope = messages::Envelope::new();
        envelope.set_message_type(messages::Envelope_Type::FIND_NODE_QUERY);
        envelope.set_find_node_query(find_node_query);
        let node = UdpNode::new(Address::null(), bootstrap_address);
        node.send(envelope);
    }

    loop {
        let (size, buf) = messages.recv().unwrap();
        let message = protobuf::parse_from_bytes::<messages::Envelope>(&buf[0..size]).unwrap();
        println!("Message: {:?}", message);

        match message.get_message_type() {
            messages::Envelope_Type::FIND_NODE_QUERY => {
                let find_node_query = message.get_find_node_query();
                let origin = node::UdpNode::deserialize(find_node_query.get_origin());
                let target = Address::from_str(find_node_query.get_target());

                {
                    let nodes: Vec<&Box<node::Node>> = routing_table.nearest_to(&target);
                    println!("Nearest nodes: {:?}", nodes);
                    let nodes: Vec<messages::Node> = nodes.iter().map(|n| n.serialize()).collect();
                    let mut find_node_response = messages::FindNodeResponse::new();
                    find_node_response.set_origin(my_origin.clone());
                    find_node_response.set_nodes(
                        protobuf::RepeatedField::from_slice(nodes.as_slice())
                    );
                    let mut response = messages::Envelope::new();
                    response.set_message_type(messages::Envelope_Type::FIND_NODE_RESPONSE);
                    response.set_find_node_response(find_node_response);

                    origin.send(response);
                }

                routing_table.insert(origin);
            }
            messages::Envelope_Type::FIND_NODE_RESPONSE => {
                let find_node_response = message.get_find_node_response();
                let origin = node::UdpNode::deserialize(find_node_response.get_origin());
                routing_table.insert(origin);

                for node in find_node_response.get_nodes() {
                    let node = node::UdpNode::deserialize(node);
                    routing_table.insert(node);
                }
            }
        }
        println!("Routing Table: {:?}", routing_table);
    }
}
<|endoftext|>


<commit_before>#[crate_id = "rboy"];

extern mod extra;
extern mod sdl;

use cpu::CPU;
use extra::getopts;
use extra::comm::DuplexStream;
use extra::arc::RWArc;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;

fn main() {
	let args: ~[~str] = std::os::args();
	let program = args[0].clone() + " <filename>";

	let opts = ~[ getopts::groups::optflag("s", "serial", "Output serial to stdout") ];
	let matches = match getopts::groups::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename: ~str = if !matches.free.is_empty() {
		matches.free[0].clone()
	} else {
		println!("{}", getopts::groups::usage(program, opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*2, 144*2, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = DuplexStream::new();
	let rawscreen = ~[0x00u8,.. 160*144*3];
	let arc = RWArc::new(rawscreen);
	let arc2 = arc.clone();
	do spawn { cpuloop(&cpustream, arc2, filename, &matches); }

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Some(_) => recalculate_screen(screen, &arc),
			None => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
	sdlstream.send(Poweroff);
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &RWArc<~[u8]>) {
	arc.read(|data|
		for y in range(0, 144) {
			for x in range(0, 160) {
				screen.fill_rect(
					Some(sdl::Rect { x: (x*2) as i16, y: (y*2) as i16, w: 2, h: 2 }),
					sdl::video::RGB(data[y*160*3 + x*3 + 0],
					                data[y*160*3 + x*3 + 1],
					                data[y*160*3 + x*3 + 2])
				);
			}
		}
	);
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	Poweroff,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: RWArc<~[u8]>, filename: ~str, matches: &getopts::Matches) {
	let mut c = CPU::new(filename);
	c.mmu.serial.enabled = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				arc.write(|data|
					for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				);
				channel.try_send(0);
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			None => {},
			Some(Poweroff) => { break; },
			Some(KeyUp(key)) => c.mmu.keypad.keyup(key),
			Some(KeyDown(key)) => c.mmu.keypad.keydown(key),
		};
	}
}
<commit_msg>Allow SpeedUp/SlowDown emulator<commit_after>#[crate_id = "rboy"];

extern mod extra;
extern mod sdl;

use cpu::CPU;
use extra::getopts;
use extra::comm::DuplexStream;
use extra::arc::RWArc;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;

fn main() {
	let args: ~[~str] = std::os::args();
	let program = args[0].clone() + " <filename>";

	let opts = ~[ getopts::groups::optflag("s", "serial", "Output serial to stdout") ];
	let matches = match getopts::groups::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename: ~str = if !matches.free.is_empty() {
		matches.free[0].clone()
	} else {
		println!("{}", getopts::groups::usage(program, opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*2, 144*2, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = DuplexStream::new();
	let rawscreen = ~[0x00u8,.. 160*144*3];
	let arc = RWArc::new(rawscreen);
	let arc2 = arc.clone();
	do spawn { cpuloop(&cpustream, arc2, filename, &matches); }

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Some(_) => recalculate_screen(screen, &arc),
			None => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
	sdlstream.send(Poweroff);
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &RWArc<~[u8]>) {
	arc.read(|data|
		for y in range(0, 144) {
			for x in range(0, 160) {
				screen.fill_rect(
					Some(sdl::Rect { x: (x*2) as i16, y: (y*2) as i16, w: 2, h: 2 }),
					sdl::video::RGB(data[y*160*3 + x*3 + 0],
					                data[y*160*3 + x*3 + 1],
					                data[y*160*3 + x*3 + 2])
				);
			}
		}
	);
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	Poweroff,
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: RWArc<~[u8]>, filename: ~str, matches: &getopts::Matches) {
	let mut c = CPU::new(filename);
	c.mmu.serial.enabled = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let mut periodic = timer.periodic(8);

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				arc.write(|data|
					for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				);
				channel.try_send(0);
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			None => {},
			Some(Poweroff) => { break; },
			Some(KeyUp(key)) => c.mmu.keypad.keyup(key),
			Some(KeyDown(key)) => c.mmu.keypad.keydown(key),
			Some(SpeedUp) => periodic = timer.periodic(1),
			Some(SlowDown) => periodic = timer.periodic(8),
		};
	}
}
<|endoftext|>


<commit_before><commit_msg>Add russian roulette render test<commit_after><|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate breezyvps;

fn sc_do(do_matches: &clap::ArgMatches) {
    let mut verbose = false;
    if do_matches.is_present("verbose") {
        verbose = true;
    }
    if let Some(create_droplet_matches) = do_matches.subcommand_matches("create_droplet") {
        if let Some(name) = create_droplet_matches.value_of("name") {
            breezyvps::digitalocean::create_droplet_by_name(name);
        } else {
            println!("Missing required name parameter!");
        }
        return;
    }
    if let Some(destroy_droplet_matches) = do_matches.subcommand_matches("destroy_droplet") {
        if let Some(name) = destroy_droplet_matches.value_of("name") {
            breezyvps::digitalocean::destroy_droplet_by_name(name);
        } else {
            println!("Missing required name parameter!");
        }
        return;
    }
    if let Some(create_ssh_key_matches) = do_matches.subcommand_matches("create_sshkey") {
        if let Some(name) = create_ssh_key_matches.value_of("name") {
            breezyvps::digitalocean::create_sshkey(name);
        } else {
            println!("Missing required sshkey name parameter!");
        }
        return;
    }
}

fn main() {
    let matches = clap_app!(myapp =>
        (version: "0.0.1")
        (author: "Michael Xu <michaeljxu11@gmail.com>")
        (about: "One stop shop for common command line goodness")
        (@arg verbose: -v ... "Enable verbose output")
        (@subcommand do =>
            (about: "Doctl wrapper")
            (version: "0.0.0")
            (author: "Michael Xu <michaeljxu11@gmail.com>")
            (@arg verbose: -v --verbose "Print test information verbosely")
            (@subcommand create_droplet =>
                (about: "Create a new droplet")
                (@arg name: +required "Name of the droplet, must be unique")
                (@arg config: -c "Which configuration to use [small|medium|large]")
            )
            (@subcommand destroy_droplet =>
                (about: "Destroy a droplet by name")
                (@arg name: +required "Name of the droplet to destroy completely")
            )
            (@subcommand create_sshkey =>
                (about: "Add an ssh key, which will be added upon instance creation")
                (@arg name: +required "Name of the new ssh keys")
            )
        )
    ).get_matches();

    // Vary the output based on how many times the user used the "verbose" flag
    // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v'
    match matches.occurrences_of("v") {
        0 => println!("No verbose info"),
        1 => println!("Some verbose info"),
        2 => println!("Tons of verbose info"),
        3 | _ => println!("Don't be crazy"),
    }

    // You can handle information about subcommands by requesting their matches by name
    // (as below), requesting just the name used, or both at the same time
    if let Some(matches) = matches.subcommand_matches("do") {
        sc_do(matches);
        return;
    }
}
<commit_msg>get rid of verbose thingy<commit_after>#[macro_use]
extern crate clap;
extern crate breezyvps;

fn sc_do(do_matches: &clap::ArgMatches) {
    let mut verbose = false;
    if do_matches.is_present("verbose") {
        verbose = true;
    }
    if let Some(create_droplet_matches) = do_matches.subcommand_matches("create_droplet") {
        if let Some(name) = create_droplet_matches.value_of("name") {
            breezyvps::digitalocean::create_droplet_by_name(name);
        } else {
            println!("Missing required name parameter!");
        }
        return;
    }
    if let Some(destroy_droplet_matches) = do_matches.subcommand_matches("destroy_droplet") {
        if let Some(name) = destroy_droplet_matches.value_of("name") {
            breezyvps::digitalocean::destroy_droplet_by_name(name);
        } else {
            println!("Missing required name parameter!");
        }
        return;
    }
    if let Some(create_ssh_key_matches) = do_matches.subcommand_matches("create_sshkey") {
        if let Some(name) = create_ssh_key_matches.value_of("name") {
            breezyvps::digitalocean::create_sshkey(name);
        } else {
            println!("Missing required sshkey name parameter!");
        }
        return;
    }
}

fn main() {
    let matches = clap_app!(myapp =>
        (version: "0.0.1")
        (author: "Michael Xu <michaeljxu11@gmail.com>")
        (about: "One stop shop for common command line goodness")
        (@arg verbose: -v ... "Enable verbose output")
        (@subcommand do =>
            (about: "Doctl wrapper")
            (version: "0.0.0")
            (author: "Michael Xu <michaeljxu11@gmail.com>")
            (@arg verbose: -v --verbose "Print test information verbosely")
            (@subcommand create_droplet =>
                (about: "Create a new droplet")
                (@arg name: +required "Name of the droplet, must be unique")
                (@arg config: -c "Which configuration to use [small|medium|large]")
            )
            (@subcommand destroy_droplet =>
                (about: "Destroy a droplet by name")
                (@arg name: +required "Name of the droplet to destroy completely")
            )
            (@subcommand create_sshkey =>
                (about: "Add an ssh key, which will be added upon instance creation")
                (@arg name: +required "Name of the new ssh keys")
            )
        )
    ).get_matches();

//    // Vary the output based on how many times the user used the "verbose" flag
//    // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v'
//    match matches.occurrences_of("v") {
//        0 => println!("No verbose info"),
//        1 => println!("Some verbose info"),
//        2 => println!("Tons of verbose info"),
//        3 | _ => println!("Don't be crazy"),
//    }

    // You can handle information about subcommands by requesting their matches by name
    // (as below), requesting just the name used, or both at the same time
    if let Some(matches) = matches.subcommand_matches("do") {
        sc_do(matches);
        return;
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate pnet;

use getopts::Options;
use std::os;
use std::net::{SocketAddr};
use std::str::FromStr;

use pnet::transport::TransportProtocol::{Ipv4};
use pnet::old_packet::ip::IpNextHeaderProtocols;
use pnet::transport::TransportChannelType::{Layer3};
use pnet::transport::{transport_channel};

fn main() {
    let args: Vec<String> = os::args();
    let mut opts = Options::new();
    let matches = match opts.parse(args.tail()) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };
    let addr_arg = matches.free[0].as_slice();
    let addr:SocketAddr = FromStr::from_str(addr_arg.as_slice()).unwrap();

    let protocol = Layer3(IpNextHeaderProtocols::Test1);
    let (mut tx, mut rx) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel:
                        {}", e)
    };
}
<commit_msg>We don't need a SocketAddr, just IpAddr. No need for port.<commit_after>extern crate getopts;
extern crate pnet;

use getopts::Options;
use std::os;
use std::net::{IpAddr};
use std::str::FromStr;

use pnet::transport::TransportProtocol::{Ipv4};
use pnet::old_packet::ip::IpNextHeaderProtocols;
use pnet::transport::TransportChannelType::{Layer3};
use pnet::transport::{transport_channel};

fn main() {
    let args: Vec<String> = os::args();
    let mut opts = Options::new();
    let matches = match opts.parse(args.tail()) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };
    let addr_arg = matches.free[0].as_slice();
    let addr: IpAddr = FromStr::from_str(addr_arg.as_slice()).unwrap();

    let protocol = Layer3(IpNextHeaderProtocols::Test1);
    let (mut tx, mut rx) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel:
                        {}", e)
    };
}
<|endoftext|>