<commit_before>extern crate chattium_oxide_lib;
extern crate bear_lib_terminal;
extern crate yaml_file_handler;
extern crate hyper;
extern crate clap;
extern crate time;

mod io;
mod options;
mod message_write;
mod response_request;

use std::io::{stderr, Write};
use std::sync::{Arc, RwLock};
use std::thread;
use hyper::client::Client;
use message_write::MessageWriter;
use response_request::ResponseRequester;
use bear_lib_terminal::terminal::{self, config};

pub use options::Options;

fn main() {
	terminal::open("chattium-oxide client", 80, 30);
	terminal::set(config::Window::empty().resizeable(true));
	terminal::refresh();

	let client           = Arc::new(Client::new());
	let options          = Options::parse();
	let continue_threads = Arc::new(RwLock::new(true));

	terminal::set(config::Window::empty().title(format!("chattium-oxide client — Connected to {} as {}", options.server, options.name)));

	let writing_messages = {
		let writing_messages_options = options.clone();
		let writing_messages_client  = client.clone();

		thread::spawn(move || MessageWriter::new(writing_messages_options, writing_messages_client).call())
	};

	let getting_responses = {
		let getting_responses_options = options.clone();
		let getting_responses_client  = client.clone();
		let getting_responses_going   = continue_threads.clone();

		thread::spawn(move || ResponseRequester::new(getting_responses_options, getting_responses_client, getting_responses_going).call())
	};

	while let Some(e) = terminal::wait_event() {
		match e {
			terminal::Event::KeyReleased{key: terminal::KeyCode::Escape, ctrl: _, shift: _} | terminal::Event::Close => break,
			_                                                                                => (),
		}
	}

	println!("Terminating...");
	*continue_threads.write().unwrap() = false;

	if let Err(error) = getting_responses.join() {
		let _ = stderr().write_fmt(format_args!("Response getter thread failed: {:?}\n", error));
	}
	if let Err(error) = writing_messages.join() {
		let _ = stderr().write_fmt(format_args!("Message writer thread failed: {:?}\n", error));
	}

	terminal::close();
}
<commit_msg>Add splash screen<commit_after>extern crate chattium_oxide_lib;
extern crate bear_lib_terminal;
extern crate yaml_file_handler;
extern crate hyper;
extern crate clap;
extern crate time;

mod io;
mod options;
mod message_write;
mod response_request;

use std::io::{stderr, Write};
use std::sync::{Arc, RwLock};
use std::thread;
use hyper::client::Client;
use message_write::MessageWriter;
use response_request::ResponseRequester;
use bear_lib_terminal::terminal::{self, config};

pub use options::Options;

fn main() {
	terminal::open("chattium-oxide client", 80, 30);
	terminal::set(config::Window::empty().resizeable(true));

	terminal::print_xy(0, 0, r#"

    ___           ___                         __________
   |   |         |   |                       /          \
   |   |         |   |                      /  _______   \
   |   |         |   |                     /  /       \   \
   |   |         |   |                    |  /         \   |
   |   |_________|   |                    | |           |  |
   |                 |                    | |           |  |
    \                |                    | |           |  |
     \__________     |                    | |           |  |
                |    |                    | |           |  |
                |    |     _______        | |           |  |
                |    |    /       \       | |           |  |
                |    |   /   ___   \      | |           |  |
                |    |  |   /   \   |     | |           |  |
                |    |  |  |     |  |     | |           |  |
                |    |  |  |     |  ||    | |           |  |
                |    |  |  | |   |  ||    | |           |  |
                |    |  |  | |__/   ||    |  \         /   |
                |    |  |  |        ||    |   \_______/    |
                |    |  |   \______/ |     \              /
                |    |   \          /       \            / __     ___       __
                |____|    \________/         \__________/ /  \   /_  |     /  \
                                                         | /\ |    | |    | /\ |
                                                  _    _ ||  ||    | |    ||  ||
                                                  \\  // | \/ | _  | |  _ | \/ |
                                                   \\//   \__/ |_| |_| |_| \__/
                                                    ‾‾
                                                                                "#);
	terminal::refresh();
	terminal::delay(1000);
	terminal::clear(None);

	let client           = Arc::new(Client::new());
	let options          = Options::parse();
	let continue_threads = Arc::new(RwLock::new(true));

	terminal::set(config::Window::empty().title(format!("chattium-oxide client — Connected to {} as {}", options.server, options.name)));

	let writing_messages = {
		let writing_messages_options = options.clone();
		let writing_messages_client  = client.clone();

		thread::spawn(move || MessageWriter::new(writing_messages_options, writing_messages_client).call())
	};

	let getting_responses = {
		let getting_responses_options = options.clone();
		let getting_responses_client  = client.clone();
		let getting_responses_going   = continue_threads.clone();

		thread::spawn(move || ResponseRequester::new(getting_responses_options, getting_responses_client, getting_responses_going).call())
	};

	while let Some(e) = terminal::wait_event() {
		match e {
			terminal::Event::KeyReleased{key: terminal::KeyCode::Escape, ctrl: _, shift: _} | terminal::Event::Close => break,
			_                                                                                => (),
		}
	}

	println!("Terminating...");
	*continue_threads.write().unwrap() = false;

	if let Err(error) = getting_responses.join() {
		let _ = stderr().write_fmt(format_args!("Response getter thread failed: {:?}\n", error));
	}
	if let Err(error) = writing_messages.join() {
		let _ = stderr().write_fmt(format_args!("Message writer thread failed: {:?}\n", error));
	}

	terminal::close();
}
<|endoftext|>


<commit_before>extern crate bencode;

use std::env;
use bencode::deserialize_file;

fn main () {
    let path = env::args().nth(1).unwrap();
    let cont = deserialize_file(path).unwrap();
    println!("{:?}", cont);
}
<commit_msg>adding ToMetadata traits and related stuff<commit_after>extern crate bencode;

use std::env;
use std::collections::HashMap;
use bencode::{deserialize_file, Bencode};

#[derive(Debug)]
struct Metadata {
    announce: String,
}

trait ToMetadata {
    fn to_metadata (&self) -> Option<Metadata>;
    fn get_int(&self, key: &str) -> Option<i64>;
    fn get_string(&self, key: &str) -> Option<String>;
}

impl ToMetadata for HashMap<String, Bencode> {
    fn to_metadata (&self) -> Option<Metadata> {
        let announce = self.get_string("announce").unwrap();
        Some(Metadata {
            announce: announce,
        })
    }

    fn get_int (&self, key: &str) -> Option <i64> {
        match self.get(key) {
            Some(&Bencode::Int(a)) => Some(a),
            _ => None
        }
    }

    fn get_string (&self, key: &str) -> Option <String> {
        match self.get(key) {
            Some(&Bencode::String(ref a)) => Some(a.clone()),
            _ => None
        }
    }

}

fn main () {
    let path = env::args().nth(1).unwrap();
    let content = deserialize_file(path).unwrap();
    let metadata = match *content.first().unwrap() {
        Bencode::Dict(ref x) => {
             x.to_metadata()
        },
        _ => panic!("something")
    };

    println!("{:?}", metadata.unwrap());
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(path)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex;
extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;
use regex::Regex;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(output);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {let (old, new) = (&pair[1], &pair[0]);
			let output = find_moves(&repo, old.clone(), new.clone()).unwrap();
			// mangle the vectors here to make pretty output
			make_output(output);
		}
	}
}

