<commit_before>
extern crate clap;
use clap::{Arg, App};

use std::ops::Index;
use std::io::{self, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::{OsString,OsStr};

use std::collections::HashSet;
use std::collections::HashMap;

extern crate petgraph;
use petgraph::Graph;
use petgraph::prelude::NodeIndex;
use petgraph::visit::EdgeRef;
use petgraph::dot::{Dot, Config};

extern crate walkdir;
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

//-----------------------------------------------------------------------------

#[derive(Debug)]
enum IncludeStatus {
    Relative,     // e.g. <vector>
    Absolute,     // e.g. /usr/include/c++/4.8/vector
    FailedLookup, // Failed to locate an absolute include path.
}

#[derive(Debug)]
struct Include {
    path: PathBuf,
    is_system_include: bool,
    status: IncludeStatus,
}

impl Include {
//    fn new(path: &Path, is_sys: bool, status: IncludeStatus) -> Include {
//        Include{path: PathBuf::from(path),
//            is_system_include: is_sys,
//            status: status}
//    }

    fn new_relative(name: &str, is_sys: bool) -> Include {
        Include{path: PathBuf::from(name),
            is_system_include: is_sys,
            status: IncludeStatus::Relative}
    }

    fn as_absolute(&self, absolute_path: &Path) -> Include {
        Include{path: PathBuf::from(absolute_path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::Absolute}
    }

    fn as_failed_lookup(&self) -> Include {
        Include{path: PathBuf::from(&self.path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::FailedLookup}
    }
}

//-----------------------------------------------------------------------------

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

//-----------------------------------------------------------------------------

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &Include, local_search_path: &Path, search_paths: &[PathBuf]) -> Include {

    let local_full_path = local_search_path.join(&include.path);
    if local_full_path.exists() {
        return include.as_absolute(&local_full_path);
    }

    for search_prefix in search_paths {
        let full_path = search_prefix.join(&include.path);
        if full_path.exists() {
            return include.as_absolute(&full_path);
        }
    }
    include.as_failed_lookup()
}

//-----------------------------------------------------------------------------

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<Include>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"#include ([<|"])(.*)[>|"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    for cap in RE.captures_iter(&text) {
        let inc_symbol = cap.at(1).unwrap_or("<");
        let is_system_include = inc_symbol == "<";
        match cap.at(2) {
            Some(include_name) => includes.push(Include::new_relative(include_name, is_system_include)),
            None => {}
        }
    }

    Ok(includes)
}

//-----------------------------------------------------------------------------

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version("0.1.0")
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("output_format")
//            .help("The output format to produce: PDF, DOT, PNG")
//            .value_name("OUTPUT")
//            .short("o"))
        .arg(Arg::with_name("source")
            .help("The source directory to scan")
            .value_name("PATH")
            .required(true)
            .multiple(false)
            .index(1))
        .get_matches();

    let root_dir_string = match args.value_of("source") {
        Some(path) => path,
        None => panic!("Unable to parse directory argument."),
    };

    println!("Scanning path: {}", root_dir_string);
    let root_dir = Path::new(root_dir_string);

    if !root_dir.exists() {
        println!("Unable to access directory: {}", root_dir.display());
    }

    // Create a list of default system include paths.
    let mut search_paths = Vec::new();
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsStr::new("h"));
    extensions.insert(OsStr::new("cpp"));
    extensions.insert(OsStr::new("hpp"));

    let mut input_queue: HashSet<String> = HashSet::new();

    // Collect all the files to scan
    let walker = WalkDir::new(root_dir).into_iter();
    for entry in walker.filter_entry(|e| !is_hidden(e)) {
        let entry = entry.unwrap(); // What error values could there be?
        let path = entry.path().canonicalize().unwrap();

        // If the file extension matches our set, queue full path for processing.
        match path.extension() {
            Some(ext) => {
                if extensions.contains(ext) {
                    input_queue.insert(path.to_str().unwrap().to_owned());
                }
            }
            None => {}
        }
    }

    // Maps source_path => Vec<include_path>
    //let mut include_table = HashMap::new();
    //let mut graph = Graph::<&str, &str>::new();

    // The graph can only store &str or &osStr, so keep the actual owned strings in a MashMap.
    //let mut all_files = HashSet::<OsString>::new();
    //let mut graph = DiGraphMap::<&OsStr, u32>::new();
    let mut graph = Graph::<OsString, bool>::new();
    let mut indices = HashMap::<OsString, NodeIndex>::new();

    for path_str in input_queue {
        let path = Path::new(&path_str);
        let includes_result = scan_file_for_includes(path);
        match includes_result {
            Ok(includes) => {
                let local_dir = path.parent().unwrap(); // strip the file name

                // Convert relative includes to absolute includes
                let absolute_includes: Vec<_> = includes.iter()
                    .map(|inc| find_absolute_include_path(inc, local_dir, &search_paths))
                    .collect();

                for inc in absolute_includes {

                    let src_str: OsString = path.as_os_str().to_owned();
                    let dst_str: OsString = inc.path.as_os_str().to_owned();

                    // Get the NodeIndex from the cache, on create a new node and add to the cache.
                    // Current bug: both the hash table and the graph contain owned copies
                    // of the OsString key. This is wasteful.
                    // Also, we need to clone the entry result to avoid borrowing the indices table.
                    let src_node = indices.entry(src_str.clone()).or_insert(graph.add_node(src_str)).clone();
                    let dst_node = indices.entry(dst_str.clone()).or_insert(graph.add_node(dst_str)).clone();

                    graph.add_edge(src_node.clone(), dst_node.clone(), true);
                }
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", path, err);
            }
        };
    }

    // Print all the includes
    for node_idx in graph.node_indices() {
        for edge in graph.edges(node_idx) {
            let src_str = graph.index(edge.source());
            let dst_str = graph.index(edge.target());
            println!("{:?} -> {:?}", src_str, dst_str);
        }
    }

    println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
}
<commit_msg>Fixed duplicate graph nodes<commit_after>
extern crate clap;
use clap::{Arg, App};

use std::io::{self, Read, Write};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::{OsString, OsStr};
use std::error::Error;

use std::collections::HashSet;
use std::collections::HashMap;
use std::collections::hash_map::Entry;

extern crate petgraph;
use petgraph::Graph;
use petgraph::prelude::NodeIndex;
use petgraph::dot::{Dot, Config};

extern crate walkdir;
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

// -----------------------------------------------------------------------------

#[derive(Debug)]
enum IncludeStatus {
    Relative, // e.g. <vector>
    Absolute, // e.g. /usr/include/c++/4.8/vector
    FailedLookup, // Failed to resolve an absolute file path.
}

#[derive(Debug)]
struct Include {
    path: PathBuf,
    is_system_include: bool,
    status: IncludeStatus,
}

impl Include {
    fn new_relative(name: &str, is_sys: bool) -> Include {
        Include {
            path: PathBuf::from(name),
            is_system_include: is_sys,
            status: IncludeStatus::Relative,
        }
    }

    fn as_absolute(&self, absolute_path: &Path) -> Include {
        Include {
            path: PathBuf::from(absolute_path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::Absolute,
        }
    }

    fn as_failed_lookup(&self) -> Include {
        Include {
            path: PathBuf::from(&self.path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::FailedLookup,
        }
    }
}

// -----------------------------------------------------------------------------

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

// -----------------------------------------------------------------------------

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &Include,
                              local_search_path: &Path,
                              search_paths: &[PathBuf])
                              -> Include {

    let local_full_path = local_search_path.join(&include.path);
    if local_full_path.exists() {
        return include.as_absolute(&local_full_path);
    }

    for search_prefix in search_paths {
        let full_path = search_prefix.join(&include.path);
        if full_path.exists() {
            return include.as_absolute(&full_path);
        }
    }
    include.as_failed_lookup()
}

// -----------------------------------------------------------------------------

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<Include>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The second (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"#include ([<|"])(.*)[>|"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    for cap in RE.captures_iter(&text) {
        let inc_symbol = cap.at(1).unwrap_or("<");
        let is_system_include = inc_symbol == "<";
        match cap.at(2) {
            Some(include_name) => {
                includes.push(Include::new_relative(include_name, is_system_include))
            }
            None => {}
        }
    }

    Ok(includes)
}

