<commit_before>/**
 * s3-file-upload
 */

extern crate rusoto;
extern crate crypto;

use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::fs::{File, remove_file};
use std::io::{stdin, Read, Write};
use std::clone::Clone;
use std::collections::HashMap;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, GetObjectRequest, PutObjectRequest};
use rusoto::default_tls_client;

use crypto::md5::Md5;
use crypto::digest::Digest;

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: PathBuf,
    md5: String
}

/**
 * Get the mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        Some(&"txt")  => "text/plain",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get the MD5 checksum of a local file
 */
fn local_file_get_md5(file: &LocalFile) -> String
{
    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut md5_checksum = Md5::new();
            md5_checksum.input(&contents);
            return md5_checksum.result_str();
        }
        Err(error) =>
        {
            println!("Error: {}", error);
            return String::from("");
        }
    }
}

/**
 * Get a relative path for a local file
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String, public_file: bool)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).expect("Could not open file");
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut acl = String::from("private");
            if public_file
            {
                acl = String::from("public-read");
            }

            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(acl),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Create the local file checksums
 */
fn local_file_create_checksums(files: &Vec<LocalFile>, local_path: &String) -> LocalFile
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    let mut checksums_file = File::create(path.to_str().unwrap()).expect("Could not create checksums");

    for file in files.clone()
    {
        let checksums_line = format!("{} {}\n", local_file_get_relative_path(&file, local_path), file.md5);
        checksums_file.write(checksums_line.as_bytes()).expect("Could not write checksums");
    }

    return LocalFile { path: path, md5: String::from("") };
}

/**
 * Delete the local file checksums
 */
fn local_file_delete_checksums(local_path: &String)
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    remove_file(path).expect("Could not remove checksums");
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }
}

/**
 * Struct for checksums
 */
#[derive(Clone)]
struct Checksums
{
    files: HashMap<String, String>
}

/**
 * Get the s3 bucket checksums
 */
fn get_bucket_object_checksums(bucket_name: &String, bucket_objects: &Vec<Object>) -> Option<Checksums>
{
    match bucket_objects.iter().position(|obj| obj.clone().key.unwrap() == String::from("checksums.txt"))
    {
        Some(index) =>
        {
            let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
            let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

            let obj = bucket_objects.get(index).clone().unwrap();

            let request = GetObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: obj.clone().key.unwrap(),
                ..GetObjectRequest::default()
            };

            if let Ok(response) = client.get_object(&request)
            {
                let contents_bytes = response.body.unwrap();
                let contents = String::from_utf8_lossy(&contents_bytes);

                let mut hash_map = HashMap::new();

                for line in contents.split("\n")
                {
                    let mut line_split = line.split_whitespace();
                    let path = line_split.next();
                    let md5 = line_split.next();

                    if path != None && md5 != None
                    {
                        let path = String::from(path.unwrap());
                        let md5 = String::from(md5.unwrap());
                        hash_map.insert(path, md5);
                    }
                }

                let checksums = Checksums {files: hash_map};
                Some(checksums)
            }
            else
            {
                None
            }
        },
        None =>
        {
            None
        }
    }
}

/**
 * Check if the file matches the checksums
 */
fn local_file_matches_checksums(file: &LocalFile, checksums: &Checksums) -> bool
{
    for (path, md5) in &checksums.files
    {
        if file.md5 == md5.to_owned()
        {
            return true;
        }
    }
    return false;
}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>, ignored_directories: &mut Vec<String>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    if !ignored_directories.contains(&file.file_name().to_str().unwrap().to_owned())
                    {
                        get_local_files(&file.path().to_str().unwrap().to_owned(), files, ignored_directories);
                    }
                }
                else
                {
                    let temp_file = LocalFile { path: file.path(), md5: String::from("") };
                    let file = LocalFile { path: file.path(), md5: local_file_get_md5(&temp_file) };
                    files.push(file);
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("\nUsage:\n\ns3-file-upload LOCAL_PATH BUCKET_NAME [OPTIONS]");
        println!("\nOptions:\n\n--ignored_directories    List of directory names using a comma separator, e.g. --ignored_directories=ignored_dir_one,ignored_dir_two");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut ignored_directories: Vec<String> = Vec::new();

    // Set the ignored directories if the ignored_directories parameter is set
    for parameter in env::args()
    {
        if parameter.contains("--ignored_directories=")
        {
            let directories = parameter.split("=").nth(1).unwrap();

            for directory in directories.split(",")
            {
                ignored_directories.push(String::from(directory));
            }
        }
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files, &mut ignored_directories);

    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    let checksums = match get_bucket_object_checksums(&bucket_name, &bucket_objects)
    {
        Some(checksums) =>
        {
            Some(checksums)
        },
        None =>
        {
            let hash_map = HashMap::new();
            let checksums = Checksums {files: hash_map};
            Some(checksums)
        }
    }.unwrap();

    println!("\nFiles found to be uploaded:\n");

    for file in &files
    {
        if !local_file_matches_checksums(&file, &checksums)
        {
            println!("{}", local_file_get_relative_path(file, &local_path));
        }
    }

    let mut input_string = String::new();

    while input_string != "y" && input_string != "n"
    {
        println!("\nConfirm upload? <y/N>");

        input_string.clear();
        stdin().read_line(&mut input_string).expect("Did not input string");
        input_string = String::from(input_string.trim().to_lowercase());

        if input_string == "" || input_string == "no"
        {
            input_string = String::from("n");
        }
        else if input_string == "yes"
        {
            input_string = String::from("y");
        }
    }

    let confirm_upload = input_string == "y";

    if confirm_upload
    {
        for file in &files
        {
            if !local_file_matches_checksums(&file, &checksums)
            {
                local_file_upload_to_bucket(&file, &local_path, &bucket_name, true);
            }
        }

        let new_checksums = local_file_create_checksums(&files, &local_path);
        local_file_upload_to_bucket(&new_checksums, &local_path, &bucket_name, false);
        local_file_delete_checksums(&local_path);
    }
    else
    {
        println!("Upload cancelled")
    }
}
<commit_msg>Files being either new or modified is shown in the upload list<commit_after>/**
 * s3-file-upload
 */

extern crate rusoto;
extern crate crypto;

use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::fs::{File, remove_file};
use std::io::{stdin, Read, Write};
use std::clone::Clone;
use std::collections::HashMap;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, GetObjectRequest, PutObjectRequest};
use rusoto::default_tls_client;

use crypto::md5::Md5;
use crypto::digest::Digest;

/**
 * Enum for file status
 */
#[derive(PartialEq)]
enum FileStatus
{
    NotModified,
    Modified,
    New
}

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: PathBuf,
    md5: String
}

/**
 * Get the mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        Some(&"txt")  => "text/plain",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get the MD5 checksum of a local file
 */
fn local_file_get_md5(file: &LocalFile) -> String
{
    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut md5_checksum = Md5::new();
            md5_checksum.input(&contents);
            return md5_checksum.result_str();
        }
        Err(error) =>
        {
            println!("Error: {}", error);
            return String::from("");
        }
    }
}

/**
 * Get a relative path for a local file
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String, public_file: bool)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).expect("Could not open file");
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut acl = String::from("private");
            if public_file
            {
                acl = String::from("public-read");
            }

            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(acl),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Create the local file checksums
 */
fn local_file_create_checksums(files: &Vec<LocalFile>, local_path: &String) -> LocalFile
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    let mut checksums_file = File::create(path.to_str().unwrap()).expect("Could not create checksums");

    for file in files.clone()
    {
        let checksums_line = format!("{} {}\n", local_file_get_relative_path(&file, local_path), file.md5);
        checksums_file.write(checksums_line.as_bytes()).expect("Could not write checksums");
    }

    return LocalFile { path: path, md5: String::from("") };
}

/**
 * Delete the local file checksums
 */
fn local_file_delete_checksums(local_path: &String)
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    remove_file(path).expect("Could not remove checksums");
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }
}

/**
 * Struct for checksums
 */
#[derive(Clone)]
struct Checksums
{
    files: HashMap<String, String>
}

/**
 * Get the s3 bucket checksums
 */
fn get_bucket_object_checksums(bucket_name: &String, bucket_objects: &Vec<Object>) -> Option<Checksums>
{
    match bucket_objects.iter().position(|obj| obj.clone().key.unwrap() == String::from("checksums.txt"))
    {
        Some(index) =>
        {
            let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
            let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

            let obj = bucket_objects.get(index).clone().unwrap();

            let request = GetObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: obj.clone().key.unwrap(),
                ..GetObjectRequest::default()
            };

            if let Ok(response) = client.get_object(&request)
            {
                let contents_bytes = response.body.unwrap();
                let contents = String::from_utf8_lossy(&contents_bytes);

                let mut hash_map = HashMap::new();

                for line in contents.split("\n")
                {
                    let mut line_split = line.split_whitespace();
                    let path = line_split.next();
                    let md5 = line_split.next();

                    if path != None && md5 != None
                    {
                        let path = String::from(path.unwrap());
                        let md5 = String::from(md5.unwrap());
                        hash_map.insert(path, md5);
                    }
                }

                let checksums = Checksums {files: hash_map};
                Some(checksums)
            }
            else
            {
                None
            }
        },
        None =>
        {
            None
        }
    }
}