fn make_output(output: Vec<Output>) {
	println!("make_output:");
    println!("\toutput.len()={}", output.len());
    for i in range(0, output.len()) {
            println!("\told_commit={}",         output[i].old_commit);
            println!("\tnew_commit={}",         output[i].new_commit);
            println!("\torigin_line={}",        output[i].origin_line);
            println!("\tdestintation_line={}",  output[i].destination_line);
            println!("\tnum_lines={}",          output[i].num_lines);
    }
}

#[derive(Debug)]
enum State {
    Other, Addition, Deletion
}

#[derive(Debug)]
enum FoundState {
    Added, Deleted
}

fn dump_diffdelta(delta: &DiffDelta) {
    println!("delta: nfiles={} status={:?} old_file=(id={} path_bytes={:?} path={:?} tsize={}) new_file=(id={} path_bytes={:?} path={:?} tsize={})",
            delta.nfiles(), delta.status(),
            delta.old_file().id(), delta.old_file().path_bytes(), delta.old_file().path(), delta.old_file().size(),
            delta.new_file().id(), delta.new_file().path_bytes(), delta.new_file().path(), delta.new_file().size());
}

fn format_key(key: String) -> String {
    let removeWhiteSpace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    removeWhiteSpace.replace_all(key.as_slice(), "")
}

#[derive(Debug)]
struct Found {
    filename: Path,
    key: String,
    state: FoundState,
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {

    println!("\nFIND MOVES---------------------");

	let old_tree = try!(old.tree());
	let new_tree = try!(new.tree());
	// Build up a diff of the two trees.
	let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();

    let mut keys: Vec<String> = Vec::new();  // TODO Will become hashmap.

    let mut founds: Vec<Found> = Vec::new();

	// Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
	// It's a bit weird, but I think it will provide the necessary information.
	diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files.");

		// Thinking:
		//  * If is not a hunk, keep going.
		//  * If it's a hunk, do regex magic.
		//  * Stick regex output into a hashmap as a hash.
		//  * Later, we will iterate through and output pased on the values.
		// Filter out all the boring context lines.
		// If we're not interested in this line just return since it will iterate to the next.
		if maybe_hunk.is_none() { return true }; // Return early.

        //println!("top of loop: keys={:?}", keys);
        //println!("top of loop: added={:?} deleted={:?}", added, deleted);

        // 'origin' is wrapped in pipes to ease displaying space characters.
        print!("line: old={:?} new={:?} offset={} |origin|=|{}|\n      content={}",
                 line.old_lineno(), line.new_lineno(), line.content_offset(),
                 line.origin(), str::from_utf8(line.content()).unwrap());

        //dump_diffdelta(&delta);

		match line.origin() {
			// Additions
			'+' | '>' => {
                println!("In additions. state={:?}", state);

                added.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Addition;
				true
			},
			// Deletions
			'-' | '<' => {
                println!("In deletion. state={:?}", state);

                deleted.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    _ => (),
                }

                state = State::Deletion;
				true
			},
			// Other
			_         => {
                println!("in _. state={:?}", state);

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
		}
	});

    println!("KEYS={:?}", keys);
    println!("FOUNDS={:?}", founds);

    Ok(vec![Output {
	    old_commit: old.id(),
	    new_commit: new.id(),
        old_filename: String::from_str("oldfilename"),
        new_filename: String::from_str("newfilename"),
	    origin_line: 0,
	    destination_line: 0,
	    num_lines: 0
    }])
}

#[derive(Debug)]
struct Output {
    old_commit: Oid,
    new_commit: Oid,
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<commit_msg>Create Found vector with additions and deletions that match the test/basic test case.<commit_after>#![feature(core)]
#![feature(path)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex;
extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;
use regex::Regex;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(output);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {let (old, new) = (&pair[1], &pair[0]);
			let output = find_moves(&repo, old.clone(), new.clone()).unwrap();
			// mangle the vectors here to make pretty output
			make_output(output);
		}
	}
}

fn make_output(output: Vec<Output>) {
	println!("make_output:");
    println!("\toutput.len()={}", output.len());
    for i in range(0, output.len()) {
            println!("\told_commit={}",         output[i].old_commit);
            println!("\tnew_commit={}",         output[i].new_commit);
            println!("\torigin_line={}",        output[i].origin_line);
            println!("\tdestintation_line={}",  output[i].destination_line);
            println!("\tnum_lines={}",          output[i].num_lines);
    }
}

#[derive(Debug)]
enum State {
    Other, Addition, Deletion
}

#[derive(Debug)]
enum FoundState {
    Added, Deleted
}

fn dump_diffdelta(delta: &DiffDelta) {
    println!("delta: nfiles={} status={:?} old_file=(id={} path_bytes={:?} path={:?} tsize={}) new_file=(id={} path_bytes={:?} path={:?} tsize={})",
            delta.nfiles(), delta.status(),
            delta.old_file().id(), delta.old_file().path_bytes(), delta.old_file().path(), delta.old_file().size(),
            delta.new_file().id(), delta.new_file().path_bytes(), delta.new_file().path(), delta.new_file().size());
}

fn format_key(key: String) -> String {
    let removeWhiteSpace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    removeWhiteSpace.replace_all(key.as_slice(), "")
}

#[derive(Debug)]
struct Found {
    filename: Path,
    key: String,
    state: FoundState,
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {

    println!("\nFIND MOVES---------------------");

	let old_tree = try!(old.tree());
	let new_tree = try!(new.tree());
	// Build up a diff of the two trees.
	let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();
    let mut old_path: Path = Path::new("");
    let mut new_path: Path = Path::new("");

    let mut keys: Vec<String> = Vec::new();  // TODO Will become hashmap.

    let mut founds: Vec<Found> = Vec::new();

	// Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
	// It's a bit weird, but I think it will provide the necessary information.
	diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files.");

		// Thinking:
		//  * If is not a hunk, keep going.
		//  * If it's a hunk, do regex magic.
		//  * Stick regex output into a hashmap as a hash.
		//  * Later, we will iterate through and output pased on the values.
		// Filter out all the boring context lines.
		// If we're not interested in this line just return since it will iterate to the next.
		if maybe_hunk.is_none() { return true }; // Return early.

        //println!("top of loop: keys={:?}", keys);
        println!("top of loop: founds={:?}", founds);
        println!("top of loop: added={:?} deleted={:?}", added, deleted);

        // 'origin' is wrapped in pipes to ease displaying space characters.
        print!("line: old={:?} new={:?} offset={} |origin|=|{}|\n      content={}",
                 line.old_lineno(), line.new_lineno(), line.content_offset(),
                 line.origin(), str::from_utf8(line.content()).unwrap());

        //dump_diffdelta(&delta);

        old_path = delta.old_file().path().unwrap().clone();
        new_path = delta.new_file().path().unwrap().clone();

		match line.origin() {
			// Additions
			'+' | '>' => {
                println!("In additions. state={:?}", state);

                added.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Addition;
				true
			},
			// Deletions
			'-' | '<' => {
                println!("In deletion. state={:?}", state);

                deleted.push_str(str::from_utf8(line.content()).unwrap());

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    _ => (),
                }

                state = State::Deletion;
				true
			},
			// Other
			_         => {
                println!("in _. state={:?}", state);

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: delta.new_file().path().unwrap().clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: delta.old_file().path().unwrap().clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
		}
	});

    // Grab one.
    match state {
        State::Addition => {
            if added.len() > 0 {
                founds.push(Found {
                    filename: new_path,
                    key: format_key(added.clone()),
                    state: FoundState::Added,
                });
            }
        },
        State::Deletion => {
            if deleted.len() > 0 {
                founds.push(Found {
                    filename: old_path,
                    key: format_key(deleted.clone()),
                    state: FoundState::Deleted,
                });
            }
        },
        _ => (),
    }

    println!("KEYS={:?}", keys);
    println!("FOUNDS={:?}", founds);

    Ok(vec![Output {
	    old_commit: old.id(),
	    new_commit: new.id(),
        old_filename: String::from_str("oldfilename"),
        new_filename: String::from_str("newfilename"),
	    origin_line: 0,
	    destination_line: 0,
	    num_lines: 0
    }])
}