// -----------------------------------------------------------------------------

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version("0.1.0")
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("output_format")
//            .help("The output format to produce: PDF, DOT, PNG")
//            .value_name("OUTPUT")
//            .short("o"))
        .arg(Arg::with_name("source")
            .help("The source directory to scan")
            .value_name("PATH")
            .required(true)
            .multiple(false)
            .index(1))
        .get_matches();

    let root_dir_string = match args.value_of("source") {
        Some(path) => path,
        None => panic!("Unable to parse directory argument."),
    };

    println!("Scanning path: {}", root_dir_string);
    let root_dir = Path::new(root_dir_string);

    if !root_dir.exists() {
        println!("Unable to access directory: {}", root_dir.display());
    }

    // Create a list of default system include paths.
    let mut search_paths = Vec::new();
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsStr::new("h"));
    extensions.insert(OsStr::new("cpp"));
    extensions.insert(OsStr::new("hpp"));

    let mut input_queue: HashSet<String> = HashSet::new();

    // Collect all the files to scan
    let walker = WalkDir::new(root_dir).into_iter();
    for entry in walker.filter_entry(|e| !is_hidden(e)) {
        let entry = entry.unwrap(); // What error values could there be?
        let path = entry.path().canonicalize().unwrap();

        // If the file extension matches our set, queue full path for processing.
        match path.extension() {
            Some(ext) => {
                if extensions.contains(ext) {
                    input_queue.insert(path.to_str().unwrap().to_owned());
                }
            }
            None => {}
        }
    }

    // Graph of all the tracked files
    let mut graph = Graph::<OsString, bool>::new();
    let mut indices = HashMap::<OsString, NodeIndex>::new();

    for path_str in input_queue {
        let path = Path::new(&path_str);
        let includes_result = scan_file_for_includes(path);
        match includes_result {
            Ok(includes) => {
                let local_dir = path.parent().unwrap(); // strip the file name

                // Convert relative includes to absolute includes
                let absolute_includes: Vec<_> = includes.iter()
                    .map(|inc| find_absolute_include_path(inc, local_dir, &search_paths))
                    .collect();

                for inc in absolute_includes {

                    // Get an existing NodeIndex from the graph, on create a new node.
                    let src_str: OsString = path.as_os_str().to_owned();
                    let dst_str: OsString = inc.path.as_os_str().to_owned();

                    // These functions should work, but currently create duplicate nodes.
                    let src_node = indices.entry(src_str.clone())
                        .or_insert_with(|| graph.add_node(src_str.clone()))
                        .clone();
                    let dst_node = indices.entry(dst_str.clone())
                        .or_insert_with(|| graph.add_node(dst_str.clone()))
                        .clone();

//                    println!("Node {:?}: {:?}", src_node, src_str);
//                    println!("Node {:?}: {:?}", dst_node, dst_str);
//                    println!("Node count is now {}\n", graph.node_count());

                    graph.add_edge(src_node, dst_node, true);
                }
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", path, err);
            }
        };
    }

    //    // Print all the includes
    //    for node_idx in graph.node_indices() {
    //        for edge in graph.edges(node_idx) {
    //            let src_str = graph.index(edge.source());
    //            let dst_str = graph.index(edge.target());
    //            println!("{:?} -> {:?}", src_str, dst_str);
    //        }
    //    }

    // Write output to file graph.dot
    let out_path = Path::new("./graph.dot");
    let out_path_display = out_path.display();

    let mut dotfile = match File::create(&out_path) {
        Ok(file) => file,
        Err(why) => panic!("couldn't create {}: {}", out_path_display, why.description()),
    };

    writeln!(&mut dotfile,
             "{:?}",
             Dot::with_config(&graph, &[Config::EdgeNoLabel]));

    println!("Wrote graph to {}", out_path_display);
    println!("Run 'dot -Tpdf graph.dot > graph.pdf' to create a graph.");
}
<|endoftext|>


<commit_before>//! A simple program that recursively changes the mode of all directories or
//! files to the given mode under the given directory. The following child
//! process will be executed and waited on, and it's exit code returned
//! `sh -c "find PATH -type TYPE -exec chmod MODE {} \;"`.
use std::env;
use std::fmt::Write as FmtWrite;
use std::io;
use std::path::Path;
use std::process;
use std::process::Command;

// Successful execution
const ESUCCESS: i32 = 0x00;
// No exit code found
const ENOEXIT: i32 = 0x01;
// Invalid usage
const EUSAGE: i32 = 0x02;

// Shell arguments (compatibility wrapper)
const SHELL_ARGS: &[&str] = &[
    "sh", "-c"
];
// Chmod arguments (recursive typed chmod)
const CHMOD_ARGS: &[&str] = &[
    "find", "-type", "-exec", "chmod", "{}", "\\;"
];

// The program name
const NAME: &str = "chmodrt";
// The program usage
const USAGE: &str = "Usage: chmodrt TYPE MODE PATH";
// The program options
const TYPES: &[&[&str]] = &[
    &["-d", "Change the mode of directories"],
    &["-f", "Change the mode of files"],
];

macro_rules! eprint {
    ($fmt:expr) => {{
        use std::io::{self, Write};
        write!(&mut io::stderr(), $fmt).unwrap();
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        use std::io::{self, Write};
        write!(&mut io::stderr(), $fmt, $($arg)*).unwrap();
    }};
}