/**
 * Check if the file matches the checksums
 */
fn local_file_matches_checksums(local_path: &String, file: &LocalFile, checksums: &Checksums) -> FileStatus
{
    for (path, md5) in &checksums.files
    {
        if &local_file_get_relative_path(file, local_path) == path
        {
            if file.md5 == md5.to_owned()
            {
                return FileStatus::NotModified;
            }
            else
            {
                return FileStatus::Modified;
            }
        }
    }
    return FileStatus::New;
}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>, ignored_directories: &mut Vec<String>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    if !ignored_directories.contains(&file.file_name().to_str().unwrap().to_owned())
                    {
                        get_local_files(&file.path().to_str().unwrap().to_owned(), files, ignored_directories);
                    }
                }
                else
                {
                    let temp_file = LocalFile { path: file.path(), md5: String::from("") };
                    let file = LocalFile { path: file.path(), md5: local_file_get_md5(&temp_file) };
                    files.push(file);
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("\nUsage:\n\ns3-file-upload LOCAL_PATH BUCKET_NAME [OPTIONS]");
        println!("\nOptions:\n\n--ignored_directories    List of directory names using a comma separator, e.g. --ignored_directories=ignored_dir_one,ignored_dir_two");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut ignored_directories: Vec<String> = Vec::new();

    // Set the ignored directories if the ignored_directories parameter is set
    for parameter in env::args()
    {
        if parameter.contains("--ignored_directories=")
        {
            let directories = parameter.split("=").nth(1).unwrap();

            for directory in directories.split(",")
            {
                ignored_directories.push(String::from(directory));
            }
        }
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files, &mut ignored_directories);

    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    let checksums = match get_bucket_object_checksums(&bucket_name, &bucket_objects)
    {
        Some(checksums) =>
        {
            Some(checksums)
        },
        None =>
        {
            let hash_map = HashMap::new();
            let checksums = Checksums {files: hash_map};
            Some(checksums)
        }
    }.unwrap();

    println!("\nFiles found to be uploaded:\n");

    for file in &files
    {
        let file_status: FileStatus = local_file_matches_checksums(&local_path, &file, &checksums);
        if file_status == FileStatus::Modified
        {
            println!("Modified: {}", local_file_get_relative_path(file, &local_path));
        }
        else if file_status == FileStatus::New
        {
            println!("New: {}", local_file_get_relative_path(file, &local_path));
        }
    }

    let mut input_string = String::new();

    while input_string != "y" && input_string != "n"
    {
        println!("\nConfirm upload? <y/N>");

        input_string.clear();
        stdin().read_line(&mut input_string).expect("Did not input string");
        input_string = String::from(input_string.trim().to_lowercase());

        if input_string == "" || input_string == "no"
        {
            input_string = String::from("n");
        }
        else if input_string == "yes"
        {
            input_string = String::from("y");
        }
    }

    let confirm_upload = input_string == "y";

    if confirm_upload
    {
        for file in &files
        {
            let file_status: FileStatus = local_file_matches_checksums(&local_path, &file, &checksums);
            if file_status == FileStatus::Modified || file_status == FileStatus::New
            {
                local_file_upload_to_bucket(&file, &local_path, &bucket_name, true);
            }
        }

        let new_checksums = local_file_create_checksums(&files, &local_path);
        local_file_upload_to_bucket(&new_checksums, &local_path, &bucket_name, false);
        local_file_delete_checksums(&local_path);
    }
    else
    {
        println!("Upload cancelled")
    }
}
<|endoftext|>


<commit_before>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]

use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

extern crate ncurses;
use ncurses::*;

//extern crate getopts;
//use getopts::Options;

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    let mut cursorstate:usize = 0; //0 is left nibble, 1 is right nibble, 2 is ascii
    const SPALTEN:usize = 16;
    let mut command = String::new();

    initscr(); //start ncursesw
    let screenheight : usize = getmaxy(stdscr) as usize;
    cbreak();  //ctrl+z and fg works with this
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    /*
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("f", "", "set file name", "NAME");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    }
    let path = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    };*/

    let path = if args.len() > 1 {
        printw(&format!("Opening file {}", args[1]));
        args[1].clone()
    } else {
        printw(&format!("No file specified. Trying to open foo.txt"));
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

//TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display,
            Error::description(&why)),
        Ok(file) => file,
    };

    file.read_to_end(&mut buffer).ok().expect("File could not be read.");

    let mut mode = 0; // 0 Command mode, 1 replace next char, 2 type a command, 3 Insert

    draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command, cursorstate);

    let mut key;
//    key = getch();
//    printw(&format!("{:?}", key));

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => { // Button "h"
                    if cursorstate == 2 && cursorpos != 0 {cursorpos-=1}
                    else if cursorstate == 1 {cursorstate = 0}
                    else if cursorpos != 0 {cursorstate = 1; cursorpos-=1}
                },
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => { // Button "l"
                    if cursorstate == 2 && cursorpos != buffer.len()-1 {cursorpos+=1}
                    else if cursorstate == 0 {cursorstate = 1}
                    else if cursorstate == 1 && cursorpos != buffer.len()-1 {cursorstate = 0; cursorpos+=1}
                },
                 48 => { // Start of line is "to start"
                     cursorpos -= cursorpos%16;
                     if cursorstate == 1 {cursorstate = 0}
                },
                 36 => { //dollar is "to end"
                     if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)}
                        else {cursorpos = buffer.len()-1};
                    if cursorstate == 0 {cursorstate = 1;}
                },
                114 => mode = 1, //r replaces the next char
                120 => {buffer.remove(cursorpos);}, //x remove the next char
                105 => mode = 3, //i goes to insert mode
                 58 => {mode = 2; command.clear();}, // ":"
                 74 => {if cursorstate == 2 {cursorstate = 0;} else {cursorstate = 2;}}, // J jumps to ascii
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 && cursorstate == 2 { // r was pressed so replace next char in ascii mode
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8 },
                _ => (),
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 0 {
            match key { // Change left nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 55)<<4 },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 48)<<4 },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 1 {
            match key { // Change right nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55) },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48) },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); },
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            if command == "wq".to_string() {
                                ragequitnow = 1;
                            }
                            command.clear();mode = 0;
                        }
                        else if command == "q".to_string() {
                            ragequitnow = 1;
                        }
                        else {
                            command.clear();
                            command.push_str("Bad_command!");
                            mode = 0;
                        }
                    },
                _ => (),
            }
        } else
        if mode == 3 {
            if cursorstate == 0 { // Left nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        {buffer.insert(cursorpos, (c as u8 - 55)<<4 ); cursorstate = 1;},
                    c @ 48... 57 => // 0-9
                        {buffer.insert(cursorpos, (c as u8 - 48)<<4); cursorstate = 1;},
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 1 { // Right nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55); cursorstate = 0; cursorpos+=1; },
                    c @ 48...57 => // 0-9
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48); cursorstate = 0; cursorpos+=1; },
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 2 { // Ascii
                match key {
                    c @ 32...126 => { buffer.insert(cursorpos, c as u8); },
                    27 => {mode = 0;},
                    _ => (),
                }
            }
        }
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command, cursorstate);
    }

    refresh();
    endwin();
}

fn draw(buffer:&Vec<u8>, cursorpos:usize, spalten:usize, maxzeilen:usize, mode:usize, command:&String, cursorstate:usize) {
//    let zeilen = buffer.len() / spalten;
    erase();

    let mut zeilen = buffer.len() / spalten;
    if zeilen >= maxzeilen-1 { // Last line reserved for Status/Commands/etc (Like in vim)
        zeilen = maxzeilen-2;
    }

    for z in 0 .. zeilen+1 {
        printw(&format!("{:08X}: ", z * spalten )); // 8 hex digits (4GB/spalten or 0.25GB@spalten=16)
        printw(" "); // Additional space between line number and hex
        for s in 0 .. spalten {
            if z*spalten+s < buffer.len() {
                if z*spalten+s == cursorpos { // Color of left nibble
                    if cursorstate == 0 {attron(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attron(A_UNDERLINE());}
                }
                printw(&format!("{:01X}", buffer[z*spalten+s]>>4) ); // Display left nibble
                if z*spalten+s == cursorpos { // End of color left nibble
                    if cursorstate == 0 {attroff(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attroff(A_UNDERLINE());}
                }

                if z*spalten+s == cursorpos { // Color of right nibble
                    if cursorstate == 1 {attron(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attron(A_UNDERLINE());}
                }
                printw(&format!("{:01X}", buffer[z*spalten+s]&0x0F) ); // Display right nibble
                if z*spalten+s == cursorpos {
                    if cursorstate == 1 {attroff(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attroff(A_UNDERLINE());}
                }
                printw(" ");
            } else {
                printw("-- ");
            }
        }
        printw(" "); // Additional space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos {
                if cursorstate == 2 {attron(COLOR_PAIR(1) | A_STANDOUT());}
                else {attron(A_UNDERLINE());}
            }
            if z*spalten+s < buffer.len() {
                if let c @ 32...126 = buffer[z*spalten+s] {
                    if c as char == '%' {
                        printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                    } else {
                        printw(&format!("{}", c as char) );
                    }
                }
                else {printw(&format!(".") );}
            }
            if z*spalten+s == cursorpos {
                if cursorstate == 2 {attroff(COLOR_PAIR(1) | A_STANDOUT());}
                else {attroff(A_UNDERLINE());}
            }
        }
        printw("\n");
    }
    for _ in 0 .. maxzeilen-zeilen-2 {
        printw("\n"); // Put the cursor on last line of terminal
    }
    if mode == 2 {
        printw(":"); // Indicate that a command can be typed in
    }
    if mode == 3 {
        printw("insert"); // Indicate that insert mode is active
    }
    printw(&format!("{}", command));
}
<commit_msg>Bugfix, writing in ascii insert mode was reversed<commit_after>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]

use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

extern crate ncurses;
use ncurses::*;

//extern crate getopts;
//use getopts::Options;

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    let mut cursorstate:usize = 0; //0 is left nibble, 1 is right nibble, 2 is ascii
    const SPALTEN:usize = 16;
    let mut command = String::new();

    initscr(); //start ncursesw
    let screenheight : usize = getmaxy(stdscr) as usize;
    cbreak();  //ctrl+z and fg works with this
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    /*
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("f", "", "set file name", "NAME");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    }
    let path = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    };*/

    let path = if args.len() > 1 {
        printw(&format!("Opening file {}", args[1]));
        args[1].clone()
    } else {
        printw(&format!("No file specified. Trying to open foo.txt"));
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

//TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display,
            Error::description(&why)),
        Ok(file) => file,
    };

    file.read_to_end(&mut buffer).ok().expect("File could not be read.");

    let mut mode = 0; // 0 Command mode, 1 replace next char, 2 type a command, 3 Insert

    draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command, cursorstate);

    let mut key;