#[derive(Debug)]
struct Output {
    old_commit: Oid,
    new_commit: Oid,
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<|endoftext|>


<commit_before>extern crate argparse;
extern crate glob;
extern crate regex;

use argparse::{ArgumentParser, StoreTrue, Store, StoreOption, Print};
use std::io::BufRead;
use glob::glob;
use regex::Regex;

fn identity<T>(x: T) -> T {x}

struct Options {
    debug: bool,
    path: String,
    file: Option<String>
}

fn get_possible_files_from_glob() -> Result<Vec<std::path::PathBuf>, glob::PatternError> {
    // Dir.glob("**/*").reject { |p| File.directory?(p) }.map { |p| Path.new(p) }
    glob("**/*").map(|paths| paths.flat_map(identity).filter(|path| path.is_file()).collect())
}

fn get_filename_minus_extension(path_str: &String) -> String {
    std::path::Path::new(path_str).file_stem().unwrap().to_str().unwrap().to_string()
}

fn is_test_file(path: &String) -> bool {
    let re = Regex::new(r"^(features/|test/|spec/|tests/)").unwrap();
    re.is_match(path.as_str())
}

fn strip_test_words(filename: &String) -> String {
    let re = Regex::new(r"(test_)?(?P<p>\w+?)(_rake_spec|_spec|_test|_steps)?(\.rb|\.exs|\.ex|\.js|\.py)?$").unwrap();
    re.replace_all(filename.as_str(), "$p")
}

fn cleanse_path(path: &String) -> String {
    let re = Regex::new(r"^(\./|\r|\n| )").unwrap();
    re.replace_all(path.as_str(), "")
}

fn find_longest_common_substring(s1: &String, s2: &String) -> String {
    // Currently this is implemented using a dynamic programming solution similar
    // to http://www.geeksforgeeks.org/longest-common-substring/. This is O(N*M)
    // where N is the length of one string and M is the length of the other
    // string.
    //
    // Another option would of course be to explore using something like a
    // suffix tree to solve this problem, something like, the following.
    // http://www.geeksforgeeks.org/suffix-tree-application-5-longest-common-substring-2/
    // This is O(M+N) to build a Generalized Suffix Tree and O(M+N) to find the
    // the longest common substring via depth first search.
    //
    // Beyond that we would have to explore not caring about longest substring
    // and moving to a similarity ranking algorithm that maybe cares about
    // subsequences rather that substrings, etc.
    if s1.is_empty() || s2.is_empty() {
        return "".to_string()
    }

    let mut m: Vec<Vec<i32>> = Vec::new();
    for x in 0..s1.len() {
        let mut v: Vec<i32> = Vec::new();
        for y in 0..s2.len() {
            v.push(0);
        }
        m.push(v);
    }

    let mut longest_length = 0;
    let mut longest_end_pos = 0;

    for i in 0..s1.len() {
        for j in 0..s2.len() {
            if s1.as_bytes()[i] == s2.as_bytes()[j] {
                m[i][j] = 1;
                if i > 0 && j > 0 {
                    m[i][j] += m[i-1][j-1];
                }
                if m[i][j] > longest_length {
                    longest_length = m[i][j];
                    longest_end_pos = i;
                }
            }
        }
    }

    let start: usize = longest_end_pos as usize - longest_length as usize + 1;
    let end: usize = longest_end_pos;
    s1[start..end].to_string()
}

fn score(s1: &String, s2: &String) -> f32 {
    // TODO: Improve performance. At the moment the find_longest_common_substring
    // method is the most costly operation in this app. This leaves us with a few
    // levers to move in terms of improving performance. Specifically, we could
    // find a more performant algorithm, or find a way to reduce the number of
    // times it has to be called, or use threading to do a scatter and gather
    // approach.
    let longest_match = find_longest_common_substring(s1, s2);
    (longest_match.len() as f32/s2.len() as f32) * (longest_match.len() as f32/s1.len() as f32)
}

fn main() {
    let mut options = Options {
        debug: false,
        path: "".to_string(),
        file: None
    };

    let mut highest_score: f32 = 0.0;
    let mut best_match: String = "".to_string();

    { // block limits of borrows by refer() method calls
        let mut ap = ArgumentParser::new();
        ap.add_option(&["-v", "--version"], Print(env!("CARGO_PKG_VERSION").to_string()), "show version");
        ap.refer(&mut options.debug).add_option(&["-d", "--debug"], StoreTrue, "enable debug output");
        ap.refer(&mut options.file).add_option(&["-f", "--file"], StoreOption, "possible alternates file, - for stdin");
        ap.refer(&mut options.path).add_argument("PATH", Store, "path to find alternate for").required();
        ap.parse_args_or_exit();
    }

    //println!("debug: {}", options.debug);
    //println!("required path: {}", options.path);
    let cleansed_path = cleanse_path(&options.path);
    let mut filename = get_filename_minus_extension(&options.path);
    if is_test_file(&cleansed_path) {
        filename = strip_test_words(&filename);
    }

    if let Some(unwrapped_file) = options.file {
        if unwrapped_file == "-" {
            println!("DREW: HERE");
            let stdin = std::io::stdin();
            //for line in stdin.lock().lines() {
            //  println!("{}", line.unwrap());
            //}
            if is_test_file(&cleansed_path) {
                let reduced_paths = stdin.lock().lines()
                .map(|path| cleanse_path(&path.unwrap()))
                .filter(|path| path.contains(filename.as_str()))  // filter to paths that contain the filename
                .filter(|path| !is_test_file(&path)); // filter to paths that aren't test files
                for path in reduced_paths {
                    let s = score(&path, &cleansed_path);
                    if s > highest_score {
                        highest_score = s;
                        best_match = path;
                    }
                }
            } else {
                let reduced_paths = stdin.lock().lines()
                .map(|path| cleanse_path(&path.unwrap()))
                .filter(|path| path.contains(filename.as_str()))  // filter to paths that contain the filename
                .filter(|path| is_test_file(&path)); // filter to paths that aren't test files
                for path in reduced_paths {
                    let s = score(&path, &cleansed_path);
                    if s > highest_score {
                        highest_score = s;
                        best_match = path;
                    }
                }
            }
        } else {
            println!("DON'T CURRENTLY SUPPORT SPECFIED FILES");
            // figure out how to open specified file and read in as content
        }
    } else {
        match get_possible_files_from_glob() {
            Ok(paths) => {
                // - get collection of possible alternate file paths
                // - cleanse possible paths (stripping prefixed ./ and postfixed \r,\n,sp)
                // - if path is test file
                //      - reduce set to paths that match filename stripped of test words and extensions
                //      - reduce set to non-test files
                //   else
                //      - reduce set to paths that match filename stripped of test words and extensions
                //      - reduce set to test files
                // - score the reduced set of possible paths, tracking the highest score
                // - return the path with the highest score

                if is_test_file(&cleansed_path) {
                    let reduced_paths = paths.iter()
                        .map(|path| cleanse_path(&path.to_str().unwrap().to_string()))
                        .filter(|path| path.contains(filename.as_str()))  // filter to paths that contain the filename
                        .filter(|path| !is_test_file(&path)); // filter to paths that aren't test files
                    for path in reduced_paths {
                        let s = score(&path, &cleansed_path);
                        if s > highest_score {
                            highest_score = s;
                            best_match = path;
                        }
                    }
                } else {
                    let reduced_paths = paths.iter()
                        .map(|path| cleanse_path(&path.to_str().unwrap().to_string()))
                        .filter(|path| path.contains(filename.as_str())) // filter to paths that contain the filename
                        .filter(|path| is_test_file(&path)); // filter to paths that ARE test files
                    for path in reduced_paths {
                        let s = score(&path, &cleansed_path);
                        if s > highest_score {
                            highest_score = s;
                            best_match = path;
                        }
                    }
                }
            },
            Err(e) => println!("{:?}", e)
        }
    }
    print!("{}", best_match);
}
<commit_msg>Clean up rust implementation a bit<commit_after>extern crate argparse;
extern crate glob;
extern crate regex;

use argparse::{ArgumentParser, StoreTrue, Store, StoreOption, Print};
use std::io::BufRead;
use glob::glob;
use regex::Regex;

fn identity<T>(x: T) -> T {x}

struct Options {
    debug: bool,
    path: String,
    file: Option<String>
}

fn get_possible_files_from_glob() -> Result<Vec<std::path::PathBuf>, glob::PatternError> {
    // Dir.glob("**/*").reject { |p| File.directory?(p) }.map { |p| Path.new(p) }
    glob("**/*").map(|paths| paths.flat_map(identity).filter(|path| path.is_file()).collect())
}

fn get_filename_minus_extension(path_str: &String) -> String {
    std::path::Path::new(path_str).file_stem().unwrap().to_str().unwrap().to_string()
}

fn is_test_file(path: &String) -> bool {
    let re = Regex::new(r"^(features/|test/|spec/|tests/)").unwrap();
    re.is_match(path.as_str())
}

fn strip_test_words(filename: &String) -> String {
    let re = Regex::new(r"(test_)?(?P<p>\w+?)(_rake_spec|_spec|_test|_steps)?(\.rb|\.exs|\.ex|\.js|\.py)?$").unwrap();
    re.replace_all(filename.as_str(), "$p")
}

fn cleanse_path(path: &str) -> String {
    let s = path.to_string();
    if s[0..2].to_string() == "./" {
        s[2..].to_string()
    } else {
        s
    }
}

fn find_longest_common_substring(s1: &String, s2: &String) -> String {
    // Currently this is implemented using a dynamic programming solution similar
    // to http://www.geeksforgeeks.org/longest-common-substring/. This is O(N*M)
    // where N is the length of one string and M is the length of the other
    // string.
    //
    // Another option would of course be to explore using something like a
    // suffix tree to solve this problem, something like, the following.
    // http://www.geeksforgeeks.org/suffix-tree-application-5-longest-common-substring-2/
    // This is O(M+N) to build a Generalized Suffix Tree and O(M+N) to find the
    // the longest common substring via depth first search.
    //
    // Beyond that we would have to explore not caring about longest substring
    // and moving to a similarity ranking algorithm that maybe cares about
    // subsequences rather that substrings, etc.
    if s1.is_empty() || s2.is_empty() {
        return "".to_string()
    }

    let mut m: Vec<Vec<i32>> = Vec::new();
    for x in 0..s1.len() {
        let mut v: Vec<i32> = Vec::new();
        for y in 0..s2.len() {
            v.push(0);
        }
        m.push(v);
    }

    let mut longest_length = 0;
    let mut longest_end_pos = 0;

    for i in 0..s1.len() {
        for j in 0..s2.len() {
            if s1.as_bytes()[i] == s2.as_bytes()[j] {
                m[i][j] = 1;
                if i > 0 && j > 0 {
                    m[i][j] += m[i-1][j-1];
                }
                if m[i][j] > longest_length {
                    longest_length = m[i][j];
                    longest_end_pos = i;
                }
            }
        }
    }

    let start: usize = longest_end_pos as usize - longest_length as usize + 1;
    let end: usize = longest_end_pos;
    s1[start..end].to_string()
}

fn score(s1: &String, s2: &String) -> f32 {
    // TODO: Improve performance. At the moment the find_longest_common_substring
    // method is the most costly operation in this app. This leaves us with a few
    // levers to move in terms of improving performance. Specifically, we could
    // find a more performant algorithm, or find a way to reduce the number of
    // times it has to be called, or use threading to do a scatter and gather
    // approach.
    let longest_match = find_longest_common_substring(s1, s2);
    (longest_match.len() as f32/s2.len() as f32) * (longest_match.len() as f32/s1.len() as f32)
}

fn main() {
    let mut options = Options {
        debug: false,
        path: "".to_string(),
        file: None
    };

    let mut highest_score: f32 = 0.0;
    let mut best_match: String = "".to_string();

    { // block limits of borrows by refer() method calls
        let mut ap = ArgumentParser::new();
        ap.add_option(&["-v", "--version"], Print(env!("CARGO_PKG_VERSION").to_string()), "show version");
        ap.refer(&mut options.debug).add_option(&["-d", "--debug"], StoreTrue, "enable debug output");
        ap.refer(&mut options.file).add_option(&["-f", "--file"], StoreOption, "possible alternates file, - for stdin");
        ap.refer(&mut options.path).add_argument("PATH", Store, "path to find alternate for").required();
        ap.parse_args_or_exit();
    }

    //println!("debug: {}", options.debug);
    //println!("required path: {}", options.path);
    let cleansed_path = cleanse_path(&options.path);
    let mut filename = get_filename_minus_extension(&options.path);
    if is_test_file(&cleansed_path) {
        filename = strip_test_words(&filename);
    }

    if let Some(unwrapped_file) = options.file {
        if unwrapped_file == "-" {
            println!("DREW: HERE");
            let stdin = std::io::stdin();
            if is_test_file(&cleansed_path) {
                let reduced_paths = stdin.lock().lines()
                .map(|path| cleanse_path(&path.unwrap()))
                .filter(|path| path.contains(filename.as_str()))  // filter to paths that contain the filename
                .filter(|path| !is_test_file(&path)); // filter to paths that aren't test files
                for path in reduced_paths {
                    let s = score(&path, &cleansed_path);
                    if s > highest_score {
                        highest_score = s;
                        best_match = path;
                    }
                }
            } else {
                let reduced_paths = stdin.lock().lines()
                .map(|path| cleanse_path(&path.unwrap()))
                .filter(|path| path.contains(filename.as_str()))  // filter to paths that contain the filename
                .filter(|path| is_test_file(&path)); // filter to paths that aren't test files
                for path in reduced_paths {
                    let s = score(&path, &cleansed_path);
                    if s > highest_score {
                        highest_score = s;
                        best_match = path;
                    }
                }
            }
        } else {
            println!("DON'T CURRENTLY SUPPORT SPECFIED FILES");
            // figure out how to open specified file and read in as content
        }
    } else {
        match get_possible_files_from_glob() {
            Ok(paths) => {
                if is_test_file(&cleansed_path) {
                    let reduced_paths = paths.iter()
                        .map(|path| cleanse_path(&path.to_str().unwrap().to_string()))
                        .filter(|path| path.contains(filename.as_str()))  // filter to paths that contain the filename
                        .filter(|path| !is_test_file(&path)); // filter to paths that aren't test files
                    for path in reduced_paths {
                        let s = score(&path, &cleansed_path);
                        if s > highest_score {
                            highest_score = s;
                            best_match = path;
                        }
                    }
                } else {
                    let reduced_paths = paths.iter()
                        .map(|path| cleanse_path(&path.to_str().unwrap().to_string()))
                        .filter(|path| path.contains(filename.as_str())) // filter to paths that contain the filename
                        .filter(|path| is_test_file(&path)); // filter to paths that ARE test files
                    for path in reduced_paths {
                        let s = score(&path, &cleansed_path);
                        if s > highest_score {
                            highest_score = s;
                            best_match = path;
                        }
                    }
                }
            },
            Err(e) => println!("{:?}", e)
        }
    }
    print!("{}", best_match);
}
<|endoftext|>


<commit_before>//  Copyright 2015 Nathanael Merrill
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
extern crate rustty;
extern crate gst;
extern crate argparse;
extern crate regex;

mod playlist;
mod default_ui;

use argparse::{ArgumentParser, Store, StoreTrue, List};
use default_ui::*;
use regex::Regex;
use gst::ElementT;
use std::fs;
use std::path::Path;
use std::io;

fn main() {
    let mut global_err = String::from("");
    {
    let mut regex = String::new();
    let mut songs : Vec<String> = vec![]; // TODO: add with capacity!
    let mut recurse = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Rust Fucking Simple Music Player");
        ap.refer(&mut recurse)
            .add_option(&["-r", "--recursive"], StoreTrue, "Recurse through directories if passed them");
        ap.refer(&mut regex)
            .add_option(&["-s", "--search"], Store, "Search songs using regular expressions");
        ap.refer(&mut songs)
            .add_argument("arguments", List, "Songs to play");
        ap.parse_args_or_exit();
    }

    if songs.len() == 0 {
        println!("Usage:");
        println!("    rfsmp [OPTIONS] [SONGS ...]");
        return;
    }

    if regex != "" {
        let  re = Regex::new(&regex).expect("regex invalid");
        songs.retain(|i| re.is_match(i));
    }

    if recurse {
        let mut append : Vec<String> = vec![];
        for song in songs.clone() {
            if fs::metadata(&song).unwrap().is_dir() {
                append.append(&mut recurse_songs(&song).unwrap());
            } else {
                append.push(song);
            }
        }
        songs = append;
    }
    let mut playlist = playlist::Playlist::new(songs);

    gst::init();
    let mut playbin = gst::PlayBin::new("audio_player")
        .expect("Couldn't create playlist");
    let mut main_loop = gst::MainLoop::new();

    let mut bus;
    let bus_receiver;

    let song = match playlist.get_next_song() {
        Some(a) => gst::filename_to_uri(a).expect("URI Error"),
        None => panic!("can't get song"),
    };

    playbin.set_uri(&song);
    bus = playbin.bus().expect("Couldn't get pipeline bus");
    bus_receiver = bus.receiver();

    let mut ui = UI::new(&playlist);

    main_loop.spawn();
    playbin.play();

    let mut song_buffered = false;

    'outer: loop {
        loop {
            match bus_receiver.try_recv() {
                Ok(message) => {
                    match message.parse(){
                        gst::Message::ErrorParsed{ref error, ..} => {
                            global_err.push_str(
                                &format!("error msg from element `{}`: {}, quit",
                                        message.src_name(), error.message()));
                            break 'outer;
                        }
                        gst::Message::Eos(ref _msg) => {
                            break 'outer;
                        }
                        gst::Message::StreamStart(ref _msg) => {
                            song_buffered = false;
                            playlist.go_to_next();
                        }
                        _ => {
                            //println!("msg of type `{}` from element `{}`",
                            //         message.type_name(), message.src_name());
                        }
                    }
                }
                Err(err) => {
                    match err {
                        std::sync::mpsc::TryRecvError::Empty => break,
                        std::sync::mpsc::TryRecvError::Disconnected => break,
                    }
                }
            }
        }
        let stream_dir = playbin.duration_s();
        let stream_pos = playbin.position_s();

        if stream_dir.is_some() && stream_pos.is_some() && !song_buffered {
            if stream_dir.unwrap() - stream_pos.unwrap() < 3.0 {
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        //println!("All songs played");
                    }
                };
            }
        }

        let stream_dir = match stream_dir {
            Some(a) => a as i32,
            None => 0,
        };
        let stream_pos = match stream_pos {
            Some(a) => a as i32,
            None => 0,
        };

        match ui.manage_ui(&playlist, stream_pos, stream_dir) {
            UIResult::PlayPause => {
                if playbin.is_paused() {
                    playbin.play();
                }
                else {
                    playbin.pause();
                }
            }
            UIResult::Previous => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                playlist.go_to_prev();
                playlist.go_to_prev();
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();

            }
            UIResult::Next => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();
            }
            UIResult::Exit => {
                break;
            }
            UIResult::Error(a) => {
                global_err = a;
                break 'outer;
            }
            UIResult::NA => {}
        }
        std::thread::sleep(std::time::Duration::new(0, 20000000));
    }
    main_loop.quit();
    }
    if global_err != "" {
        println!("{}", global_err);
    }
}