macro_rules! eprintln {
    () => (eprint!("\n"));
    ($fmt:expr) => (eprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (eprint!(concat!($fmt, "\n"), $($arg)*));
}

// Prepends the program name to the given message
macro_rules! sformat {
    ($fmt:expr) => (format!(concat!("{}: ", $fmt), NAME));
    ($fmt:expr, $($arg:tt)*) => (format!(concat!("{}: ", $fmt), NAME, $($arg)*));
}

// Writes a formatted system message to the standard error
macro_rules! sprint {
    ($fmt:expr) => (eprint!("{}", sformat!($fmt)));
    ($fmt:expr, $($arg:tt)*) => (eprint!("{}", sformat!($fmt, $($arg)*)));
}

// Writes a formatted system message to the standard error with a new line
macro_rules! sprintln {
    () => (sprint!("\n"));
    ($fmt:expr) => (sprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (sprint!(concat!($fmt, "\n"), $($arg)*));
}

fn main() {
    process::exit(chmodrt(env::args().collect()));
}

fn chmodrt(args: Vec<String>) -> i32 {

    // Enforce correct usage
    if args.len() != 4 {
        print!("{}\n\n{}", USAGE, options());
        return EUSAGE;
    }

    // Check if the type is not a directory or a file
    if args[1] == TYPES[0][0] || args[1] == TYPES[1][0] {} else {
        sprintln!("unknown type: '{}'", args[1]);
        return EUSAGE;
    }

    // Authorize absolute paths
    if Path::new(&args[3]).has_root() {
        let stdin_err = sformat!("cannot read from stdin");
        loop {
            // Reset the input buffer with every pass
            let mut input = String::new();

            // Print a confirmation prompt and wait for input
            sprint!("path is absolute - continue? [y/n] ");
            io::stdin().read_line(&mut input).expect(&stdin_err);

            // Normalize the input for comparison
            input = input.trim().to_lowercase();

            // The response must be 'y' or 'n'
            match input.as_str() {
                "y" => break,
                "n" => {
                    sprintln!("abort");
                    return ESUCCESS;
                },
                _ => continue,
            }
        }
    }

    // Construct the child process and error messages
    let find_command = format!("{} {} {} {} {} {} {} {} {}",
                               CHMOD_ARGS[0], args[3], CHMOD_ARGS[1], args[1].trim_left_matches("-"),
                               CHMOD_ARGS[2], CHMOD_ARGS[3], args[2], CHMOD_ARGS[4], CHMOD_ARGS[5]);
    let child_exec_err = sformat!("failed to execute the child process: `{}`", SHELL_ARGS[0]);
    let child_wait_err = sformat!("failed to wait on the child process: `{}`", SHELL_ARGS[0]);

    // Execute and wait on the child process
    let child = Command::new(SHELL_ARGS[0])
        .arg(SHELL_ARGS[1])
        .arg(find_command)
        .spawn().expect(&child_exec_err)
        .wait().expect(&child_wait_err);

    // Return the child exit code if it exists
    match child.code() {
        None => return ENOEXIT,
        Some(code) => return code,
    }
}

fn options() -> String {

    // Initialize the buffer and write the options header
    let mut buffer = String::with_capacity(128);
    writeln!(&mut buffer, "Types:").unwrap();

    // Enumerate the options and return the buffer
    for outer in TYPES.iter() {
        for inner in outer.iter() {
            write!(&mut buffer, "{:2}{}", "", inner).unwrap();
        }
        writeln!(&mut buffer, "").unwrap();
    }
    return buffer;
}
<commit_msg>Removed stderr macros (1.19)<commit_after>//! A simple program that recursively changes the mode of all directories or
//! files to the given mode under the given directory. The following child
//! process will be executed and waited on, and it's exit code returned
//! `sh -c "find PATH -type TYPE -exec chmod MODE {} \;"`.
use std::env;
use std::fmt::Write as FmtWrite;
use std::io;
use std::path::Path;
use std::process;
use std::process::Command;

// Successful execution
const ESUCCESS: i32 = 0x00;
// No exit code found
const ENOEXIT: i32 = 0x01;
// Invalid usage
const EUSAGE: i32 = 0x02;

// Shell arguments (compatibility wrapper)
const SHELL_ARGS: &[&str] = &[
    "sh", "-c"
];
// Chmod arguments (recursive typed chmod)
const CHMOD_ARGS: &[&str] = &[
    "find", "-type", "-exec", "chmod", "{}", "\\;"
];

// The program name
const NAME: &str = "chmodrt";
// The program usage
const USAGE: &str = "Usage: chmodrt TYPE MODE PATH";
// The program options
const TYPES: &[&[&str]] = &[
    &["-d", "Change the mode of directories"],
    &["-f", "Change the mode of files"],
];

// Prepends the program name to the given message
macro_rules! sformat {
    ($fmt:expr) => (format!(concat!("{}: ", $fmt), NAME));
    ($fmt:expr, $($arg:tt)*) => (format!(concat!("{}: ", $fmt), NAME, $($arg)*));
}

// Writes a formatted system message to the standard error
macro_rules! sprint {
    ($fmt:expr) => (eprint!("{}", sformat!($fmt)));
    ($fmt:expr, $($arg:tt)*) => (eprint!("{}", sformat!($fmt, $($arg)*)));
}

// Writes a formatted system message to the standard error with a new line
macro_rules! sprintln {
    () => (sprint!("\n"));
    ($fmt:expr) => (sprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (sprint!(concat!($fmt, "\n"), $($arg)*));
}

fn main() {
    process::exit(chmodrt(env::args().collect()));
}

fn chmodrt(args: Vec<String>) -> i32 {

    // Enforce correct usage
    if args.len() != 4 {
        print!("{}\n\n{}", USAGE, options());
        return EUSAGE;
    }

    // Check if the type is not a directory or a file
    if args[1] == TYPES[0][0] || args[1] == TYPES[1][0] {} else {
        sprintln!("unknown type: '{}'", args[1]);
        return EUSAGE;
    }

    // Authorize absolute paths
    if Path::new(&args[3]).has_root() {
        let stdin_err = sformat!("cannot read from stdin");
        loop {
            // Reset the input buffer with every pass
            let mut input = String::new();

            // Print a confirmation prompt and wait for input
            sprint!("path is absolute - continue? [y/n] ");
            io::stdin().read_line(&mut input).expect(&stdin_err);

            // Normalize the input for comparison
            input = input.trim().to_lowercase();

            // The response must be 'y' or 'n'
            match input.as_str() {
                "y" => break,
                "n" => {
                    sprintln!("abort");
                    return ESUCCESS;
                },
                _ => continue,
            }
        }
    }

    // Construct the child process and error messages
    let find_command = format!("{} {} {} {} {} {} {} {} {}",
                               CHMOD_ARGS[0], args[3], CHMOD_ARGS[1], args[1].trim_left_matches("-"),
                               CHMOD_ARGS[2], CHMOD_ARGS[3], args[2], CHMOD_ARGS[4], CHMOD_ARGS[5]);
    let child_exec_err = sformat!("failed to execute the child process: `{}`", SHELL_ARGS[0]);
    let child_wait_err = sformat!("failed to wait on the child process: `{}`", SHELL_ARGS[0]);

    // Execute and wait on the child process
    let child = Command::new(SHELL_ARGS[0])
        .arg(SHELL_ARGS[1])
        .arg(find_command)
        .spawn().expect(&child_exec_err)
        .wait().expect(&child_wait_err);

    // Return the child exit code if it exists
    match child.code() {
        None => return ENOEXIT,
        Some(code) => return code,
    }
}

fn options() -> String {

    // Initialize the buffer and write the options header
    let mut buffer = String::with_capacity(128);
    writeln!(&mut buffer, "Types:").unwrap();

    // Enumerate the options and return the buffer
    for outer in TYPES.iter() {
        for inner in outer.iter() {
            write!(&mut buffer, "{:2}{}", "", inner).unwrap();
        }
        writeln!(&mut buffer, "").unwrap();
    }
    return buffer;
}
<|endoftext|>


<commit_before>// This needs to be here
#![allow(dead_code)]

extern crate libc;
extern crate term;

use libc::{c_uint, c_uchar, c_int, c_short, c_long, c_ulong};
use libc::{clock_t, pid_t, uid_t, size_t};
use std::io;
use std::mem;
use std::ptr;

// used in Termios struct
const NCCS:uint = 32;

// stdin, stdout, stderr have standard file descriptors
const STDIN:c_int = 0;

// constants for control sequences is useful
const EOF:char = '\u{4}';
const DEL:char = '\u{7f}';
const NL:char  = '\n';
const ESC:char = '\u{1b}';
const ANSI:char = '\u{5b}';
const BS:char = '\u{8}';

// select termios constants that we use
const ICANON:c_uint   = 2;
const ECHO:c_uint     = 8;
const TCSANOW:c_int   = 0;
const TCSADRAIN:c_int = 1;
const TCSAFLUSH:c_int = 2;

// signal values
const SIGHUP:c_int    = 1;       /* Hangup (POSIX).  */
const SIGINT:c_int    = 2;       /* Interrupt (ANSI).  */
const SIGQUIT:c_int   = 3;       /* Quit (POSIX).  */
const SIGILL:c_int    = 4;       /* Illegal instruction (ANSI).  */
const SIGTRAP:c_int   = 5;       /* Trace trap (POSIX).  */
const SIGABRT:c_int   = 6;       /* Abort (ANSI).  */
const SIGIOT:c_int    = 6;       /* IOT trap (4.2 BSD).  */
const SIGBUS:c_int    = 7;       /* BUS error (4.2 BSD).  */
const SIGFPE:c_int    = 8;       /* Floating-point exception (ANSI).  */
const SIGKILL:c_int   = 9;       /* Kill, unblockable (POSIX).  */
const SIGUSR1:c_int   = 10;      /* User-defined signal 1 (POSIX).  */
const SIGSEGV:c_int   = 11;      /* Segmentation violation (ANSI).  */
const SIGUSR2:c_int   = 12;      /* User-defined signal 2 (POSIX).  */
const SIGPIPE:c_int   = 13;      /* Broken pipe (POSIX).  */
const SIGALRM:c_int   = 14;      /* Alarm clock (POSIX).  */
const SIGTERM:c_int   = 15;      /* Termination (ANSI).  */
const SIGSTKFLT:c_int = 16;      /* Stack fault.  */
const SIGCLD:c_int    = SIGCHLD; /* Same as SIGCHLD (System V).  */
const SIGCHLD:c_int   = 17;      /* Child status has changed (POSIX).  */
const SIGCONT:c_int   = 18;      /* Continue (POSIX).  */
const SIGSTOP:c_int   = 19;      /* Stop, unblockable (POSIX).  */
const SIGTSTP:c_int   = 20;      /* Keyboard stop (POSIX).  */
const SIGTTIN:c_int   = 21;      /* Background read from tty (POSIX).  */
const SIGTTOU:c_int   = 22;      /* Background write to tty (POSIX).  */
const SIGURG:c_int    = 23;      /* Urgent condition on socket (4.2 BSD).  */
const SIGXCPU:c_int   = 24;      /* CPU limit exceeded (4.2 BSD).  */
const SIGXFSZ:c_int   = 25;      /* File size limit exceeded (4.2 BSD).  */
const SIGVTALRM:c_int = 26;      /* Virtual alarm clock (4.2 BSD).  */
const SIGPROF:c_int   = 27;      /* Profiling alarm clock (4.2 BSD).  */
const SIGWINCH:c_int  = 28;      /* Window size change (4.3 BSD, Sun).  */
const SIGPOLL:c_int   = SIGIO;   /* Pollable event occurred (System V).  */
const SIGIO:c_int     = 29;      /* I/O now possible (4.2 BSD).  */
const SIGPWR:c_int    = 30;      /* Power failure restart (System V).  */
const SIGSYS:c_int    = 31;      /* Bad system call.  */
const SIGUNUSED:c_int = 31;

// sizeof(int) = 4
// sizeof(unsigned long int) = 8
const SI_MAX_SIZE:uint = 128;
const SI_PAD_SIZE:uint = (SI_MAX_SIZE / 4)  - 4;
const SIGSET_NWORDS:uint = (1024 / (8 * 8));

const SA_NOCLDSTOP:c_int = 1;
const SA_NOCLDWAIT:c_int = 2;
const SA_SIGINFO:c_int   = 4;
const SA_RESTART:c_int   = 0x10000000;

// types used in Termios struct
type CCType = c_uchar;
type SpeedType = c_uint;
type TCFlag = c_uint;

#[repr(C)]
#[deriving(Copy)]
#[deriving(Clone)]
struct Termios {
    iflag: TCFlag,
    oflag: TCFlag,
    cflag: TCFlag,
    lflag: TCFlag,
    line: CCType,
    cc: [c_uchar, ..NCCS],
    ispeed: SpeedType,
    ospeed: SpeedType,
}

impl Termios {
    fn new() -> Termios {
        Termios {
            cc: [0, ..NCCS],
            cflag: 0,
            iflag: 0,
            ispeed: 0,
            lflag: 0,
            line: 0,
            oflag: 0,
            ospeed: 0,
        }
    }

    fn get_from(&mut self, fd:c_int) -> bool {
        unsafe {
            return tcgetattr(fd, self) == 0;
        }
    }

    fn get(&mut self) -> bool {
        self.get_from(STDIN)
    }

    fn set_to(&self, fd:c_int) -> bool {
        unsafe {
            return tcsetattr(fd, TCSANOW, self) == 0;
        }
    }

    fn set(&self) -> bool {
        self.set_to(STDIN)
    }

    fn lenable(&mut self, flag:c_uint) {
        self.lflag |= flag;
    }

    fn ldisable(&mut self, flag:c_uint) {
        self.lflag &= !flag;
    }
}

#[link(name = "c")]
extern {
    fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int;
    fn tcsetattr(fd: c_int, optional_actions: c_int, termios: *const Termios) -> c_int;
}

// This could also be a pointer,
// but it probably isn't
type SigVal = c_int;

// _pad is needed to make all these types the same size
// this is the case because rust doesn't have an equivalent to C unions
// it's really a bitch
// at least let me transmute between different sized types I mean jesus christ

struct KillFields {
    pid: pid_t,
    uid: uid_t,
    _pad: [c_int, ..(SI_PAD_SIZE - 2)]
}

struct PTimerFields {
    tid: c_int,
    overrun: c_int,
    sigval: SigVal,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct PSignalFields {
    pid: pid_t,
    uid: uid_t,
    sigval: SigVal,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct SigChldFields {
    pid: pid_t,
    uid: uid_t,
    status: c_int,
    utime: clock_t,
    stime: clock_t,
    _pad: [c_int, ..(SI_PAD_SIZE - 8)]
}

struct SigFaultFields {
    addr: size_t, // pointer
    addr_lsb: c_short,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct SigPollFields {
    band: c_long,
    fd: c_int,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct SigSysFields {
    _call_addr: size_t, // pointer
    _syscall: c_int,
    _arch: c_uint,
    _pad: [c_int, ..(SI_PAD_SIZE - 4)]
}

// If only Rust had a C union equivalent...
enum SigFields {
    Kill(KillFields),
    PTimer(PTimerFields),
    PSignal(PSignalFields),
    SigChld(SigChldFields),
    SigFault(SigFaultFields),
    SigPoll(SigPollFields),
    SigSys(SigSysFields)
}

#[repr(C)]
struct SigInfo {
    signo: c_int,
    errno: c_int,
    code: c_int,

    // Rust doesn't currently support unions for FFI's
    // the best option is to have a generic pad and use
    // transmute to change between types
    // No, you can't just use enums, that would be too easy
    pad: [c_int, ..SI_PAD_SIZE]
}

// The size_t at the end is a pointer to a ucontext_t
// do I want to implement that type? Lol no
// Protip: we're calling sigaction with SA_SIGINFO
// In part because I didn't realize I didn't have to implement
// all of SigInfo, so I did, and now I want to use that code dammit!
type SigHandler = extern fn(c_int, *const SigInfo, size_t);
type SigSet = [c_ulong, ..SIGSET_NWORDS];

#[repr(C)]
struct SigAction {
    handler: SigHandler,
    mask: SigSet,
    flags: c_int
}

impl SigInfo {
    fn determine_sigfields(&self) -> SigFields {
        use SigFields::{Kill, PTimer, PSignal, SigChld, SigFault, SigPoll, SigSys};
        unsafe {
            match self.signo {
                SIGKILL => return Kill(mem::transmute(self.pad)),
                SIGALRM | SIGVTALRM | SIGPROF => return PTimer(mem::transmute(self.pad)),
                SIGCHLD => return SigChld(mem::transmute(self.pad)),
                SIGILL | SIGFPE | SIGSEGV | SIGBUS => return SigFault(mem::transmute(self.pad)),
                SIGPOLL => return SigPoll(mem::transmute(self.pad)),
                SIGSYS => return SigSys(mem::transmute(self.pad)),
                _ => return PSignal(mem::transmute(self.pad))
            }
        }
    }
}

#[link(name="c")]
extern {
    fn sigaction(signum:c_int, act:&SigAction, oldact:*mut SigAction) -> c_int;
    fn sigfillset(mask:*mut SigSet);
}

fn empty_escape(esc:&mut Iterator<char>) -> String {
    let mut out = String::new();
    loop {
        match esc.next() {
            Some(c) => out.push(c),
            None => break
        }
    }
    return out;
}

#[allow(unused_variables)]
extern fn handle_sigint(signum:c_int, siginfo:*const SigInfo, context:size_t) {
    println!("Caught SIGINT");
}

fn prepare_terminal() -> Termios {
    // new terminal mode info
    let mut tios = Termios::new();
    // populate terminal info
    tios.get();
    let tios_clone = tios.clone();
    // turn off canonical mode
    tios.ldisable(ICANON);
    // turn off echo mode
    tios.ldisable(ECHO);
    // set the terminal mode
    update_terminal(tios);
    // return the old terminal mode
    return tios_clone;
}

fn update_terminal(tios:Termios) -> bool {
    if !tios.set() {
        io::stderr().write_line("Warning: Could not set terminal mode").unwrap();
        return false;
    }
    return true;
}

fn handle_escape(stdin:&mut io::stdio::StdinReader,
                 line:&mut String, part:&mut String) {
    // Handle an ANSI escape sequence
    if stdin.read_char() != Ok(ANSI) {
        return;
    }
    match stdin.read_char() {
        Err(_) => return,
        Ok('D') => {
            match line.pop() {
                Some(c) => {
                    part.push(c);
                    cursor_left();
                },
                None => return
            }
        },
        Ok('C') => {
            match part.pop() {
                Some(c) => {
                    line.push(c);
                    cursor_right();
                },
                None => return
            }
        },
        Ok(_) => return
    }
}

fn redraw_line(line:&String, pad_to:uint) {
    // TODO: make this more optimized
    print!("\r{}", line);
    if pad_to > line.len() {
        let pad_amount = pad_to - line.len();
        print!("{}", String::from_char(pad_amount, ' '));
    }

}

fn cursor_left() {
    print!("{}", DEL);
}

fn cursor_right() {
    print!("{}{}C", ESC, ANSI);
}

fn draw_part(part:&String) {
    // quick out if part is empty
    if part.is_empty() {
        return;
    }
    let mut cpart = part.clone();
    let mut rpart = String::new();
    loop {
        match cpart.pop() {
            Some(c) => rpart.push(c),
            None => break
        }
    }
    print!("{}", rpart);
}

fn cursors_left(by:uint) {
    // move back by a given number of characters
    print!("{}", String::from_char(by, DEL));
}

fn idraw_part(part:&String) {
    // in-place draw of the line part
    draw_part(part);
    cursors_left(part.len());
}

fn main() {
    let mut sa = SigAction {
        handler: handle_sigint,
        mask: [0, ..SIGSET_NWORDS],
        flags: SA_RESTART | SA_SIGINFO
    };
    unsafe {
        sigfillset(&mut sa.mask);
        assert!(sigaction(SIGINT, &sa, ptr::null_mut::<SigAction>()) == 0);
    }
    let old_tios = prepare_terminal();
    let mut stdin = io::stdin();
    let mut line = String::new();
    let mut part = String::new();
    loop {
        // Note: in non-canonical mode
        match stdin.read_char() {
            Ok(EOF) => break,
            Ok(NL) => {
                line.clear();
                part.clear();
                print!("\n");
            },
            Ok(DEL) => {
                if line.is_empty() {
                    continue;
                }
                line.pop();
                cursor_left();
                draw_part(&part);
                print!(" ");
                cursors_left(part.len() + 1);
            },
            Ok(ESC) => handle_escape(&mut stdin, &mut line, &mut part),
            Ok(c) => {
                line.push(c);
                print!("{}", c);
                idraw_part(&part);
            },
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        }
    }
    // print so we know we've reached this code
    println!("Exiting");
    // restore old term state
    update_terminal(old_tios);
}
<commit_msg>Move SIGINT setup into separate function<commit_after>// This needs to be here
#![allow(dead_code)]

extern crate libc;
extern crate term;

use libc::{c_uint, c_uchar, c_int, c_short, c_long, c_ulong};
use libc::{clock_t, pid_t, uid_t, size_t};
use std::io;
use std::mem;
use std::ptr;

// used in Termios struct
const NCCS:uint = 32;

// stdin, stdout, stderr have standard file descriptors
const STDIN:c_int = 0;

// constants for control sequences is useful
const EOF:char = '\u{4}';
const DEL:char = '\u{7f}';
const NL:char  = '\n';
const ESC:char = '\u{1b}';
const ANSI:char = '\u{5b}';
const BS:char = '\u{8}';

// select termios constants that we use
const ICANON:c_uint   = 2;
const ECHO:c_uint     = 8;
const TCSANOW:c_int   = 0;
const TCSADRAIN:c_int = 1;
const TCSAFLUSH:c_int = 2;

// signal values
const SIGHUP:c_int    = 1;       /* Hangup (POSIX).  */
const SIGINT:c_int    = 2;       /* Interrupt (ANSI).  */
const SIGQUIT:c_int   = 3;       /* Quit (POSIX).  */
const SIGILL:c_int    = 4;       /* Illegal instruction (ANSI).  */
const SIGTRAP:c_int   = 5;       /* Trace trap (POSIX).  */
const SIGABRT:c_int   = 6;       /* Abort (ANSI).  */
const SIGIOT:c_int    = 6;       /* IOT trap (4.2 BSD).  */
const SIGBUS:c_int    = 7;       /* BUS error (4.2 BSD).  */
const SIGFPE:c_int    = 8;       /* Floating-point exception (ANSI).  */
const SIGKILL:c_int   = 9;       /* Kill, unblockable (POSIX).  */
const SIGUSR1:c_int   = 10;      /* User-defined signal 1 (POSIX).  */
const SIGSEGV:c_int   = 11;      /* Segmentation violation (ANSI).  */
const SIGUSR2:c_int   = 12;      /* User-defined signal 2 (POSIX).  */
const SIGPIPE:c_int   = 13;      /* Broken pipe (POSIX).  */
const SIGALRM:c_int   = 14;      /* Alarm clock (POSIX).  */
const SIGTERM:c_int   = 15;      /* Termination (ANSI).  */
const SIGSTKFLT:c_int = 16;      /* Stack fault.  */
const SIGCLD:c_int    = SIGCHLD; /* Same as SIGCHLD (System V).  */
const SIGCHLD:c_int   = 17;      /* Child status has changed (POSIX).  */
const SIGCONT:c_int   = 18;      /* Continue (POSIX).  */
const SIGSTOP:c_int   = 19;      /* Stop, unblockable (POSIX).  */
const SIGTSTP:c_int   = 20;      /* Keyboard stop (POSIX).  */
const SIGTTIN:c_int   = 21;      /* Background read from tty (POSIX).  */
const SIGTTOU:c_int   = 22;      /* Background write to tty (POSIX).  */
const SIGURG:c_int    = 23;      /* Urgent condition on socket (4.2 BSD).  */
const SIGXCPU:c_int   = 24;      /* CPU limit exceeded (4.2 BSD).  */
const SIGXFSZ:c_int   = 25;      /* File size limit exceeded (4.2 BSD).  */
const SIGVTALRM:c_int = 26;      /* Virtual alarm clock (4.2 BSD).  */
const SIGPROF:c_int   = 27;      /* Profiling alarm clock (4.2 BSD).  */
const SIGWINCH:c_int  = 28;      /* Window size change (4.3 BSD, Sun).  */
const SIGPOLL:c_int   = SIGIO;   /* Pollable event occurred (System V).  */
const SIGIO:c_int     = 29;      /* I/O now possible (4.2 BSD).  */
const SIGPWR:c_int    = 30;      /* Power failure restart (System V).  */
const SIGSYS:c_int    = 31;      /* Bad system call.  */
const SIGUNUSED:c_int = 31;

// sizeof(int) = 4
// sizeof(unsigned long int) = 8
const SI_MAX_SIZE:uint = 128;
const SI_PAD_SIZE:uint = (SI_MAX_SIZE / 4)  - 4;
const SIGSET_NWORDS:uint = (1024 / (8 * 8));

const SA_NOCLDSTOP:c_int = 1;
const SA_NOCLDWAIT:c_int = 2;
const SA_SIGINFO:c_int   = 4;
const SA_RESTART:c_int   = 0x10000000;

// types used in Termios struct
type CCType = c_uchar;
type SpeedType = c_uint;
type TCFlag = c_uint;

#[repr(C)]
#[deriving(Copy)]
#[deriving(Clone)]
struct Termios {
    iflag: TCFlag,
    oflag: TCFlag,
    cflag: TCFlag,
    lflag: TCFlag,
    line: CCType,
    cc: [c_uchar, ..NCCS],
    ispeed: SpeedType,
    ospeed: SpeedType,
}

impl Termios {
    fn new() -> Termios {
        Termios {
            cc: [0, ..NCCS],
            cflag: 0,
            iflag: 0,
            ispeed: 0,
            lflag: 0,
            line: 0,
            oflag: 0,
            ospeed: 0,
        }
    }

    fn get_from(&mut self, fd:c_int) -> bool {
        unsafe {
            return tcgetattr(fd, self) == 0;
        }
    }

    fn get(&mut self) -> bool {
        self.get_from(STDIN)
    }

    fn set_to(&self, fd:c_int) -> bool {
        unsafe {
            return tcsetattr(fd, TCSANOW, self) == 0;
        }
    }

    fn set(&self) -> bool {
        self.set_to(STDIN)
    }

    fn lenable(&mut self, flag:c_uint) {
        self.lflag |= flag;
    }

    fn ldisable(&mut self, flag:c_uint) {
        self.lflag &= !flag;
    }
}

#[link(name = "c")]
extern {
    fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int;
    fn tcsetattr(fd: c_int, optional_actions: c_int, termios: *const Termios) -> c_int;
}

// This could also be a pointer,
// but it probably isn't
type SigVal = c_int;

// _pad is needed to make all these types the same size
// this is the case because rust doesn't have an equivalent to C unions
// it's really a bitch
// at least let me transmute between different sized types I mean jesus christ

struct KillFields {
    pid: pid_t,
    uid: uid_t,
    _pad: [c_int, ..(SI_PAD_SIZE - 2)]
}

struct PTimerFields {
    tid: c_int,
    overrun: c_int,
    sigval: SigVal,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct PSignalFields {
    pid: pid_t,
    uid: uid_t,
    sigval: SigVal,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct SigChldFields {
    pid: pid_t,
    uid: uid_t,
    status: c_int,
    utime: clock_t,
    stime: clock_t,
    _pad: [c_int, ..(SI_PAD_SIZE - 8)]
}

struct SigFaultFields {
    addr: size_t, // pointer
    addr_lsb: c_short,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct SigPollFields {
    band: c_long,
    fd: c_int,
    _pad: [c_int, ..(SI_PAD_SIZE - 3)]
}

struct SigSysFields {
    _call_addr: size_t, // pointer
    _syscall: c_int,
    _arch: c_uint,
    _pad: [c_int, ..(SI_PAD_SIZE - 4)]
}

// If only Rust had a C union equivalent...
enum SigFields {
    Kill(KillFields),
    PTimer(PTimerFields),
    PSignal(PSignalFields),
    SigChld(SigChldFields),
    SigFault(SigFaultFields),
    SigPoll(SigPollFields),
    SigSys(SigSysFields)
}

#[repr(C)]
struct SigInfo {
    signo: c_int,
    errno: c_int,
    code: c_int,

    // Rust doesn't currently support unions for FFI's
    // the best option is to have a generic pad and use
    // transmute to change between types
    // No, you can't just use enums, that would be too easy
    pad: [c_int, ..SI_PAD_SIZE]
}

// The size_t at the end is a pointer to a ucontext_t
// do I want to implement that type? Lol no
// Protip: we're calling sigaction with SA_SIGINFO
// In part because I didn't realize I didn't have to implement
// all of SigInfo, so I did, and now I want to use that code dammit!
type SigHandler = extern fn(c_int, *const SigInfo, size_t);
type SigSet = [c_ulong, ..SIGSET_NWORDS];

#[repr(C)]
struct SigAction {
    handler: SigHandler,
    mask: SigSet,
    flags: c_int
}

impl SigInfo {
    fn determine_sigfields(&self) -> SigFields {
        use SigFields::{Kill, PTimer, PSignal, SigChld, SigFault, SigPoll, SigSys};
        unsafe {
            match self.signo {
                SIGKILL => return Kill(mem::transmute(self.pad)),
                SIGALRM | SIGVTALRM | SIGPROF => return PTimer(mem::transmute(self.pad)),
                SIGCHLD => return SigChld(mem::transmute(self.pad)),
                SIGILL | SIGFPE | SIGSEGV | SIGBUS => return SigFault(mem::transmute(self.pad)),
                SIGPOLL => return SigPoll(mem::transmute(self.pad)),
                SIGSYS => return SigSys(mem::transmute(self.pad)),
                _ => return PSignal(mem::transmute(self.pad))
            }
        }
    }
}

#[link(name="c")]
extern {
    fn sigaction(signum:c_int, act:&SigAction, oldact:*mut SigAction) -> c_int;
    fn sigfillset(mask:*mut SigSet) -> c_int;
}

fn empty_escape(esc:&mut Iterator<char>) -> String {
    let mut out = String::new();
    loop {
        match esc.next() {
            Some(c) => out.push(c),
            None => break
        }
    }
    return out;
}

#[allow(unused_variables)]
extern fn handle_sigint(signum:c_int, siginfo:*const SigInfo, context:size_t) {
    println!("Caught SIGINT");
}

fn prepare_terminal() -> Termios {
    // new terminal mode info
    let mut tios = Termios::new();
    // populate terminal info
    tios.get();
    let tios_clone = tios.clone();
    // turn off canonical mode
    tios.ldisable(ICANON);
    // turn off echo mode
    tios.ldisable(ECHO);
    // set the terminal mode
    update_terminal(tios);
    // return the old terminal mode
    return tios_clone;
}

fn update_terminal(tios:Termios) -> bool {
    if !tios.set() {
        io::stderr().write_line("Warning: Could not set terminal mode").unwrap();
        return false;
    }
    return true;
}

fn handle_escape(stdin:&mut io::stdio::StdinReader,
                 line:&mut String, part:&mut String) {
    // Handle an ANSI escape sequence
    if stdin.read_char() != Ok(ANSI) {
        return;
    }
    match stdin.read_char() {
        Err(_) => return,
        Ok('D') => {
            match line.pop() {
                Some(c) => {
                    part.push(c);
                    cursor_left();
                },
                None => return
            }
        },
        Ok('C') => {
            match part.pop() {
                Some(c) => {
                    line.push(c);
                    cursor_right();
                },
                None => return
            }
        },
        Ok(_) => return
    }
}

fn redraw_line(line:&String, pad_to:uint) {
    // TODO: make this more optimized
    print!("\r{}", line);
    if pad_to > line.len() {
        let pad_amount = pad_to - line.len();
        print!("{}", String::from_char(pad_amount, ' '));
    }

}

fn cursor_left() {
    print!("{}", DEL);
}

fn cursor_right() {
    print!("{}{}C", ESC, ANSI);
}

fn draw_part(part:&String) {
    // quick out if part is empty
    if part.is_empty() {
        return;
    }
    let mut cpart = part.clone();
    let mut rpart = String::new();
    loop {
        match cpart.pop() {
            Some(c) => rpart.push(c),
            None => break
        }
    }
    print!("{}", rpart);
}

fn cursors_left(by:uint) {
    // move back by a given number of characters
    print!("{}", String::from_char(by, DEL));
}

fn idraw_part(part:&String) {
    // in-place draw of the line part
    draw_part(part);
    cursors_left(part.len());
}

fn prepare_signals() {
    let mut sa = SigAction {
        handler: handle_sigint,
        mask: [0, ..SIGSET_NWORDS],
        flags: SA_RESTART | SA_SIGINFO
    };
    unsafe {
        if sigfillset(&mut sa.mask) != 0 {
            io::stderr().write_line("Warning: could not fill mask set for SIGINT handler").unwrap();
        }
        if sigaction(SIGINT, &sa, ptr::null_mut::<SigAction>()) != 0 {
            io::stderr().write_line("Warning: could not set handler for SIGINT").unwrap();
        }
    }
}

fn main() {
    prepare_signals();
    let old_tios = prepare_terminal();
    let mut stdin = io::stdin();
    let mut line = String::new();
    let mut part = String::new();
    loop {
        // Note: in non-canonical mode
        match stdin.read_char() {
            Ok(EOF) => break,
            Ok(NL) => {
                line.clear();
                part.clear();
                print!("\n");
            },
            Ok(DEL) => {
                if line.is_empty() {
                    continue;
                }
                line.pop();
                cursor_left();
                draw_part(&part);
                print!(" ");
                cursors_left(part.len() + 1);
            },
            Ok(ESC) => handle_escape(&mut stdin, &mut line, &mut part),
            Ok(c) => {
                line.push(c);
                print!("{}", c);
                idraw_part(&part);
            },
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        }
    }
    // print so we know we've reached this code
    println!("Exiting");
    // restore old term state
    update_terminal(old_tios);
}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]

extern crate cookie;
extern crate regex;
extern crate reqwest;
extern crate rpassword;
extern crate rprompt;
extern crate select;
extern crate serde;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate serde_yaml;
extern crate term;
extern crate toml;
extern crate webbrowser;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;

#[macro_use]
mod macros;

mod config;
mod error;
mod judge;
mod service;
mod testcase;
mod util;

use self::config::{Config, ServiceName};
use self::error::OrExit1;
use self::service::{atcoder, atcoder_beta};
use clap::{AppSettings, Arg, SubCommand};

fn main() {
    fn arg_default_lang() -> Arg<'static, 'static> {
        Arg::with_name("default-lang")
            .help("Default language")
            .required(true)
    }

    fn arg_dir() -> Arg<'static, 'static> {
        Arg::with_name("dir")
            .help("Directory to create \"snowchains.yml\"")
            .required(true)
    }

    fn arg_property_or_target() -> Arg<'static, 'static> {
        Arg::with_name("property-or-target")
            .help(
                "Property name (\"service\", \"contest\", \"testcases\", \
                 \"testcase_extension\", \"default_lang\") or target name",
            )
            .required(true)
    }

    fn arg_value() -> Arg<'static, 'static> {
        Arg::with_name("value").required(true)
    }

    fn arg_service() -> Arg<'static, 'static> {
        Arg::with_name("service")
            .possible_values(&["atcoder", "atcoder-beta"])
            .help("Service name")
            .required(true)
    }

    fn arg_contest() -> Arg<'static, 'static> {
        Arg::with_name("contest").help("Contest name").required(
            true,
        )
    }

    fn arg_target() -> Arg<'static, 'static> {
        Arg::with_name("target").help("Target name").required(true)
    }

    let subcommand_init_config = SubCommand::with_name("init-config")
        .arg(arg_default_lang())
        .arg(arg_dir());

    let subcommand_set = SubCommand::with_name("set")
        .arg(arg_property_or_target())
        .arg(arg_value());

    let subcommand_login = SubCommand::with_name("login").arg(arg_service());

    let subcommand_participate = SubCommand::with_name("participate")
        .arg(arg_service())
        .arg(arg_contest());

    let subcommand_download = SubCommand::with_name("download");

    let subcommand_judge = SubCommand::with_name("judge").arg(arg_target());

    let subcommand_submit = SubCommand::with_name("submit").arg(arg_target());

    let matches = app_from_crate!()
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::VersionlessSubcommands)
        .subcommand(subcommand_init_config)
        .subcommand(subcommand_set)
        .subcommand(subcommand_login)
        .subcommand(subcommand_participate)
        .subcommand(subcommand_download)
        .subcommand(subcommand_judge)
        .subcommand(subcommand_submit)
        .get_matches();

    fn config() -> Config {
        Config::load_from_file().or_exit1()
    }

    if let Some(matches) = matches.subcommand_matches("init-config") {
        let lang = matches.value_of("default-lang").unwrap();
        let dir = matches.value_of("dir").unwrap();
        return config::create_config_file(lang, dir).or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("set") {
        let key = matches.value_of("property-or-target").unwrap();
        let value = matches.value_of("value").unwrap();
        return config::set_property(key, value).or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("login") {
        let service_name = matches.value_of("service").unwrap();
        if service_name == "atcoder" {
            return atcoder::login().or_exit1();
        } else if service_name == "atcoder-beta" {
            return atcoder_beta::login().or_exit1();
        }
    } else if let Some(matches) = matches.subcommand_matches("participate") {
        let contest = matches.value_of("contest").unwrap();
        let service_name = matches.value_of("service").unwrap();
        if service_name == "atcoder" {
            return atcoder::participate(&contest).or_exit1();
        } else if service_name == "atcoder-beta" {
            return atcoder_beta::participate(&contest).or_exit1();
        }
    } else if let Some(_) = matches.subcommand_matches("download") {
        let config = config();
        let contest = config.contest().or_exit1();
        let path = config.testcase_dir().or_exit1();
        let extension = config.testcase_extension();
        return match config.service().or_exit1() {
            ServiceName::AtCoder => atcoder::download(&contest, &path, extension),
            ServiceName::AtCoderBeta => atcoder_beta::download(&contest, &path, extension),
        }.or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("judge") {
        let config = config();
        let target = matches.value_of("target").unwrap();
        let cases = config.testcase_path(target).or_exit1();
        let build_command = config.construct_build_command(target).or_exit1();
        let run_command = config.construct_run_command(target).or_exit1();
        return judge::judge(&cases, run_command, build_command).or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("submit") {
        let config = config();
        let target = matches.value_of("target").unwrap();
        let cases = config.testcase_path(target).or_exit1();
        let build_command = config.construct_build_command(target).or_exit1();
        let run_command = config.construct_run_command(target).or_exit1();
        let contest = config.contest().or_exit1();
        let lang_id = config.atcoder_lang_id(&target).or_exit1();
        let src = config.src_path(&target).or_exit1();
        judge::judge(&cases, run_command, build_command).or_exit1();
        return match config.service().or_exit1() {
            ServiceName::AtCoder => unimplemented!(),
            ServiceName::AtCoderBeta => atcoder_beta::submit(&contest, &target, lang_id, &src),
        }.or_exit1();
    }
    unreachable!();
}
<commit_msg>Refactor<commit_after>#![recursion_limit = "1024"]

extern crate cookie;
extern crate regex;
extern crate reqwest;
extern crate rpassword;
extern crate rprompt;
extern crate select;
extern crate serde;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate serde_yaml;
extern crate term;
extern crate toml;
extern crate webbrowser;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;

#[macro_use]
mod macros;

mod config;
mod error;
mod judge;
mod service;
mod testcase;
mod util;

use self::config::{Config, ServiceName};
use self::error::OrExit1;
use self::service::{atcoder, atcoder_beta};
use clap::{AppSettings, Arg, SubCommand};

fn main() {
    fn arg_default_lang() -> Arg<'static, 'static> {
        Arg::with_name("default-lang")
            .help("Default language")
            .required(true)
    }

    fn arg_dir() -> Arg<'static, 'static> {
        Arg::with_name("dir")
            .help("Directory to create \"snowchains.yml\"")
            .required(true)
    }

    fn arg_property_or_target() -> Arg<'static, 'static> {
        Arg::with_name("property-or-target")
            .help(
                "Property name (\"service\", \"contest\", \"testcases\", \
                 \"testcase_extension\", \"default_lang\") or target name",
            )
            .required(true)
    }

    fn arg_value() -> Arg<'static, 'static> {
        Arg::with_name("value").required(true)
    }

    fn arg_service() -> Arg<'static, 'static> {
        Arg::with_name("service")
            .possible_values(&["atcoder", "atcoder-beta"])
            .help("Service name")
            .required(true)
    }

    fn arg_contest() -> Arg<'static, 'static> {
        Arg::with_name("contest").help("Contest name").required(
            true,
        )
    }

    fn arg_target() -> Arg<'static, 'static> {
        Arg::with_name("target").help("Target name").required(true)
    }

    let subcommand_init_config = SubCommand::with_name("init-config")
        .arg(arg_default_lang())
        .arg(arg_dir());

    let subcommand_set = SubCommand::with_name("set")
        .arg(arg_property_or_target())
        .arg(arg_value());

    let subcommand_login = SubCommand::with_name("login").arg(arg_service());

    let subcommand_participate = SubCommand::with_name("participate")
        .arg(arg_service())
        .arg(arg_contest());

    let subcommand_download = SubCommand::with_name("download");

    let subcommand_judge = SubCommand::with_name("judge").arg(arg_target());

    let subcommand_submit = SubCommand::with_name("submit").arg(arg_target());

    let matches = app_from_crate!()
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::VersionlessSubcommands)
        .subcommand(subcommand_init_config)
        .subcommand(subcommand_set)
        .subcommand(subcommand_login)
        .subcommand(subcommand_participate)
        .subcommand(subcommand_download)
        .subcommand(subcommand_judge)
        .subcommand(subcommand_submit)
        .get_matches();

    fn config() -> Config {
        Config::load_from_file().or_exit1()
    }

    if let Some(matches) = matches.subcommand_matches("init-config") {
        let lang = matches.value_of("default-lang").unwrap();
        let dir = matches.value_of("dir").unwrap();
        return config::create_config_file(lang, dir).or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("set") {
        let key = matches.value_of("property-or-target").unwrap();
        let value = matches.value_of("value").unwrap();
        return config::set_property(key, value).or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("login") {
        let service_name = matches.value_of("service").unwrap();
        if service_name == "atcoder" {
            return atcoder::login().or_exit1();
        } else if service_name == "atcoder-beta" {
            return atcoder_beta::login().or_exit1();
        }
    } else if let Some(matches) = matches.subcommand_matches("participate") {
        let contest = matches.value_of("contest").unwrap();
        let service_name = matches.value_of("service").unwrap();
        if service_name == "atcoder" {
            return atcoder::participate(&contest).or_exit1();
        } else if service_name == "atcoder-beta" {
            return atcoder_beta::participate(&contest).or_exit1();
        }
    } else if let Some(_) = matches.subcommand_matches("download") {
        let config = config();
        let contest = config.contest().or_exit1();
        let path = config.testcase_dir().or_exit1();
        let extension = config.testcase_extension();
        return match config.service().or_exit1() {
            ServiceName::AtCoder => atcoder::download(&contest, &path, extension),
            ServiceName::AtCoderBeta => atcoder_beta::download(&contest, &path, extension),
        }.or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("judge") {
        let config = config();
        let target = matches.value_of("target").unwrap();
        let cases = config.testcase_path(target).or_exit1();
        let run_command = config.construct_run_command(target).or_exit1();
        let build_command = config.construct_build_command(target).or_exit1();
        return judge::judge(cases, run_command, build_command).or_exit1();
    } else if let Some(matches) = matches.subcommand_matches("submit") {
        let config = config();
        let target = matches.value_of("target").unwrap();
        let contest = config.contest().or_exit1();
        let cases = config.testcase_path(target).or_exit1();
        let src = config.src_path(&target).or_exit1();
        let run_command = config.construct_run_command(target).or_exit1();
        let build_command = config.construct_build_command(target).or_exit1();
        judge::judge(cases, run_command, build_command).or_exit1();
        return match config.service().or_exit1() {
            ServiceName::AtCoder => unimplemented!(),
            ServiceName::AtCoderBeta => {
                let lang_id = config.atcoder_lang_id(&target).or_exit1();
                atcoder_beta::submit(&contest, &target, lang_id, &src)
            }
        }.or_exit1();
    }
    unreachable!();
}
<|endoftext|>


<commit_before>#![feature(phase)]
#![feature(if_let)]

#[phase(plugin)]
extern crate rustecs_macros;

extern crate rustecs;
extern crate serialize;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;
use std::default::Default;
use std::rand;
use std::rand::Rng;
use std::num::abs;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
};
use piston::graphics::{
    AddBorder,
    AddRectangle,
    AddEllipse,
    AddColor,
    Context,
    Draw,
};
use piston::event::{
    Event,
    Render,
    Update,
    Input,
};

use rustecs::{
    Entities,
    Components,
};

static WINDOW_W: f64 = 800.0;
static WINDOW_H: f64 = 600.0;

fn draw_system(event: &Event,
              gl: &mut Gl,
              positions: &mut Components<Position>,
              shapes: &mut Components<Shape>,
              colors: &mut Components<Color>) {
    if let &Render(args) = event {
        let w = args.width as f64;
        let h = args.height as f64;
        gl.viewport(0, 0, w as i32, h as i32);
        let c = Context::abs(w, h);
        // Clear background.
        c.rgb(0.0, 0.0, 0.0).draw(gl);

        for (eid, pos) in positions.iter_mut() {
          let mut shape = Point;
          let mut border = None;
          if shapes.contains_key(eid) {
            shape = shapes.get_mut(eid).shape;
            border = shapes.get_mut(eid).border;
          }
          let (r, g, b) = if colors.contains_key(eid) {
              (colors.get_mut(eid).r, colors.get_mut(eid).g, colors.get_mut(eid).b)
          } else {
              (1.0, 1.0, 1.0)
          };
          let drawing = c.rgb(r, g, b);
          match (shape, border) {
              (Point, None)    => drawing.rect(1.0, 1.0, w, h).draw(gl),
              (Point, Some(b)) => drawing.rect(1.0, 1.0, w, h).border_radius(b).draw(gl),

              (Circle(rad), None)    => drawing.ellipse(pos.x, pos.y, rad, rad).draw(gl),
              (Circle(rad), Some(b)) => drawing.ellipse(pos.x, pos.y, rad, rad).border_radius(b).draw(gl),

              (Square(w,h), None)    => drawing.rect(pos.x, pos.y, w, h).draw(gl),
              (Square(w,h), Some(b)) => drawing.rect(pos.x, pos.y, w, h).border_radius(b).draw(gl),
          };
        }
    }
}

fn shimmer_system(shimmers: &mut Components<Shimmer>,
                 colors: &mut Components<Color>) {
    for (eid, _) in shimmers.iter_mut() {
        if !colors.contains_key(eid) {
            continue;
        }
        let color = colors.get_mut(eid);
        let ref mut rng = rand::task_rng();
        color.r = rng.gen_range(0.5, 1.0);
        color.g = rng.gen_range(0.5, 1.0);
        color.b = rng.gen_range(0.5, 1.0);
    }
}

fn move_system(event: &Event,
              positions: &mut Components<Position>,
              velocities: &mut Components<Velocity>) {
    if let &Update(args) = event {
        let dt = args.dt;
        for (eid, position) in positions.iter_mut() {
            if !velocities.contains_key(eid) {
                continue;
            }

            // If we have both a position and a velocity, integrate.
            let velocity = velocities.get_mut(eid);
            position.x += velocity.x * dt;
            position.y += velocity.y * dt;
            if position.x > WINDOW_W || position.x < 0.0 {
                velocity.x *= -1.0;
            }
            if position.y > WINDOW_H || position.y < 0.0 {
                velocity.y *= -1.0;
            }
        }
    }
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Position {
    x: f64,
    y: f64
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shimmer;

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Color {
    r: f32,
    g: f32,
    b: f32
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub enum ShapeVarient {
    Point,
    Circle(f64), // radius
    Square(f64, f64), // width, height
}

impl Default for ShapeVarient {
    fn default() -> ShapeVarient { Point }
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shape {
    shape: ShapeVarient,
    border: Option<f64>
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Velocity {
    x: f64,
    y: f64
}

world! {
    World,
    components Position, Shape, Velocity, Color, Shimmer;
}

fn make_ball() -> Entity {
    const BALL_R: f64 = 20.0;
    let shape = Circle(BALL_R);
    let x = (WINDOW_W - BALL_R) / 2.0;
    let y = (WINDOW_H - BALL_R) / 2.0;

    fn random_vel() -> f64 {
        let ref mut rng = rand::task_rng();
        rng.gen_range(80.0, 100.0) * if rng.gen() { 1.0 } else { -1.0 }
    }

    Entity::new()
        .with_shimmer(Shimmer)
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_velocity(
            Velocity {
                x: random_vel(),
                y: random_vel()
            })
        .with_shape(
            Shape {
                shape: shape,
                border: None
            })
        .with_color(
            Color{
                r: 1.0,
                g: 0.5,
                b: 0.2
            })
}

fn make_player(p1: bool) -> Entity {
    const FROM_WALL: f64 = 20.0;
    const PADDLE_W: f64 = 20.0;
    const PADDLE_H: f64 = 150.0;
    let x = if p1 {
        FROM_WALL
    } else {
        WINDOW_W - FROM_WALL - PADDLE_W
    };
    let y = (WINDOW_H - PADDLE_H) / 2.0;
    Entity::new()
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_shape(
            Shape {
                shape: Square(PADDLE_W, PADDLE_H),
                border: None
            })
        .with_color(
            Color{
                r: 0.5,
                g: 0.2,
                b: 0.8
            })
}

fn main() {
    let opengl = piston::shader_version::opengl::OpenGL_3_2;
    let mut window = WindowSDL2::new(
        opengl,
        WindowSettings {
            title: "Pong".to_string(),
            size: [WINDOW_W as u32, WINDOW_H as u32],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut gl = Gl::new(opengl);
    let mut world = World::new();

    world.add(make_player(true));
    world.add(make_player(false));
    world.add(make_ball());

    let event_settings = EventSettings {
        updates_per_second: 120,
        max_frames_per_second: 60,
    };

    for e in EventIterator::new(&mut window, &event_settings) {
        move_system(&e, &mut world.positions, &mut world.velocities);
        shimmer_system(&mut world.shimmers, &mut world.colors);
        draw_system(&e, &mut gl, &mut world.positions, &mut world.shapes, &mut world.colors);
    }
}
<commit_msg>Basic controls<commit_after>#![feature(phase)]
#![feature(if_let)]

#[phase(plugin)]
extern crate rustecs_macros;

extern crate rustecs;
extern crate serialize;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;
use std::default::Default;
use std::rand;
use std::rand::Rng;
use std::num::abs;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
};
use piston::graphics::{
    AddBorder,
    AddRectangle,
    AddEllipse,
    AddColor,
    Context,
    Draw,
};
use piston::event::{
    PressEvent,
    ReleaseEvent,
    Event,
    Render,
    Update,
    Input,
};
use piston::input::{
    keyboard,
    Keyboard,
};
use rustecs::{
    Entities,
    Components,
};

static WINDOW_W: f64 = 800.0;
static WINDOW_H: f64 = 600.0;

fn draw_system(event: &Event,
              gl: &mut Gl,
              positions: &mut Components<Position>,
              shapes: &mut Components<Shape>,
              colors: &mut Components<Color>) {
    if let &Render(args) = event {
        let w = args.width as f64;
        let h = args.height as f64;
        gl.viewport(0, 0, w as i32, h as i32);
        let c = Context::abs(w, h);
        // Clear background.
        c.rgb(0.0, 0.0, 0.0).draw(gl);

        for (eid, pos) in positions.iter_mut() {
          let mut shape = Point;
          let mut border = None;
          if shapes.contains_key(eid) {
            shape = shapes.get_mut(eid).shape;
            border = shapes.get_mut(eid).border;
          }
          let (r, g, b) = if colors.contains_key(eid) {
              (colors.get_mut(eid).r, colors.get_mut(eid).g, colors.get_mut(eid).b)
          } else {
              (1.0, 1.0, 1.0)
          };
          let drawing = c.rgb(r, g, b);
          match (shape, border) {
              (Point, None)    => drawing.rect(1.0, 1.0, w, h).draw(gl),
              (Point, Some(b)) => drawing.rect(1.0, 1.0, w, h).border_radius(b).draw(gl),

              (Circle(rad), None)    => drawing.ellipse(pos.x, pos.y, rad, rad).draw(gl),
              (Circle(rad), Some(b)) => drawing.ellipse(pos.x, pos.y, rad, rad).border_radius(b).draw(gl),

              (Square(w,h), None)    => drawing.rect(pos.x, pos.y, w, h).draw(gl),
              (Square(w,h), Some(b)) => drawing.rect(pos.x, pos.y, w, h).border_radius(b).draw(gl),
          };
        }
    }
}

fn shimmer_system(shimmers: &mut Components<Shimmer>,
                 colors: &mut Components<Color>) {
    for (eid, _) in shimmers.iter_mut() {
        if !colors.contains_key(eid) {
            continue;
        }
        let color = colors.get_mut(eid);
        let ref mut rng = rand::task_rng();
        color.r = rng.gen_range(0.5, 1.0);
        color.g = rng.gen_range(0.5, 1.0);
        color.b = rng.gen_range(0.5, 1.0);
    }
}

fn move_system(event: &Event,
              positions: &mut Components<Position>,
              velocities: &mut Components<Velocity>) {
    if let &Update(args) = event {
        let dt = args.dt;
        for (eid, position) in positions.iter_mut() {
            if !velocities.contains_key(eid) {
                continue;
            }

            // If we have both a position and a velocity, integrate.
            let velocity = velocities.get_mut(eid);
            position.x += velocity.x * dt;
            position.y += velocity.y * dt;
            if position.x > WINDOW_W || position.x < 0.0 {
                velocity.x *= -1.0;
            }
            if position.y > WINDOW_H || position.y < 0.0 {
                velocity.y *= -1.0;
            }
        }
    }
}

fn control_system(event: &Event,
              player_eids: &[u32, ..2],
              velocities: &mut Components<Velocity>) {

    const PADDLE_V: f64 = 800.0;
    let p1 = &player_eids[0];
    let p2 = &player_eids[1];
    event.press(|button| {
        if button == Keyboard(keyboard::W) {
            velocities.get_mut(p1).y = -PADDLE_V;
        } else if button == Keyboard(keyboard::S) {
            velocities.get_mut(p1).y = PADDLE_V;
        } else if button == Keyboard(keyboard::I) {
            velocities.get_mut(p2).y = -PADDLE_V;
        } else if button == Keyboard(keyboard::K) {
            velocities.get_mut(p2).y = PADDLE_V;
        }
    });
    event.release(|button| {
        if button == Keyboard(keyboard::W)
        || button == Keyboard(keyboard::S) {
            velocities.get_mut(p1).y = 0.0;
        } else if button == Keyboard(keyboard::I)
               || button == Keyboard(keyboard::K) {
            velocities.get_mut(p2).y = 0.0;
        }
    });
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Position {
    x: f64,
    y: f64
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shimmer;

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Color {
    r: f32,
    g: f32,
    b: f32
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub enum ShapeVarient {
    Point,
    Circle(f64), // radius
    Square(f64, f64), // width, height
}

impl Default for ShapeVarient {
    fn default() -> ShapeVarient { Point }
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shape {
    shape: ShapeVarient,
    border: Option<f64>
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Velocity {
    x: f64,
    y: f64
}

world! {
    World,
    components Position, Shape, Velocity, Color, Shimmer;
}

fn make_ball() -> Entity {
    const BALL_R: f64 = 20.0;
    let shape = Circle(BALL_R);
    let x = (WINDOW_W - BALL_R) / 2.0;
    let y = (WINDOW_H - BALL_R) / 2.0;

    fn random_vel() -> f64 {
        let ref mut rng = rand::task_rng();
        rng.gen_range(80.0, 100.0) * if rng.gen() { 1.0 } else { -1.0 }
    }

    Entity::new()
        .with_shimmer(Shimmer)
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_velocity(
            Velocity {
                x: random_vel(),
                y: random_vel()
            })
        .with_shape(
            Shape {
                shape: shape,
                border: None
            })
        .with_color(
            Color{
                r: 1.0,
                g: 0.5,
                b: 0.2
            })
}

fn make_player(p1: bool) -> Entity {
    const FROM_WALL: f64 = 20.0;
    const PADDLE_W: f64 = 20.0;
    const PADDLE_H: f64 = 150.0;
    let x = if p1 {
        FROM_WALL
    } else {
        WINDOW_W - FROM_WALL - PADDLE_W
    };
    let y = (WINDOW_H - PADDLE_H) / 2.0;
    Entity::new()
        .with_velocity(Velocity { x: 0.0, y: 0.0 } )
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_shape(
            Shape {
                shape: Square(PADDLE_W, PADDLE_H),
                border: None
            })
        .with_color(
            Color{
                r: 0.5,
                g: 0.2,
                b: 0.8
            })
}

fn main() {
    let opengl = piston::shader_version::opengl::OpenGL_3_2;
    let mut window = WindowSDL2::new(
        opengl,
        WindowSettings {
            title: "Pong".to_string(),
            size: [WINDOW_W as u32, WINDOW_H as u32],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut gl = Gl::new(opengl);
    let mut world = World::new();

    let players = [
        world.add(make_player(true)),
        world.add(make_player(false))
    ];
    world.add(make_ball());

    let event_settings = EventSettings {
        updates_per_second: 120,
        max_frames_per_second: 60,
    };

    for e in EventIterator::new(&mut window, &event_settings) {
        control_system(&e, &players, &mut world.velocities);
        move_system(&e, &mut world.positions, &mut world.velocities);
        shimmer_system(&mut world.shimmers, &mut world.colors);
        draw_system(&e, &mut gl, &mut world.positions, &mut world.shapes, &mut world.colors);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate error_chain;

extern crate clap;
extern crate serde;
extern crate rmp_serde;
extern crate sodiumoxide;
extern crate xdg;

extern crate mine;

mod init;
mod insert;
mod show;
mod set_tag;

use clap::{Arg, App, AppSettings, SubCommand};

use mine::Mine;

mod errors {
    extern crate mine;
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain! {
        links {
            MineLib(mine::errors::Error, mine::errors::ErrorKind);
        }
    }
}

use errors::*;

quick_main!(run);

fn run() -> Result<()> {
    sodiumoxide::init();

    let matches = App::new("mine")
                    .version("0.0.0")
                    .author("Johannes Löthberg <johannes@kyriasis.com>")
                    .about("NaCL based password manager in Rust")
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("init")
                                .about("initialize password store"))
                    .subcommand(SubCommand::with_name("insert")
                                .about("insert a password")
                                .arg(Arg::with_name("NAME")
                                     .required(true)
                                     .index(1))
                                .arg(Arg::with_name("PASSWORD")
                                     .required(true)
                                     .index(2)))
                    .subcommand(SubCommand::with_name("show")
                                .about("show a password")
                                .arg(Arg::with_name("NAME")
                                     .help("Password name")
                                     .required(true)
                                     .index(1)))
                    .subcommand(SubCommand::with_name("set-tag")
                                .about("set a tag on a password")
                                .arg(Arg::with_name("PASSWORD")
                                     .required(true)
                                     .index(1))
                                .arg(Arg::with_name("TAG")
                                     .required(true)
                                     .index(2))
                                .arg(Arg::with_name("VALUE")
                                     .required(true)
                                     .index(3)))
                    .get_matches();

    let mine = Mine::new("mine")
        .chain_err(|| "failed to initialize mine")?;

    let subcommand = matches.subcommand_name().unwrap();
    let sub_matches = matches.subcommand_matches(subcommand).unwrap();

    match subcommand {
        "init" => init::init_run(mine)?,
        "insert" => insert::insert_run(mine, sub_matches)?,
        "show" => show::show_run(mine, sub_matches)?,
        "set-tag" => set_tag::set_tag_run(mine, sub_matches)?,
        _ => unreachable!(),
    }

    Ok(())
}

<commit_msg>Make clap help colored<commit_after>#[macro_use]
extern crate error_chain;

extern crate clap;
extern crate serde;
extern crate rmp_serde;
extern crate sodiumoxide;
extern crate xdg;

extern crate mine;

mod init;
mod insert;
mod show;
mod set_tag;

use clap::{Arg, App, AppSettings, SubCommand};

use mine::Mine;

mod errors {
    extern crate mine;
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain! {
        links {
            MineLib(mine::errors::Error, mine::errors::ErrorKind);
        }
    }
}

use errors::*;

quick_main!(run);

fn run() -> Result<()> {
    sodiumoxide::init();

    let matches = App::new("mine")
                    .version("0.0.0")
                    .author("Johannes Löthberg <johannes@kyriasis.com>")
                    .about("NaCL based password manager in Rust")
                    .global_settings(&[AppSettings::ColoredHelp])
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("init")
                                .about("initialize password store"))
                    .subcommand(SubCommand::with_name("insert")
                                .about("insert a password")
                                .arg(Arg::with_name("NAME")
                                     .required(true)
                                     .index(1))
                                .arg(Arg::with_name("PASSWORD")
                                     .required(true)
                                     .index(2)))
                    .subcommand(SubCommand::with_name("show")
                                .about("show a password")
                                .arg(Arg::with_name("NAME")
                                     .help("Password name")
                                     .required(true)
                                     .index(1)))
                    .subcommand(SubCommand::with_name("set-tag")
                                .about("set a tag on a password")
                                .arg(Arg::with_name("PASSWORD")
                                     .required(true)
                                     .index(1))
                                .arg(Arg::with_name("TAG")
                                     .required(true)
                                     .index(2))
                                .arg(Arg::with_name("VALUE")
                                     .required(true)
                                     .index(3)))
                    .get_matches();

    let mine = Mine::new("mine")
        .chain_err(|| "failed to initialize mine")?;

    let subcommand = matches.subcommand_name().unwrap();
    let sub_matches = matches.subcommand_matches(subcommand).unwrap();

    match subcommand {
        "init" => init::init_run(mine)?,
        "insert" => insert::insert_run(mine, sub_matches)?,
        "show" => show::show_run(mine, sub_matches)?,
        "set-tag" => set_tag::set_tag_run(mine, sub_matches)?,
        _ => unreachable!(),
    }

    Ok(())
}

<|endoftext|>