//    key = getch();
//    printw(&format!("{:?}", key));

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => { // Button "h"
                    if cursorstate == 2 && cursorpos != 0 {cursorpos-=1}
                    else if cursorstate == 1 {cursorstate = 0}
                    else if cursorpos != 0 {cursorstate = 1; cursorpos-=1}
                },
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => { // Button "l"
                    if cursorstate == 2 && cursorpos != buffer.len()-1 {cursorpos+=1}
                    else if cursorstate == 0 {cursorstate = 1}
                    else if cursorstate == 1 && cursorpos != buffer.len()-1 {cursorstate = 0; cursorpos+=1}
                },
                 48 => { // Start of line is "to start"
                     cursorpos -= cursorpos%16;
                     if cursorstate == 1 {cursorstate = 0}
                },
                 36 => { //dollar is "to end"
                     if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)}
                        else {cursorpos = buffer.len()-1};
                    if cursorstate == 0 {cursorstate = 1;}
                },
                114 => mode = 1, //r replaces the next char
                120 => {buffer.remove(cursorpos);}, //x remove the next char
                105 => mode = 3, //i goes to insert mode
                 58 => {mode = 2; command.clear();}, // ":"
                 74 => {if cursorstate == 2 {cursorstate = 0;} else {cursorstate = 2;}}, // J jumps to ascii
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 && cursorstate == 2 { // r was pressed so replace next char in ascii mode
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8 },
                _ => (),
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 0 {
            match key { // Change left nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 55)<<4 },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0x0F | (c as u8 - 48)<<4 },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 1 && cursorstate == 1 {
            match key { // Change right nibble
                c @ 65...70 | c @ 97...102 => // A-F or a-f
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55) },
                c @ 48... 57 => // 0-9
                    { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48) },
                _ => ()
            }
            mode = 0;
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); },
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            if command == "wq".to_string() {
                                ragequitnow = 1;
                            }
                            command.clear();mode = 0;
                        }
                        else if command == "q".to_string() {
                            ragequitnow = 1;
                        }
                        else {
                            command.clear();
                            command.push_str("Bad_command!");
                            mode = 0;
                        }
                    },
                _ => (),
            }
        } else
        if mode == 3 {
            if cursorstate == 0 { // Left nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        {buffer.insert(cursorpos, (c as u8 - 55)<<4 ); cursorstate = 1;},
                    c @ 48... 57 => // 0-9
                        {buffer.insert(cursorpos, (c as u8 - 48)<<4); cursorstate = 1;},
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 1 { // Right nibble
                match key {
                    c @ 65...70 | c @ 97...102 => // A-F or a-f
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 55); cursorstate = 0; cursorpos+=1; },
                    c @ 48...57 => // 0-9
                        { buffer[cursorpos] = buffer[cursorpos]&0xF0 | (c as u8 - 48); cursorstate = 0; cursorpos+=1; },
                    27 => {mode = 0;},
                    _ => ()
                }
            } else
            if cursorstate == 2 { // Ascii
                match key {
                    c @ 32...126 => { buffer.insert(cursorpos, c as u8); cursorpos+=1; },
                    27 => {mode = 0;},
                    _ => (),
                }
            }
        }
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command, cursorstate);
    }

    refresh();
    endwin();
}

fn draw(buffer:&Vec<u8>, cursorpos:usize, spalten:usize, maxzeilen:usize, mode:usize, command:&String, cursorstate:usize) {
//    let zeilen = buffer.len() / spalten;
    erase();

    let mut zeilen = buffer.len() / spalten;
    if zeilen >= maxzeilen-1 { // Last line reserved for Status/Commands/etc (Like in vim)
        zeilen = maxzeilen-2;
    }

    for z in 0 .. zeilen+1 {
        printw(&format!("{:08X}: ", z * spalten )); // 8 hex digits (4GB/spalten or 0.25GB@spalten=16)
        printw(" "); // Additional space between line number and hex
        for s in 0 .. spalten {
            if z*spalten+s < buffer.len() {
                if z*spalten+s == cursorpos { // Color of left nibble
                    if cursorstate == 0 {attron(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attron(A_UNDERLINE());}
                }
                printw(&format!("{:01X}", buffer[z*spalten+s]>>4) ); // Display left nibble
                if z*spalten+s == cursorpos { // End of color left nibble
                    if cursorstate == 0 {attroff(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attroff(A_UNDERLINE());}
                }

                if z*spalten+s == cursorpos { // Color of right nibble
                    if cursorstate == 1 {attron(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attron(A_UNDERLINE());}
                }
                printw(&format!("{:01X}", buffer[z*spalten+s]&0x0F) ); // Display right nibble
                if z*spalten+s == cursorpos {
                    if cursorstate == 1 {attroff(COLOR_PAIR(1) | A_STANDOUT());}
                    else if cursorstate == 2 {attroff(A_UNDERLINE());}
                }
                printw(" ");
            } else {
                printw("-- ");
            }
        }
        printw(" "); // Additional space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos {
                if cursorstate == 2 {attron(COLOR_PAIR(1) | A_STANDOUT());}
                else {attron(A_UNDERLINE());}
            }
            if z*spalten+s < buffer.len() {
                if let c @ 32...126 = buffer[z*spalten+s] {
                    if c as char == '%' {
                        printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                    } else {
                        printw(&format!("{}", c as char) );
                    }
                }
                else {printw(&format!(".") );}
            }
            if z*spalten+s == cursorpos {
                if cursorstate == 2 {attroff(COLOR_PAIR(1) | A_STANDOUT());}
                else {attroff(A_UNDERLINE());}
            }
        }
        printw("\n");
    }
    for _ in 0 .. maxzeilen-zeilen-2 {
        printw("\n"); // Put the cursor on last line of terminal
    }
    if mode == 2 {
        printw(":"); // Indicate that a command can be typed in
    }
    if mode == 3 {
        printw("insert"); // Indicate that insert mode is active
    }
    printw(&format!("{}", command));
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate multirust;
#[macro_use(shared_ntfy)]
extern crate rust_install;

use std::ffi::OsStr;
use std::error::Error;

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

use multirust::{Cfg, Notification, Toolchain};

type Result<T> = std::result::Result<T, Box<Error>>;

// Copied from multirust-rs.
fn set_globals() -> multirust::Result<Cfg> {
    // Base config
    let verbose = false;
    Cfg::from_env(shared_ntfy!(move |n: Notification| {
        use multirust::notify::NotificationLevel::*;
        match n.level() {
            Verbose => {
                if verbose {
                    println!("{}", n);
                }
            }
            _ => {
                println!("{}", n);
            }
        }
    }))
}

struct Cmd<'a> {
    program: &'a OsStr,
    args: &'a [&'a OsStr],
}

impl<'a> Cmd<'a> {
    fn from(command: &'a [&'a OsStr]) -> Cmd<'a> {
        let program = command[0];
        let args = &command[1..];

        Cmd {
            program: program,
            args: args,
        }
    }

    fn succeeds_with<'b>(&self, toolchain: &Toolchain<'b>) -> Result<bool> {
        let mut cmd = try!(toolchain.create_command(self.program));
        cmd.args(self.args);
        let status = try!(cmd.status());
        Ok(status.success())
    }
}