fn recurse_songs(dir : &String) -> Result<Vec<String>, io::Error>{
    let mut toAppend : Vec<String> = vec![];
    if try!(fs::metadata(&dir)).is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let memes = entry.unwrap().path();
            match try!(fs::metadata(memes.clone())).is_dir() {
                false => toAppend.push(memes.to_str().unwrap().to_string()),
                true => toAppend.append(&mut recurse_songs(&memes.to_str().unwrap()
                                                           .to_string()).unwrap()),
            };
         }
    }
    Ok(toAppend)
}

<commit_msg>Minor formatting fixes.<commit_after>//  Copyright 2015 Nathanael Merrill
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
extern crate rustty;
extern crate gst;
extern crate argparse;
extern crate regex;

mod playlist;
mod default_ui;

use argparse::{ArgumentParser, Store, StoreTrue, List};
use default_ui::*;
use regex::Regex;
use gst::ElementT;
use std::fs;
use std::io;

fn recurse_songs(dir : &String) -> Result<Vec<String>, io::Error>{
    let mut to_append : Vec<String> = vec![];
    if try!(fs::metadata(&dir)).is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let memes = entry.unwrap().path();
            match try!(fs::metadata(memes.clone())).is_dir() {
                false => to_append.push(memes.to_str().unwrap().to_string()),
                true => to_append.append(&mut recurse_songs(&memes.to_str().unwrap()
                                                           .to_string()).unwrap()),
            };
         }
    }
    Ok(to_append)
}

fn main() {
    let mut global_err = String::from("");
    {
    let mut regex = String::new();
    let mut songs : Vec<String> = vec![]; // TODO: add with capacity!
    let mut recurse = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Rust Fucking Simple Music Player");
        ap.refer(&mut recurse)
            .add_option(&["-r", "--recursive"], StoreTrue, "Recurse through directories if passed them");
        ap.refer(&mut regex)
            .add_option(&["-s", "--search"], Store, "Search songs using regular expressions");
        ap.refer(&mut songs)
            .add_argument("arguments", List, "Songs to play");
        ap.parse_args_or_exit();
    }

    if songs.len() == 0 {
        println!("Usage:");
        println!("    rfsmp [OPTIONS] [SONGS ...]");
        return;
    }

    if regex != "" {
        let re = Regex::new(&regex).expect("regex invalid");
        songs.retain(|i| re.is_match(i));
    }

    if recurse {
        let mut append : Vec<String> = vec![];
        for song in songs.clone() {
            if fs::metadata(&song).unwrap().is_dir() {
                append.append(&mut recurse_songs(&song).unwrap());
            } else {
                append.push(song);
            }
        }
        songs = append;
    }
    let mut playlist = playlist::Playlist::new(songs);

    gst::init();
    let mut playbin = gst::PlayBin::new("audio_player")
        .expect("Couldn't create playlist");
    let mut main_loop = gst::MainLoop::new();

    let mut bus;
    let bus_receiver;

    let song = match playlist.get_next_song() {
        Some(a) => gst::filename_to_uri(a).expect("URI Error"),
        None => panic!("can't get song"),
    };

    playbin.set_uri(&song);
    bus = playbin.bus().expect("Couldn't get pipeline bus");
    bus_receiver = bus.receiver();

    let mut ui = UI::new(&playlist);

    main_loop.spawn();
    playbin.play();

    let mut song_buffered = false;

    'outer: loop {
        loop {
            match bus_receiver.try_recv() {
                Ok(message) => {
                    match message.parse(){
                        gst::Message::ErrorParsed{ref error, ..} => {
                            global_err.push_str(
                                &format!("error msg from element `{}`: {}, quit",
                                        message.src_name(), error.message()));
                            break 'outer;
                        }
                        gst::Message::Eos(ref _msg) => {
                            break 'outer;
                        }
                        gst::Message::StreamStart(ref _msg) => {
                            song_buffered = false;
                            playlist.go_to_next();
                        }
                        _ => {
                            //println!("msg of type `{}` from element `{}`",
                            //         message.type_name(), message.src_name());
                        }
                    }
                }
                Err(err) => {
                    match err {
                        std::sync::mpsc::TryRecvError::Empty => break,
                        std::sync::mpsc::TryRecvError::Disconnected => break,
                    }
                }
            }
        }
        let stream_dir = playbin.duration_s();
        let stream_pos = playbin.position_s();

        if stream_dir.is_some() && stream_pos.is_some() && !song_buffered {
            if stream_dir.unwrap() - stream_pos.unwrap() < 3.0 {
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        //println!("All songs played");
                    }
                };
            }
        }

        let stream_dir = match stream_dir {
            Some(a) => a as i32,
            None => 0,
        };
        let stream_pos = match stream_pos {
            Some(a) => a as i32,
            None => 0,
        };

        match ui.manage_ui(&playlist, stream_pos, stream_dir) {
            UIResult::PlayPause => {
                if playbin.is_paused() {
                    playbin.play();
                }
                else {
                    playbin.pause();
                }
            }
            UIResult::Previous => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                playlist.go_to_prev();
                playlist.go_to_prev();
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();

            }
            UIResult::Next => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();
            }
            UIResult::Exit => {
                break;
            }
            UIResult::Error(a) => {
                global_err = a;
                break 'outer;
            }
            UIResult::NA => {}
        }
        std::thread::sleep(std::time::Duration::new(0, 20000000));
    }
    main_loop.quit();
    }
    if global_err != "" {
        println!("{}", global_err);
    }
}
<|endoftext|>