fn run_rust_bisect() -> Result<()> {
    let matches = App::new("rustc-bisect")
                      .author("Kamal Marhubi <kamal@marhubi.com>")
                      .setting(AppSettings::TrailingVarArg)
                      .arg(Arg::with_name("toolchain")
                               .long("tmp-toolchain")
                               .takes_value(true)
                               .value_name("TOOLCHAIN")
                               .required(true))
                      .arg(Arg::with_name("good")
                               .long("good")
                               .takes_value(true)
                               .value_name("TOOLCHAIN"))
                      .arg(Arg::with_name("bad")
                               .long("bad")
                               .takes_value(true)
                               .value_name("TOOLCHAIN"))
                      .arg(Arg::with_name("COMMAND")
                               .multiple(true)
                               .required(true))
                      .get_matches();

    let cfg = try!(set_globals());

    let cmd: Vec<_> = matches.values_of_os("COMMAND").expect("COMMAND").collect();
    let cmd = Cmd::from(&cmd[..]);

    let toolchain = try!(cfg.get_toolchain(matches.value_of("toolchain").expect("toolchain"),
                                           false));
    try!(toolchain.install_from_dist_if_not_installed());

    println!("{}", try!(cmd.succeeds_with(&toolchain)));

    Ok(())
}

fn main() {
    run_rust_bisect().expect("something went wrong");

}
<commit_msg>Minor formatting changes<commit_after>#[macro_use(shared_ntfy)]
extern crate rust_install;

extern crate clap;
extern crate multirust;

use std::ffi::OsStr;
use std::error::Error;

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

use multirust::{Cfg, Notification, Toolchain};

type Result<T> = std::result::Result<T, Box<Error>>;

// Copied from multirust-rs.
fn set_globals() -> multirust::Result<Cfg> {
    // Base config
    let verbose = false;
    Cfg::from_env(shared_ntfy!(move |n: Notification| {
        use multirust::notify::NotificationLevel::*;
        match n.level() {
            Verbose => {
                if verbose {
                    println!("{}", n);
                }
            }
            _ => {
                println!("{}", n);
            }
        }
    }))
}

struct Cmd<'a> {
    program: &'a OsStr,
    args: &'a [&'a OsStr],
}

impl<'a> Cmd<'a> {
    fn from(command: &'a [&'a OsStr]) -> Cmd<'a> {
        let program = command[0];
        let args = &command[1..];

        Cmd {
            program: program,
            args: args,
        }
    }

    fn succeeds_with<'b>(&self, toolchain: &Toolchain<'b>) -> Result<bool> {
        let mut cmd = try!(toolchain.create_command(self.program));
        cmd.args(self.args);
        let status = try!(cmd.status());
        Ok(status.success())
    }
}

fn run_rust_bisect() -> Result<()> {
    let matches = App::new("rustc-bisect")
                      .author("Kamal Marhubi <kamal@marhubi.com>")
                      .setting(AppSettings::TrailingVarArg)
                      .arg(Arg::with_name("toolchain")
                               .long("tmp-toolchain")
                               .takes_value(true)
                               .value_name("TOOLCHAIN")
                               .required(true))
                      .arg(Arg::with_name("good")
                               .long("good")
                               .takes_value(true)
                               .value_name("TOOLCHAIN"))
                      .arg(Arg::with_name("bad")
                               .long("bad")
                               .takes_value(true)
                               .value_name("TOOLCHAIN"))
                      .arg(Arg::with_name("COMMAND")
                               .multiple(true)
                               .required(true))
                      .get_matches();

    let cfg = try!(set_globals());

    let cmd: Vec<_> = matches.values_of_os("COMMAND").expect("COMMAND").collect();
    let cmd = Cmd::from(&cmd[..]);

    let toolchain = try!(cfg.get_toolchain(matches.value_of("toolchain").expect("toolchain"),
                                           false));
    try!(toolchain.install_from_dist_if_not_installed());

    println!("{}", try!(cmd.succeeds_with(&toolchain)));

    Ok(())
}

fn main() {
    run_rust_bisect().expect("something went wrong");
}
<|endoftext|>


<commit_before>#![feature(globs)]

extern crate graphics;
extern crate piston;
extern crate rand;
extern crate native;
extern crate sdl2_game_window;
extern crate opengl_graphics;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use graphics::*;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
    Render,
    Update,
    Input,
};
use piston::input;

mod snakeapp;
mod object;
mod settings;
mod text;
mod bar;
mod player;
mod snake;
mod action;
mod game_state;
mod character;
mod air_bottle;