<commit_before>//#![feature(no_std, lang_items, asm, core, core_str_ext)]
//#![no_std]
//#![no_builtins]
#![feature(asm, libc)]
#![no_main]

#![allow(dead_code)]
#![allow(unused_variables)]

//mod llvm_symbols;
mod auxv;
mod kernel_block;
mod utils;
mod image;
mod binary;
mod relocate;
mod link_map;
mod linker;

use kernel_block::KernelBlock;
use utils::*;
use binary::elf::dyn;
use binary::elf::program_header;
//use binary::elf::rela;

extern crate libc;

//extern "C"
//void __attribute__((noinline)) __attribute__((visibility("default")))

// unused; someone figure out how to get gdb working when running as a dyld
extern "C" {
    fn rtld_db_dlactivity();
}

// elf abi requires _start; this must be in assembly because we need
// the raw stack pointer as the argument to _dryad_init
extern {
    fn _start();
    fn __init_tls(aux: *const u64); // pointer to aux vector indexed by AT_<TYPE> that musl likes
}

#[no_mangle]
pub extern fn _dryad_init(raw_args: *const u64) -> u64 {

    let block = KernelBlock::new(raw_args);
    unsafe { block.unsafe_print(); }

    let linker_base = block.getauxval(auxv::AT_BASE).unwrap();
    let entry  = block.getauxval(auxv::AT_ENTRY).unwrap();

    let start_addr = _start as *const u64 as u64;

    // without this,
    // following comparison fails for some inexplicable reason... yay for side-effectful printing again
    unsafe {
        write(&"start: 0x");
        write_u64(start_addr, true);
        write(&" entry: 0x");
        write_u64(entry, true);
        write(&"\n");
    }

    if start_addr == entry {
        // because it's _tradition_
        // (https://fossies.org/dox/glibc-2.22/rtld_8c_source.html)
        // line 786:
        // > Ho ho.  We are not the program interpreter!  We are the program itself!
        unsafe { write(&"-=|dryad====-\n"); }
        _exit(0);
        return 0;
    }

    match linker::Linker::new(linker_base) {
        Ok (dryad) => {
            // dryad has successfully relocated itself; time to init tls
            unsafe { __init_tls(block.get_aux().as_ptr()); }
            // EXECUTABLE
            println!("BEGIN EXE LINKING");
            // TODO:
            // * image::elf::new(<stuff>)
            // * dryad::link(image)
            unsafe {
                let phdr_addr = block.getauxval(auxv::AT_PHDR).unwrap();
                let phent  = block.getauxval(auxv::AT_PHENT).unwrap();
                let phnum  = block.getauxval(auxv::AT_PHNUM).unwrap();

                let addr = phdr_addr as *const program_header::ProgramHeader;
                let phdrs = program_header::to_phdr_array(addr, phnum as usize);
                println!("Program Headers: {:#?}", &phdrs);
                let mut base = 0;
                let mut load_bias = 0;
                for phdr in phdrs {
                    if phdr.p_type == program_header::PT_PHDR {
                        load_bias = phdr_addr - phdr.p_vaddr;
                        base = phdr_addr - phdr.p_offset;
                        break;
                    }
                }
                println!("load bias: {:x} base: {:x}", load_bias, base);

                if let Some(dynamic) = dyn::get_dynamic_array(load_bias, phdrs) {
                    println!("_DYNAMIC: {:#?}", dynamic);
                    let strtab = dyn::get_strtab(load_bias, dynamic);
                    let needed = dyn::get_needed(dynamic, strtab, base, load_bias);
                    println!("Needed: {:#?}", needed);
                } else {
                    //            println!("<dryad> NO DYNAMIC for {}", *block.argv);
                }
            }

            // commenting _exit will successfully
            // tranfer control (in my single test case ;))
            // to the program entry in test/test,
            // but segfaults when printf is called (obviously)
            // since we've done no dynamic linking
            _exit(0);
            entry
        },
        Err (msg) => {
            unsafe { write(&msg); }
            _exit(1);
            0
        }
    }
}
<commit_msg>comment;<commit_after>//#![feature(no_std, lang_items, asm, core, core_str_ext)]
//#![no_std]
//#![no_builtins]
#![feature(asm, libc)]
#![no_main]

#![allow(dead_code)]
#![allow(unused_variables)]

//mod llvm_symbols;
mod auxv;
mod kernel_block;
mod utils;
mod image;
mod binary;
mod relocate;
mod link_map;
mod linker;

use kernel_block::KernelBlock;
use utils::*;
use binary::elf::dyn;
use binary::elf::program_header;
//use binary::elf::rela;

extern crate libc;

//extern "C"
//void __attribute__((noinline)) __attribute__((visibility("default")))

// unused; someone figure out how to get gdb working when running as a dyld
extern "C" {
    fn rtld_db_dlactivity();
}

// elf abi requires _start; this must be in assembly because we need
// the raw stack pointer as the argument to _dryad_init
extern {
    fn _start();
    fn __init_tls(aux: *const u64); // pointer to aux vector indexed by AT_<TYPE> that musl likes
}

#[no_mangle]
pub extern fn _dryad_init(raw_args: *const u64) -> u64 {

    let block = KernelBlock::new(raw_args);
    unsafe { block.unsafe_print(); }

    let linker_base = block.getauxval(auxv::AT_BASE).unwrap();
    let entry  = block.getauxval(auxv::AT_ENTRY).unwrap();

    let start_addr = _start as *const u64 as u64;

    // without this,
    // following comparison fails for some inexplicable reason... yay for side-effectful printing again
    unsafe {
        write(&"start: 0x");
        write_u64(start_addr, true);
        write(&" entry: 0x");
        write_u64(entry, true);
        write(&"\n");
    }

    if start_addr == entry {
        // because it's _tradition_
        // (https://fossies.org/dox/glibc-2.22/rtld_8c_source.html)
        // line 786:
        // > Ho ho.  We are not the program interpreter!  We are the program itself!
        unsafe { write(&"-=|dryad====-\n"); }
        _exit(0);
        return 0;
    }

    match linker::Linker::new(linker_base) {
        Ok (dryad) => {
            // dryad has successfully relocated itself; time to init tls
            unsafe { __init_tls(block.get_aux().as_ptr()); }
            // EXECUTABLE
            println!("BEGIN EXE LINKING");
            // TODO:
            // * image::elf::new(<stuff>)
            // * dryad::link(image)
            unsafe {
                let phdr_addr = block.getauxval(auxv::AT_PHDR).unwrap();
                let phent  = block.getauxval(auxv::AT_PHENT).unwrap();
                let phnum  = block.getauxval(auxv::AT_PHNUM).unwrap();

                let addr = phdr_addr as *const program_header::ProgramHeader;
                let phdrs = program_header::to_phdr_array(addr, phnum as usize);
                println!("Program Headers: {:#?}", &phdrs);
                let mut base = 0;
                let mut load_bias = 0;
                for phdr in phdrs {
                    if phdr.p_type == program_header::PT_PHDR {
                        load_bias = phdr_addr - phdr.p_vaddr;
                        base = phdr_addr - phdr.p_offset;
                        break;
                    }
                }
                println!("load bias: {:x} base: {:x}", load_bias, base);

                if let Some(dynamic) = dyn::get_dynamic_array(load_bias, phdrs) {
                    println!("_DYNAMIC: {:#?}", dynamic);
                    let strtab = dyn::get_strtab(load_bias, dynamic);
                    let needed = dyn::get_needed(dynamic, strtab, base, load_bias);
                    println!("Needed: {:#?}", needed);
                } else {
                    //            println!("<dryad> NO DYNAMIC for {}", *block.argv);
                }
            }

            // commenting _exit will successfully
            // tranfer control (in my single test case ;))
            // to the program entry in test/test,
            // but segfaults when printf is called (obviously)
            // since we've done no dynamic linking
            _exit(0);
            entry
        },
        Err (msg) => {
            // relocating self failed somehow; we write the error message and exit
            unsafe { write(&msg); }
            _exit(1);
            0
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Import crates with necessary types into a new project.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

// Import necessary types from crates.

use exonum::blockchain::{self, Blockchain, Service, GenesisConfig, ValidatorKeys, Transaction,
                         ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, TransactionSend, ApiSender, NodeChannel};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::storage::{Fork, MemoryDB, MapIndex};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::encoding::{self, Field};
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

// Define constants for transaction types within the service.

const SERVICE_ID: u16 = 1;

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

impl Wallet {
    pub fn increase(&mut self, amount: u64) {
        let balance = self.balance() + amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }

    pub fn decrease(&mut self, amount: u64) {
        let balance = self.balance() - amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }
}

// // // // // // // // // // STORAGE DATA LAYOUT // // // // // // // // // //

pub struct CurrencySchema<'a> {
    view: &'a mut Fork,
}

impl<'a> CurrencySchema<'a> {
    pub fn wallets(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        let prefix = blockchain::gen_prefix(SERVICE_ID, 0, &());
        MapIndex::new(prefix, self.view)
    }

    pub fn wallet(&mut self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

impl Transaction for TxCreateWallet {
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets().put(self.pub_key(), wallet)
        }
    }
}

impl Transaction for TxTransfer {
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(mut sender), Some(mut receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                sender.decrease(amount);
                receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender<NodeChannel>,
}

impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {

        #[serde(untagged)]
        #[derive(Clone, Serialize, Deserialize)]
        enum TransactionRequest {
            CreateWallet(TxCreateWallet),
            Transfer(TxTransfer),
        }

        impl Into<Box<Transaction>> for TransactionRequest {
            fn into(self) -> Box<Transaction> {
                match self {
                    TransactionRequest::CreateWallet(trans) => Box::new(trans),
                    TransactionRequest::Transfer(trans) => Box::new(trans),
                }
            }
        }

        #[derive(Serialize, Deserialize)]
        struct TransactionResponse {
            tx_hash: Hash,
        }

        let self_ = self.clone();
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<TransactionRequest>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self_.channel.send(transaction).map_err(ApiError::Events)?;
                    let json = TransactionResponse { tx_hash };
                    self_.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };
        let route_post = "/v1/wallets/transaction";
        router.post(&route_post, transaction, "transaction");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

struct CurrencyService;

impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi { channel: ctx.node_channel().clone() };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

// // // // // // // // // // ENTRY POINT // // // // // // // // // //

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(CurrencyService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let peer_address = "0.0.0.0:2000".parse().unwrap();
    let api_address = "0.0.0.0:8000".parse().unwrap();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    println!("Starting a single node...");
    let mut node = Node::new(blockchain, node_cfg);

    println!("Blockchain is ready for transactions!");
    node.run().unwrap();
}
<commit_msg>add comments to the updated code<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Import crates with necessary types into a new project.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

// Import necessary types from crates.

use exonum::blockchain::{self, Blockchain, Service, GenesisConfig, ValidatorKeys, Transaction,
                         ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, TransactionSend, ApiSender, NodeChannel};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::storage::{Fork, MemoryDB, MapIndex};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::encoding::{self, Field};
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

// Define service ID for the service trait.

const SERVICE_ID: u16 = 1;

// Define constants for transaction types within the service.

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

// Define initial balance of a newly created wallet.

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

// Declare the data to be stored in the blockchain. In the present case,
// declare a type for storing information about the wallet and its balance.

/// Declare a [serializable][1]
/// [1]: https://github.com/exonum/exonum-doc/blob/master/src/architecture/serialization.md
/// struct and determine bounds of its fields with `encoding_struct!` macro.
encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

/// Add methods to the `Wallet` type for changing balance.
impl Wallet {
    pub fn increase(&mut self, amount: u64) {
        let balance = self.balance() + amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }

    pub fn decrease(&mut self, amount: u64) {
        let balance = self.balance() - amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }
}

// // // // // // // // // // DATA LAYOUT // // // // // // // // // //

/// Create schema of the key-value storage implemented by `MemoryDB`. In the
/// present case a `Fork` of the database is used.
pub struct CurrencySchema<'a> {
    view: &'a mut Fork,
}

/// Declare layout of the data. Use an instance of [`MapIndex`][2]
/// [2]: https://github.com/exonum/exonum-doc/blob/master/src/architecture/storage.md#mapindex
/// to keep wallets in storage. Index values are serialized `Wallet` structs.
///
/// Isolate the wallets map into a separate entity by adding a unique prefix,
/// i.e. the first argument to the `MapIndex::new` call.
impl<'a> CurrencySchema<'a> {
    pub fn wallets(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        let prefix = blockchain::gen_prefix(SERVICE_ID, 0, &());
        MapIndex::new(prefix, self.view)
    }

    /// Get a separate wallet from the storage.
    pub fn wallet(&mut self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

/// Create a new wallet.
message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

/// Transfer coins between the wallets.
message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

/// Execute a transaction.
impl Transaction for TxCreateWallet {
    /// Verify integrity of the transaction by checking the transaction
    /// signature.
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    /// Apply logic to the storage when executing the transaction.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets().put(self.pub_key(), wallet)
        }
    }
}

impl Transaction for TxTransfer {
    /// Check if the sender is not the receiver. Check correctness of the
    /// sender's signature.
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    /// Retrieve two wallets to apply the transfer. Check the sender's
    /// balance and apply changes to the balances of the wallets.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(mut sender), Some(mut receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                sender.decrease(amount);
                receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

/// Implement the node API.
#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender<NodeChannel>,
    blockchain: Blockchain,
}

/// Shortcut to get data on wallets.
impl CryptocurrencyApi {
    fn get_wallet(&self, pub_key: &PublicKey) -> Option<Wallet> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        schema.wallet(pub_key)
    }

    fn get_wallets(&self) -> Option<Vec<Wallet>> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        let idx = schema.wallets();
        let wallets: Vec<Wallet> = idx.values().collect();
        if wallets.is_empty() {
            None
        } else {
            Some(wallets)
        }
    }
}