fn main() {
    use snakeapp::SnakeApp;
    let mut game_window = WindowSDL2::new(
        piston::shader_version::opengl::OpenGL_3_2,
        WindowSettings {
            title: "Sea Snake Escape".to_string(),
            size: [512, 512],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut app = SnakeApp::new();
    app.load();

    let mut event_iterator = EventIterator::new(&mut game_window,
        &EventSettings {
            updates_per_second: 120,
            max_frames_per_second: 60
        });
    let ref mut gl = Gl::new();
    for e in event_iterator {
        match e {
            Render(args) => {
                gl.viewport(0, 0, args.width as i32, args.height as i32);
                let c = graphics::Context::abs(args.width as f64, args.height as f64);
                c.color(settings::WATER_COLOR).draw(gl);
                app.render(&c, gl);
            },
            Update(args) => {
                app.update(args.dt);
            },
            Input(input::Press(input::Keyboard(key))) => {
                app.key_press(key);
            },
            Input(input::Release(input::Keyboard(key))) => {
                app.key_release(key);
            },
            _ => {},
        }
    }
}

<commit_msg>Upgraded to latest Piston<commit_after>#![feature(globs)]

extern crate graphics;
extern crate piston;
extern crate rand;
extern crate native;
extern crate sdl2_game_window;
extern crate opengl_graphics;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use graphics::*;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
    Render,
    Update,
    Input,
};
use piston::input;

mod snakeapp;
mod object;
mod settings;
mod text;
mod bar;
mod player;
mod snake;
mod action;
mod game_state;
mod character;
mod air_bottle;

fn main() {
    use snakeapp::SnakeApp;

    let opengl = piston::shader_version::opengl::OpenGL_3_2;
    let mut game_window = WindowSDL2::new(
        opengl,
        WindowSettings {
            title: "Sea Snake Escape".to_string(),
            size: [512, 512],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut app = SnakeApp::new();
    app.load();

    let mut event_iterator = EventIterator::new(&mut game_window,
        &EventSettings {
            updates_per_second: 120,
            max_frames_per_second: 60
        });
    let ref mut gl = Gl::new(opengl);
    for e in event_iterator {
        match e {
            Render(args) => {
                gl.viewport(0, 0, args.width as i32, args.height as i32);
                let c = graphics::Context::abs(args.width as f64, args.height as f64);
                c.color(settings::WATER_COLOR).draw(gl);
                app.render(&c, gl);
            },
            Update(args) => {
                app.update(args.dt);
            },
            Input(input::Press(input::Keyboard(key))) => {
                app.key_press(key);
            },
            Input(input::Release(input::Keyboard(key))) => {
                app.key_release(key);
            },
            _ => {},
        }
    }
}

<|endoftext|>


<commit_before>extern crate serde_json;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate clap;
extern crate colored;

extern crate rustfix;

use std::fs::File;
use std::io::{Read, Write};
use std::error::Error;
use std::process::Command;

use colored::Colorize;
use clap::{Arg, App};

use rustfix::Suggestion;
use rustfix::diagnostics::Diagnostic;

const USER_OPTIONS: &'static str = "What do you want to do? \
    [r]eplace | [s]kip | save and [q]uit | [a]bort (without saving)";

fn main() {
    let program = try_main();
    match program {
        Ok(_) => std::process::exit(0),
        Err(ProgramError::UserAbort) => {
            writeln!(std::io::stdout(), "{}", ProgramError::UserAbort).unwrap();
            std::process::exit(0);
        }
        Err(error) => {
            writeln!(std::io::stderr(), "An error occured: {}", error).unwrap();
            writeln!(std::io::stderr(), "{:?}", error).unwrap();
            if let Some(cause) = error.cause() {
                writeln!(std::io::stderr(), "Cause: {:?}", cause).unwrap();
            }
            std::process::exit(1);
        }
    }
}

macro_rules! flush {
    () => (try!(std::io::stdout().flush());)
}

fn try_main() -> Result<(), ProgramError> {
    let matches = App::new("rustfix")
        .about("Automatically apply suggestions made by rustc")
        .version(crate_version!())
        .arg(Arg::with_name("clippy")
            .long("clippy")
            .help("Use `cargo clippy` for suggestions"))
        .arg(Arg::with_name("from_file")
            .long("from-file")
            .value_name("FILE")
            .takes_value(true)
            .help("Read suggestions from file (each line is a JSON object)"))
        .get_matches();

    // Get JSON output from rustc...
    let json = if let Some(file_name) = matches.value_of("from_file") {
        // either by reading a file the user saved (probably only for debugging rustfix itself)...
        try!(json_from_file(&file_name))
    } else {
        // or by spawning a subcommand that runs rustc.
        let subcommand = if matches.is_present("clippy") {
            "clippy"
        } else {
            "rustc"
        };
        try!(json_from_subcommand(subcommand))
    };

    let suggestions: Vec<Suggestion> = json.lines()
        .filter(not_empty)
        // Convert JSON string (and eat parsing errors)
        .flat_map(|line| serde_json::from_str::<Diagnostic>(line))
        // One diagnostic line might have multiple suggestions
        .flat_map(|diagnostic| rustfix::collect_suggestions(&diagnostic, None))
        .collect();

    try!(handle_suggestions(&suggestions));

    Ok(())
}

fn json_from_file(file_name: &str) -> Result<String, ProgramError> {
    read_file_to_string(file_name).map_err(From::from)
}

fn json_from_subcommand(subcommand: &str) -> Result<String, ProgramError> {
    let output = try!(Command::new("cargo")
        .args(&[subcommand,
                "--quiet",
                "--color", "never",
                "--",
                "-Z", "unstable-options",
                "--error-format", "json"])
        .output());

    let content = try!(String::from_utf8(output.stderr));

    if !output.status.success() {
        return Err(ProgramError::SubcommandError(format!("cargo {}", subcommand), content));
    }

    Ok(content)
}

fn handle_suggestions(suggestions: &[Suggestion]) -> Result<(), ProgramError> {
    let mut accepted_suggestions: Vec<&Suggestion> = vec![];

    if suggestions.is_empty() {
        println!("I don't have any suggestions for you right now. Check back later!");
        return Ok(());
    }

    'suggestions: for suggestion in suggestions {
        println!("\n\n{info}: {message}\n\
            {arrow} {file}:{range}\n\
            {suggestion}\n\n\
            {text}\n\n\
            {with}\n\n\
            {replacement}\n",
            info = "Info".green().bold(),
            message = split_at_lint_name(&suggestion.message),
            arrow = "  -->".blue().bold(),
            suggestion = "Suggestion - Replace:".yellow().bold(),
            file = suggestion.file_name,
            range = suggestion.line_range,
            text = indent(4, &reset_indent(&suggestion.text)),
            with = "with:".yellow().bold(),
            replacement = indent(4, &suggestion.replacement));

        'userinput: loop {
            print!("{arrow} {user_options}\n\
                {prompt} ",
                arrow = "==>".green().bold(),
                prompt = "  >".green().bold(),
                user_options = USER_OPTIONS.green());

            flush!();
            let mut input = String::new();
            try!(std::io::stdin().read_line(&mut input));

            match input.trim() {
                "s" => {
                    println!("Skipped.");
                    continue 'suggestions;
                }
                "r" => {
                    accepted_suggestions.push(suggestion);
                    println!("Suggestion accepted. I'll remember that and apply it later.");
                    continue 'suggestions;
                }
                "q" => {
                    println!("Thanks for playing!");
                    break 'suggestions;
                }
                "a" => {
                    return Err(ProgramError::UserAbort);
                }
                _ => {
                    println!("{error}: I didn't quite get that. {user_options}",
                                error = "Error".red().bold(),
                                user_options = USER_OPTIONS);
                    continue 'userinput;
                }
            }
        }
    }

    if !accepted_suggestions.is_empty() {
        println!("Good work. Let me just apply these {} changes!",
                 accepted_suggestions.len());

        for suggestion in accepted_suggestions.iter().rev() {
            try!(apply_suggestion(suggestion));

            print!(".");
            flush!();
        }

        println!("\nDone.");
    }

    Ok(())
}

quick_error! {
    /// All possible errors in programm lifecycle
    #[derive(Debug)]
    pub enum ProgramError {
        UserAbort {
            display("Let's get outta here!")
        }
        /// Missing File
        NoFile {
            display("No input file given")
        }
        SubcommandError(subcommand: String, output: String) {
            display("Error executing subcommand `{}`", subcommand)
            description(output)
        }
        /// Error while dealing with file or stdin/stdout
        Io(err: std::io::Error) {
            from()
            cause(err)
            display("I/O error")
            description(err.description())
        }
        Utf8Error(err: std::string::FromUtf8Error) {
            from()
            display("Error reading input as UTF-8")
        }
        /// Error with deserialization
        Serde(err: serde_json::Error) {
            from()
            cause(err)
            display("Serde JSON error")
            description(err.description())
        }
    }
}

// Helpers
// -------

fn read_file_to_string(file_name: &str) -> Result<String, std::io::Error> {
    let mut file = try!(File::open(file_name));
    let mut buffer = String::new();
    try!(file.read_to_string(&mut buffer));
    Ok(buffer)
}

fn not_empty(s: &&str) -> bool {
    !s.trim().is_empty()
}

fn split_at_lint_name(s: &str) -> String {
    s.split(", #[")
        .collect::<Vec<_>>()
        .join("\n      #[") // Length of whitespace == length of "Info: "
}

fn reset_indent(s: &str) -> String {
    let leading_whitespace = s.lines()
        .nth(0)
        .unwrap_or("")
        .chars()
        .take_while(|&c| char::is_whitespace(c))
        .count();

    s.lines()
        .map(|line| String::from(&line[leading_whitespace..]))
        .collect::<Vec<_>>()
        .join("\n")
}

fn indent(size: u32, s: &str) -> String {
    let whitespace: String = std::iter::repeat(' ').take(size as usize).collect();

    s.lines()
        .map(|l| format!("{}{}", whitespace, l))
        .collect::<Vec<_>>()
        .join("\n")
}

/// Apply suggestion to a file
///
/// Please beware of ugly hacks below! Originally, I wanted to replace byte ranges, but sadly the
/// ranges rustc's JSON output gives me do not correspond to the parts of the file they are meant
/// to correspond to. So, for now, let's just replace lines!
///
/// This function is as stupid as possible. Make sure you call for the replacemnts in one file in
/// reverse order to not mess up the lines for replacements further down the road.
fn apply_suggestion(suggestion: &Suggestion) -> Result<(), ProgramError> {
    use std::cmp::max;

    let file_content = try!(read_file_to_string(&suggestion.file_name));
    let mut new_content = String::new();

    // Add the lines before the section we want to replace
    new_content.push_str(&file_content.lines()
        .take(max(suggestion.line_range.start.line - 1, 0) as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    // TODO(killercup): Replace sections of lines only
    new_content.push_str(&indent((suggestion.line_range.start.column - 1) as u32,
                                suggestion.replacement.trim()));

    if suggestion.text.trim().ends_with(';') && !suggestion.replacement.trim().ends_with(';') {
        new_content.push_str(";");
    }

    // Add the lines after the section we want to replace
    new_content.push_str("\n");
    new_content.push_str(&file_content.lines()
        .skip(suggestion.line_range.end.line as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    let mut file = try!(File::create(&suggestion.file_name));
    let new_content = new_content.as_bytes();

    try!(file.set_len(new_content.len() as u64));
    try!(file.write_all(&new_content));

    Ok(())
}
<commit_msg>First try at replacing partials lines<commit_after>extern crate serde_json;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate clap;
extern crate colored;

extern crate rustfix;

use std::fs::File;
use std::io::{Read, Write};
use std::error::Error;
use std::process::Command;

use colored::Colorize;
use clap::{Arg, App};

use rustfix::Suggestion;
use rustfix::diagnostics::Diagnostic;

const USER_OPTIONS: &'static str = "What do you want to do? \
    [r]eplace | [s]kip | save and [q]uit | [a]bort (without saving)";

fn main() {
    let program = try_main();
    match program {
        Ok(_) => std::process::exit(0),
        Err(ProgramError::UserAbort) => {
            writeln!(std::io::stdout(), "{}", ProgramError::UserAbort).unwrap();
            std::process::exit(0);
        }
        Err(error) => {
            writeln!(std::io::stderr(), "An error occured: {}", error).unwrap();
            writeln!(std::io::stderr(), "{:?}", error).unwrap();
            if let Some(cause) = error.cause() {
                writeln!(std::io::stderr(), "Cause: {:?}", cause).unwrap();
            }
            std::process::exit(1);
        }
    }
}

macro_rules! flush {
    () => (try!(std::io::stdout().flush());)
}

fn try_main() -> Result<(), ProgramError> {
    let matches = App::new("rustfix")
        .about("Automatically apply suggestions made by rustc")
        .version(crate_version!())
        .arg(Arg::with_name("clippy")
            .long("clippy")
            .help("Use `cargo clippy` for suggestions"))
        .arg(Arg::with_name("from_file")
            .long("from-file")
            .value_name("FILE")
            .takes_value(true)
            .help("Read suggestions from file (each line is a JSON object)"))
        .get_matches();

    // Get JSON output from rustc...
    let json = if let Some(file_name) = matches.value_of("from_file") {
        // either by reading a file the user saved (probably only for debugging rustfix itself)...
        try!(json_from_file(&file_name))
    } else {
        // or by spawning a subcommand that runs rustc.
        let subcommand = if matches.is_present("clippy") {
            "clippy"
        } else {
            "rustc"
        };
        try!(json_from_subcommand(subcommand))
    };

    let suggestions: Vec<Suggestion> = json.lines()
        .filter(not_empty)
        // Convert JSON string (and eat parsing errors)
        .flat_map(|line| serde_json::from_str::<Diagnostic>(line))
        // One diagnostic line might have multiple suggestions
        .flat_map(|diagnostic| rustfix::collect_suggestions(&diagnostic, None))
        .collect();

    try!(handle_suggestions(&suggestions));

    Ok(())
}

fn json_from_file(file_name: &str) -> Result<String, ProgramError> {
    read_file_to_string(file_name).map_err(From::from)
}

fn json_from_subcommand(subcommand: &str) -> Result<String, ProgramError> {
    let output = try!(Command::new("cargo")
        .args(&[subcommand,
                "--quiet",
                "--color", "never",
                "--",
                "-Z", "unstable-options",
                "--error-format", "json"])
        .output());

    let content = try!(String::from_utf8(output.stderr));

    if !output.status.success() {
        return Err(ProgramError::SubcommandError(format!("cargo {}", subcommand), content));
    }

    Ok(content)
}

fn handle_suggestions(suggestions: &[Suggestion]) -> Result<(), ProgramError> {
    let mut accepted_suggestions: Vec<&Suggestion> = vec![];

    if suggestions.is_empty() {
        println!("I don't have any suggestions for you right now. Check back later!");
        return Ok(());
    }

    'suggestions: for suggestion in suggestions {
        println!("\n\n{info}: {message}\n\
            {arrow} {file}:{range}\n\
            {suggestion}\n\n\
            {text}\n\n\
            {with}\n\n\
            {replacement}\n",
            info = "Info".green().bold(),
            message = split_at_lint_name(&suggestion.message),
            arrow = "  -->".blue().bold(),
            suggestion = "Suggestion - Replace:".yellow().bold(),
            file = suggestion.file_name,
            range = suggestion.line_range,
            text = indent(4, &reset_indent(&suggestion.text)),
            with = "with:".yellow().bold(),
            replacement = indent(4, &suggestion.replacement));

        'userinput: loop {
            print!("{arrow} {user_options}\n\
                {prompt} ",
                arrow = "==>".green().bold(),
                prompt = "  >".green().bold(),
                user_options = USER_OPTIONS.green());

            flush!();
            let mut input = String::new();
            try!(std::io::stdin().read_line(&mut input));

            match input.trim() {
                "s" => {
                    println!("Skipped.");
                    continue 'suggestions;
                }
                "r" => {
                    accepted_suggestions.push(suggestion);
                    println!("Suggestion accepted. I'll remember that and apply it later.");
                    continue 'suggestions;
                }
                "q" => {
                    println!("Thanks for playing!");
                    break 'suggestions;
                }
                "a" => {
                    return Err(ProgramError::UserAbort);
                }
                _ => {
                    println!("{error}: I didn't quite get that. {user_options}",
                                error = "Error".red().bold(),
                                user_options = USER_OPTIONS);
                    continue 'userinput;
                }
            }
        }
    }

    if !accepted_suggestions.is_empty() {
        println!("Good work. Let me just apply these {} changes!",
                 accepted_suggestions.len());

        for suggestion in accepted_suggestions.iter().rev() {
            try!(apply_suggestion(suggestion));

            print!(".");
            flush!();
        }

        println!("\nDone.");
    }

    Ok(())
}

quick_error! {
    /// All possible errors in programm lifecycle
    #[derive(Debug)]
    pub enum ProgramError {
        UserAbort {
            display("Let's get outta here!")
        }
        /// Missing File
        NoFile {
            display("No input file given")
        }
        SubcommandError(subcommand: String, output: String) {
            display("Error executing subcommand `{}`", subcommand)
            description(output)
        }
        /// Error while dealing with file or stdin/stdout
        Io(err: std::io::Error) {
            from()
            cause(err)
            display("I/O error")
            description(err.description())
        }
        Utf8Error(err: std::string::FromUtf8Error) {
            from()
            display("Error reading input as UTF-8")
        }
        /// Error with deserialization
        Serde(err: serde_json::Error) {
            from()
            cause(err)
            display("Serde JSON error")
            description(err.description())
        }
    }
}

// Helpers
// -------

fn read_file_to_string(file_name: &str) -> Result<String, std::io::Error> {
    let mut file = try!(File::open(file_name));
    let mut buffer = String::new();
    try!(file.read_to_string(&mut buffer));
    Ok(buffer)
}

fn not_empty(s: &&str) -> bool {
    !s.trim().is_empty()
}

fn split_at_lint_name(s: &str) -> String {
    s.split(", #[")
        .collect::<Vec<_>>()
        .join("\n      #[") // Length of whitespace == length of "Info: "
}

fn reset_indent(s: &str) -> String {
    let leading_whitespace = s.lines()
        .nth(0)
        .unwrap_or("")
        .chars()
        .take_while(|&c| char::is_whitespace(c))
        .count();

    s.lines()
        .map(|line| String::from(&line[leading_whitespace..]))
        .collect::<Vec<_>>()
        .join("\n")
}

fn indent(size: u32, s: &str) -> String {
    let whitespace: String = std::iter::repeat(' ').take(size as usize).collect();

    s.lines()
        .map(|l| format!("{}{}", whitespace, l))
        .collect::<Vec<_>>()
        .join("\n")
}

/// Apply suggestion to a file
///
/// Please beware of ugly hacks below! Originally, I wanted to replace byte ranges, but sadly the
/// ranges rustc's JSON output gives me do not correspond to the parts of the file they are meant
/// to correspond to. So, for now, let's just replace lines!
///
/// This function is as stupid as possible. Make sure you call for the replacemnts in one file in
/// reverse order to not mess up the lines for replacements further down the road.
fn apply_suggestion(suggestion: &Suggestion) -> Result<(), ProgramError> {
    use std::cmp::max;

    let file_content = try!(read_file_to_string(&suggestion.file_name));
    let mut new_content = String::new();

    // Add the lines before the section we want to replace
    new_content.push_str(&file_content.lines()
        .take(max(suggestion.line_range.start.line - 1, 0) as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    // Parts of line before replacement
    new_content.push_str(&file_content.lines()
        .nth(suggestion.line_range.start.line - 1)
        .unwrap_or("")
        .chars()
        .take(suggestion.line_range.start.column - 1)
        .collect::<String>());

    // Insert new content! Finally!
    new_content.push_str(&suggestion.replacement);

    // TODO(killercup): better handling of trailing semicolons
    if suggestion.text.trim().ends_with(';') && !suggestion.replacement.trim().ends_with(';') {
        new_content.push_str(";");
    }

    // Parts of line after replacement
    new_content.push_str(&file_content.lines()
        .nth(suggestion.line_range.end.line - 1)
        .unwrap_or("")
        .chars()
        .skip(suggestion.line_range.end.column)
        .collect::<String>());

    // Add the lines after the section we want to replace
    new_content.push_str("\n");
    new_content.push_str(&file_content.lines()
        .skip(suggestion.line_range.end.line as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    let mut file = try!(File::create(&suggestion.file_name));
    let new_content = new_content.as_bytes();

    try!(file.set_len(new_content.len() as u64));
    try!(file.write_all(&new_content));

    Ok(())
}
<|endoftext|>


<commit_before>#![desc = "Constraint Satisfaction in Rust"]
#![license = "MIT"]
#![crate_id = "csar#0.2"]
#![crate_type = "lib"]

use std::fmt;
use std::cell::RefCell;
use std::collections::hashmap::HashMap;
use std::rc::{Rc,Weak};

pub struct Mod {
    vars: RefCell<Vec<Rc<FDVar>>>,
    propagators: RefCell<Vec<Rc<Box<Propagator>>>>,
    waiting: RefCell<HashMap<(uint, Event), Vec<uint>>>
}

// cannot use type Model = Rc<Mod> since that would forbid using impl Model
pub struct Model;

/// Representation of finite domains as a list of intervals, maintaining
/// min and max for easy/quick access
#[deriving(Clone)]
struct Dom {
    min: int,
    max: int,
    intervals: Vec<(int, int)>
}

/// Runtime checked mutability with borrowing
#[deriving(Clone)]
struct Domain {
    dom: RefCell<Dom>
}

pub trait Propagator {
    fn propagate(&self) -> Vec<uint>;
    fn id(&self) -> uint;
    fn events(&self) -> Vec<(uint, Event)>;
    fn model(&self) -> Weak<Mod>;

    fn register(&self) {
        for &(var, event) in self.events().iter() {
            self.model().upgrade().unwrap().add_waiting(var, event, self.id());
        }
    }

    fn unregister(&self) {
        for &(var, event) in self.events().iter() {
            self.model().upgrade().unwrap().del_waiting(var, event, self.id());
        }
    }

}

pub struct Var;

#[deriving(Clone)]
pub struct FDVar {
    model: Weak<Mod>,
    id: uint,
    name: String,
    dom: Domain
}

#[deriving(Show,Hash,Eq,PartialEq)]
pub enum Event {
    Min,
    Max,
    Ins
}

#[allow(dead_code)]
impl Model {
    fn new() -> Rc<Mod> {
        Rc::new(Mod {
            vars: RefCell::new(Vec::new()),
            propagators: RefCell::new(Vec::new()),
            waiting: RefCell::new(HashMap::new())
        })
    }
}

impl Mod {
    fn add_var(&self, var: Rc<FDVar>) {
        self.vars.borrow_mut().push(var);
    }

    fn add_prop(&self, prop: Rc<Box<Propagator>>) {
        self.propagators.borrow_mut().push(prop);
    }

    fn add_waiting(&self, var: uint, event: Event, propagator: uint) {
        let mut waiting = self.waiting.borrow_mut();
        if waiting.contains_key(&(var, event)) {
            waiting.get_mut(&(var, event)).push(propagator);
        } else {
            waiting.insert((var, event), vec![propagator]);
        }
    }

    fn del_waiting(&self, var: uint, event: Event, propagator: uint) {
        self.waiting.borrow_mut().get_mut(&(var, event)).remove(propagator);
    }

    fn get_waiting(&self, var: uint, event: Event) -> Vec<uint> {
        let waiting = self.waiting.borrow();
        match waiting.find_copy(&(var, event)) {
            Some(vec) => vec,
            None => Vec::new()
        }
    }
}

#[allow(dead_code)]
impl Domain {
    /// Domain created with initial bounds
    fn new(min: int, max: int) -> Domain {
        Domain {
            dom: RefCell::new(Dom {
                     min: min,
                     max: max,
                     intervals: vec![(min, max)]
                 })
        }
    }

    fn set_min(&self, min: int) {
        let mut dom = self.dom.borrow_mut();
        if min < dom.min { return; }
        if min > dom.max { return; } // TODO failure via conditions
        loop {
            match dom.intervals.get(0) {
                // note that the breaks are for the loop, not the matching
                &(x, _) if min < x => { dom.min = x; break; },
                &(_, y) if min > y => { dom.intervals.shift(); },
                &(_, y) => {
                    dom.min = min;
                    *dom.intervals.get_mut(0) = (min, y);
                    break;
                }
            }
        }
    }

    fn get_min(&self) -> int {
        self.dom.borrow().min
    }

    fn set_max(&self, max: int) {
        let mut dom = self.dom.borrow_mut();
        if max > dom.max { return; }
        if max < dom.min { return; } // TODO failure via conditions
        loop {
            match dom.intervals.last().unwrap() {
                &(_, y) if max > y => { dom.max = y; break; },
                &(x, _) if max < x => { dom.intervals.pop(); },
                &(x, _) => {
                    dom.max = max;
                    *dom.intervals.mut_last().unwrap() = (x, max);
                    break
                }
            }
        }
    }

    fn get_max(&self) -> int {
        self.dom.borrow().max
    }

    fn remove(&self, val: int) {
        let mut dom = self.dom.borrow_mut();
        if val > dom.max || val < dom.min { return; }
        let mut down = 0;
        let mut up = dom.intervals.len();
        let mut test;
        loop {
            test = down + (up - down) / 2;
            match dom.intervals.get(test) {
                &(x, _) if val < x => {
                    if test > down {
                        up = test;
                    } else {
                        break;
                    }
                },
                &(_, y) if val > y => {
                    if test < up - 1 {
                        down = test + 1;
                    } else {
                        break;
                    }
                },
                &(x, y) if val == x && val == y => {
                    dom.intervals.remove(test);
                    break;
                },
                &(x, y) if val == x => {
                    *dom.intervals.get_mut(test) = (x + 1, y);
                    break;
                },
                &(x, y) if val == y => {
                    *dom.intervals.get_mut(test) = (x, y - 1);
                    break;
                },
                &(x, y) => {
                    *dom.intervals.get_mut(test) = (x, val - 1);
                    dom.intervals.insert(test + 1, (val + 1, y));
                    break;
                }
            }
        }
        if test == 0 {
            match dom.intervals.get(test) {
                &(x, _) => dom.min = x
            }
        } else if test == dom.intervals.len() - 1 {
            match dom.intervals.get(test) {
                &(_, y) => dom.max = y
            }
        }
    }
}

impl fmt::Show for Domain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let dom = self.dom.borrow();
        let mut s = format!("({}, {}) [", dom.min, dom.max);
        for &(min, max) in dom.intervals.iter() {
            s = s + min.to_str() + ".." + max.to_str() + ", ";
        }
        return write!(f, "{}]", s);
    }
}

impl Var {
    pub fn new(model: Rc<Mod>, min: int, max: int, name: &str) -> Rc<FDVar> {
        assert!(min <= max);
        let id = model.vars.borrow().len();
        let v = Rc::new(FDVar {
            model: model.downgrade(),
            id: id,
            name: name.to_string(),
            dom: Domain::new(min, max)
        });
        model.add_var(v.clone());
        v
    }
}

#[allow(dead_code)]
impl FDVar {
    pub fn min(&self) -> int {
        self.dom.get_min()
    }

    pub fn max(&self) -> int {
        self.dom.get_max()
    }

    fn set_min(&self, v: int) -> Vec<uint> {
        if v > self.min() {
            self.dom.set_min(v);
            if self.is_instanciated() {
                self.model.upgrade().unwrap().get_waiting(self.id, Min)
                    // + self.waitingOnIns
            } else {
                self.model.upgrade().unwrap().get_waiting(self.id, Min)
            }
        } else {
            vec![]
        }
    }

    fn set_max(&self, v: int) -> Vec<uint> {
        if v < self.max() {
            self.dom.set_max(v);
            if self.is_instanciated() {
                // self.waitingOnMax + self.waitingOnIns
                self.model.upgrade().unwrap().get_waiting(self.id, Max)
            } else {
                self.model.upgrade().unwrap().get_waiting(self.id, Max)
            }
        } else {
            vec![]
        }
    }

    fn is_instanciated(&self) -> bool {
        self.min() == self.max()
    }
}

impl fmt::Show for FDVar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} ({})", self.name, self.dom)
    }
}

pub struct LtXYx {
    model: Weak<Mod>,
    id: uint,
    x: uint,
    y: uint
}

impl LtXYx {
    pub fn new(model: Rc<Mod>, x: Rc<FDVar>, y: Rc<FDVar>) -> Rc<Box<Propagator>> {
        let id = model.propagators.borrow().len();
        let this = LtXYx { model: model.downgrade(), id: id, x: x.id, y: y.id };
        this.register();
        this.propagate();
        let p = Rc::new((box this) as Box<Propagator>);
        model.add_prop(p.clone());
        p
    }
}

impl Propagator for LtXYx {
    fn id(&self) -> uint {
        self.id
    }
    fn model(&self) -> Weak<Mod> {
        self.model.clone()
    }

    fn events(&self) -> Vec<(uint, Event)> {
        vec![(self.y, Max)]
    }

    fn propagate(&self) -> Vec<uint> {
        let model = self.model.upgrade().unwrap();
        let mut vars = model.vars.borrow_mut();
        if vars.get(self.x).max() < vars.get(self.y).min() {
            // entailed
            self.unregister();
            vec![]
        } else if vars.get(self.x).max() > vars.get(self.y).max() - 1 {
            //if y.is_instanciated() {
            //   self.unregister();
            //}
            let max = vars.get(self.y).max() - 1;
            vars.get(self.x).set_max(max)
        } else {
            vec![]
        }
    }
}

pub struct LtXYy {
    model: Weak<Mod>,
    id: uint,
    x: uint,
    y: uint
}

impl LtXYy {
    pub fn new(model: Rc<Mod>, x: Rc<FDVar>, y: Rc<FDVar>) -> Rc<Box<Propagator>> {
        let id = model.propagators.borrow().len();
        let this = LtXYy { model: model.downgrade(), id: id, x: x.id, y: y.id };
        this.register();
        this.propagate();
        let p = Rc::new((box this) as Box<Propagator>);
        model.add_prop(p.clone());
        p
    }
}

impl Propagator for LtXYy {
    fn id(&self) -> uint {
        self.id
    }
    fn model(&self) -> Weak<Mod> {
        self.model.clone()
    }

    fn events(&self) -> Vec<(uint, Event)> {
        vec![(self.x, Min)]
    }

    fn propagate(&self) -> Vec<uint> {
        let model = self.model.upgrade().unwrap();
        let mut vars = model.vars.borrow_mut();
        if vars.get(self.x).max() < vars.get(self.y).min() {
            // entailed
            self.unregister();
            vec![]
        } else if vars.get(self.y).min() < vars.get(self.x).min() + 1 {
            //if y.is_instanciated() {
            //   self.unregister();
            //}
            let min = vars.get(self.x).min() + 1;
            vars.get(self.y).set_min(min)
        } else {
            vec![]
        }
    }
}

#[cfg(test)]
mod tests;
<commit_msg>struct inheritance<commit_after>#![desc = "Constraint Satisfaction in Rust"]
#![license = "MIT"]
#![crate_id = "csar#0.2"]
#![crate_type = "lib"]

#![feature(struct_inherit)]

use std::fmt;
use std::cell::RefCell;
use std::collections::hashmap::HashMap;
use std::rc::{Rc,Weak};

pub struct Mod {
    vars: RefCell<Vec<Rc<FDVar>>>,
    propagators: RefCell<Vec<Rc<Box<Propagator>>>>,
    waiting: RefCell<HashMap<(uint, Event), Vec<uint>>>
}

// cannot use type Model = Rc<Mod> since that would forbid using impl Model
pub struct Model;

/// Representation of finite domains as a list of intervals, maintaining
/// min and max for easy/quick access
#[deriving(Clone)]
struct Dom {
    min: int,
    max: int,
    intervals: Vec<(int, int)>
}

/// Runtime checked mutability with borrowing
#[deriving(Clone)]
struct Domain {
    dom: RefCell<Dom>
}

trait Propagator {
    fn id(&self) -> uint;
    fn model(&self) -> Weak<Mod>;

    fn events(&self) -> Vec<(uint, Event)>;
    fn propagate(&self) -> Vec<uint>;

    fn register(&self) {
        for &(var, event) in self.events().iter() {
            self.model().upgrade().unwrap().add_waiting(var, event, self.id());
        }
    }

    fn unregister(&self) {
        for &(var, event) in self.events().iter() {
            self.model().upgrade().unwrap().del_waiting(var, event, self.id());
        }
    }
}

pub virtual struct Prop {
    id: uint,
    model: Weak<Mod>
}

pub struct Var;

#[deriving(Clone)]
pub struct FDVar {
    model: Weak<Mod>,
    id: uint,
    name: String,
    dom: Domain
}

#[deriving(Show,Hash,Eq,PartialEq)]
pub enum Event {
    Min,
    Max,
    Ins
}

#[allow(dead_code)]
impl Model {
    fn new() -> Rc<Mod> {
        Rc::new(Mod {
            vars: RefCell::new(Vec::new()),
            propagators: RefCell::new(Vec::new()),
            waiting: RefCell::new(HashMap::new())
        })
    }
}

impl Mod {
    fn add_var(&self, var: Rc<FDVar>) {
        self.vars.borrow_mut().push(var);
    }

    fn add_prop(&self, prop: Rc<Box<Propagator>>) {
        self.propagators.borrow_mut().push(prop);
    }

    fn add_waiting(&self, var: uint, event: Event, propagator: uint) {
        let mut waiting = self.waiting.borrow_mut();
        if waiting.contains_key(&(var, event)) {
            waiting.get_mut(&(var, event)).push(propagator);
        } else {
            waiting.insert((var, event), vec![propagator]);
        }
    }

    fn del_waiting(&self, var: uint, event: Event, propagator: uint) {
        self.waiting.borrow_mut().get_mut(&(var, event)).remove(propagator);
    }

    fn get_waiting(&self, var: uint, event: Event) -> Vec<uint> {
        let waiting = self.waiting.borrow();
        match waiting.find_copy(&(var, event)) {
            Some(vec) => vec,
            None => Vec::new()
        }
    }
}

#[allow(dead_code)]
impl Domain {
    /// Domain created with initial bounds
    fn new(min: int, max: int) -> Domain {
        Domain {
            dom: RefCell::new(Dom {
                     min: min,
                     max: max,
                     intervals: vec![(min, max)]
                 })
        }
    }

    fn set_min(&self, min: int) {
        let mut dom = self.dom.borrow_mut();
        if min < dom.min { return; }
        if min > dom.max { return; } // TODO failure via conditions
        loop {
            match dom.intervals.get(0) {
                // note that the breaks are for the loop, not the matching
                &(x, _) if min < x => { dom.min = x; break; },
                &(_, y) if min > y => { dom.intervals.shift(); },
                &(_, y) => {
                    dom.min = min;
                    *dom.intervals.get_mut(0) = (min, y);
                    break;
                }
            }
        }
    }

    fn get_min(&self) -> int {
        self.dom.borrow().min
    }

    fn set_max(&self, max: int) {
        let mut dom = self.dom.borrow_mut();
        if max > dom.max { return; }
        if max < dom.min { return; } // TODO failure via conditions
        loop {
            match dom.intervals.last().unwrap() {
                &(_, y) if max > y => { dom.max = y; break; },
                &(x, _) if max < x => { dom.intervals.pop(); },
                &(x, _) => {
                    dom.max = max;
                    *dom.intervals.mut_last().unwrap() = (x, max);
                    break
                }
            }
        }
    }

    fn get_max(&self) -> int {
        self.dom.borrow().max
    }

    fn remove(&self, val: int) {
        let mut dom = self.dom.borrow_mut();
        if val > dom.max || val < dom.min { return; }
        let mut down = 0;
        let mut up = dom.intervals.len();
        let mut test;
        loop {
            test = down + (up - down) / 2;
            match dom.intervals.get(test) {
                &(x, _) if val < x => {
                    if test > down {
                        up = test;
                    } else {
                        break;
                    }
                },
                &(_, y) if val > y => {
                    if test < up - 1 {
                        down = test + 1;
                    } else {
                        break;
                    }
                },
                &(x, y) if val == x && val == y => {
                    dom.intervals.remove(test);
                    break;
                },
                &(x, y) if val == x => {
                    *dom.intervals.get_mut(test) = (x + 1, y);
                    break;
                },
                &(x, y) if val == y => {
                    *dom.intervals.get_mut(test) = (x, y - 1);
                    break;
                },
                &(x, y) => {
                    *dom.intervals.get_mut(test) = (x, val - 1);
                    dom.intervals.insert(test + 1, (val + 1, y));
                    break;
                }
            }
        }
        if test == 0 {
            match dom.intervals.get(test) {
                &(x, _) => dom.min = x
            }
        } else if test == dom.intervals.len() - 1 {
            match dom.intervals.get(test) {
                &(_, y) => dom.max = y
            }
        }
    }
}

impl fmt::Show for Domain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let dom = self.dom.borrow();
        let mut s = format!("({}, {}) [", dom.min, dom.max);
        for &(min, max) in dom.intervals.iter() {
            s = s + min.to_str() + ".." + max.to_str() + ", ";
        }
        return write!(f, "{}]", s);
    }
}