/// Add an enum which joins transactions of both types to simplify request
/// processing.
#[serde(untagged)]
#[derive(Clone, Serialize, Deserialize)]
enum TransactionRequest {
    CreateWallet(TxCreateWallet),
    Transfer(TxTransfer),
}

/// Implement a trait for the enum for deserialized `TransactionRequest`s
/// to fit into the node channel.
impl Into<Box<Transaction>> for TransactionRequest {
    fn into(self) -> Box<Transaction> {
        match self {
            TransactionRequest::CreateWallet(trans) => Box::new(trans),
            TransactionRequest::Transfer(trans) => Box::new(trans),
        }
    }
}

/// The structure returned by the REST API.
#[derive(Serialize, Deserialize)]
struct TransactionResponse {
    tx_hash: Hash,
}

/// Implement the `Api` trait.
/// `Api` facilitates conversion between transactions/read requests and REST
/// endpoints; for example, it parses `POSTed` JSON into the binary transaction
/// representation used in Exonum internally.
impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<TransactionRequest>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self_.channel.send(transaction).map_err(ApiError::Events)?;
                    let json = TransactionResponse { tx_hash };
                    self_.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };

        // Gets status of all wallets.
        let self_ = self.clone();
        let wallets_info = move |_: &mut Request| -> IronResult<Response> {
            if let Some(wallets) = self_.get_wallets() {
                self_.ok_response(&serde_json::to_value(wallets).unwrap())
            } else {
                self_.not_found_response(
                    &serde_json::to_value("Wallets database is empty")
                        .unwrap(),
                )
            }
        };

        // Gets status of the wallet corresponding to the public key.
        let self_ = self.clone();
        let wallet_info = move |req: &mut Request| -> IronResult<Response> {
            let path = req.url.path();
            let wallet_key = path.last().unwrap();
            let public_key = PublicKey::from_hex(wallet_key).map_err(ApiError::FromHex)?;
            if let Some(wallet) = self_.get_wallet(&public_key) {
                self_.ok_response(&serde_json::to_value(wallet).unwrap())
            } else {
                self_.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
            }
        };

        // Bind the transaction handler to a specific route.
        router.post("/v1/wallets/transaction", transaction, "transaction");
        router.get("/v1/wallets", wallets_info, "wallets_info");
        router.get("/v1/wallet/:pub_key", wallet_info, "wallet_info");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

/// Define the service.
struct CurrencyService;

/// Implement a `Service` trait for the service.
impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    /// Implement a method to deserialize transactions coming to the node.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    /// Create a REST `Handler` to process web requests to the node.
    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

// // // // // // // // // // ENTRY POINT // // // // // // // // // //

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(CurrencyService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_address = "0.0.0.0:8000".parse().unwrap();
    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let peer_address = "0.0.0.0:2000".parse().unwrap();

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    println!("Starting a single node...");
    let mut node = Node::new(blockchain, node_cfg);

    println!("Blockchain is ready for transactions!");
    node.run().unwrap();
}
<|endoftext|>