impl Var {
    pub fn new(model: Rc<Mod>, min: int, max: int, name: &str) -> Rc<FDVar> {
        assert!(min <= max);
        let id = model.vars.borrow().len();
        let v = Rc::new(FDVar {
            model: model.downgrade(),
            id: id,
            name: name.to_string(),
            dom: Domain::new(min, max)
        });
        model.add_var(v.clone());
        v
    }
}

impl FDVar {
    pub fn min(&self) -> int {
        self.dom.get_min()
    }

    pub fn max(&self) -> int {
        self.dom.get_max()
    }

    fn set_min(&self, v: int) -> Vec<uint> {
        if v > self.min() {
            self.dom.set_min(v);
            if self.is_instanciated() {
                self.model.upgrade().unwrap().get_waiting(self.id, Min)
                    // + self.waitingOnIns
            } else {
                self.model.upgrade().unwrap().get_waiting(self.id, Min)
            }
        } else {
            vec![]
        }
    }

    fn set_max(&self, v: int) -> Vec<uint> {
        if v < self.max() {
            self.dom.set_max(v);
            if self.is_instanciated() {
                // self.waitingOnMax + self.waitingOnIns
                self.model.upgrade().unwrap().get_waiting(self.id, Max)
            } else {
                self.model.upgrade().unwrap().get_waiting(self.id, Max)
            }
        } else {
            vec![]
        }
    }

    fn is_instanciated(&self) -> bool {
        self.min() == self.max()
    }
}

impl fmt::Show for FDVar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} ({})", self.name, self.dom)
    }
}

pub struct LtXYx : Prop {
    x: uint,
    y: uint
}

impl LtXYx {
    pub fn new(model: Rc<Mod>, x: Rc<FDVar>, y: Rc<FDVar>) -> Rc<Box<Propagator>> {
        let id = model.propagators.borrow().len();
        let this = LtXYx { model: model.downgrade(), id: id, x: x.id, y: y.id };
        this.register();
        this.propagate();
        let p = Rc::new((box this) as Box<Propagator>);
        model.add_prop(p.clone());
        p
    }
}

impl Propagator for LtXYx {
    fn id(&self) -> uint {
        self.id
    }
    fn model(&self) -> Weak<Mod> {
        self.model.clone()
    }

    fn events(&self) -> Vec<(uint, Event)> {
        vec![(self.y, Max)]
    }

    fn propagate(&self) -> Vec<uint> {
        let model = self.model.upgrade().unwrap();
        let mut vars = model.vars.borrow_mut();
        if vars.get(self.x).max() < vars.get(self.y).min() {
            // entailed
            self.unregister();
            vec![]
        } else if vars.get(self.x).max() > vars.get(self.y).max() - 1 {
            //if y.is_instanciated() {
            //   self.unregister();
            //}
            let max = vars.get(self.y).max() - 1;
            vars.get(self.x).set_max(max)
        } else {
            vec![]
        }
    }
}

pub struct LtXYy : Prop {
    x: uint,
    y: uint
}

impl LtXYy {
    pub fn new(model: Rc<Mod>, x: Rc<FDVar>, y: Rc<FDVar>) -> Rc<Box<Propagator>> {
        let id = model.propagators.borrow().len();
        let this = LtXYy { model: model.downgrade(), id: id, x: x.id, y: y.id };
        this.register();
        this.propagate();
        let p = Rc::new((box this) as Box<Propagator>);
        model.add_prop(p.clone());
        p
    }
}

impl Propagator for LtXYy {
    fn id(&self) -> uint {
        self.id
    }
    fn model(&self) -> Weak<Mod> {
        self.model.clone()
    }

    fn events(&self) -> Vec<(uint, Event)> {
        vec![(self.x, Min)]
    }

    fn propagate(&self) -> Vec<uint> {
        let model = self.model.upgrade().unwrap();
        let mut vars = model.vars.borrow_mut();
        if vars.get(self.x).max() < vars.get(self.y).min() {
            // entailed
            self.unregister();
            vec![]
        } else if vars.get(self.y).min() < vars.get(self.x).min() + 1 {
            //if y.is_instanciated() {
            //   self.unregister();
            //}
            let min = vars.get(self.x).min() + 1;
            vars.get(self.y).set_min(min)
        } else {
            vec![]
        }
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>