<commit_before>use std::os;
use std::collections::HashMap;
use std::io;
use std::io::fs::PathExtensions;
use std::borrow::ToOwned;

use vobject::{Component,Property,parse_component,write_component};
use email::rfc5322::Rfc5322Parser;
use uuid::Uuid;

macro_rules! main_try {
    ($result: expr, $errmsg: expr) => (
        match $result {
            Ok(m) => m,
            Err(e) => {
                println!("{}: {}", $errmsg, e);
                os::set_exit_status(1);
                return;
            }
        }
    )
}

fn get_env() -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.extend(os::env().into_iter().filter(|&(ref key, ref value)| {
        (key.as_slice() == "EDITOR" || key.as_slice().starts_with("MATES_")) &&
            value.len() > 0
    }));
    env
}

fn expect_env<'a>(env: &'a HashMap<String, String>, key: &str) -> &'a String {
    env.get(key).expect(
        format!("The {} environment variable must be set.", key).as_slice()
    )
}

fn build_index(outfile: &Path, dir: &Path) -> io::IoResult<()> {
    if !dir.is_dir() {
        return Err(io::IoError {
            kind: io::MismatchedFileTypeForOperation,
            desc: "MATES_DIR must be a directory.",
            detail: None
        });
    };

    let mut outf = io::File::create(outfile);
    let entries = try!(io::fs::readdir(dir));
    let mut errors = false;

    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }

        let contact = match Contact::from_file(entry.clone()) {
            Ok(x) => x,
            Err(e) => {
                println!("Error while reading {}: {}", entry.display(), e);
                errors = true;
                continue
            }
        };

        match index_item_from_contact(&contact) {
            Ok(index_string) => {
                try!(outf.write_str(index_string.as_slice()));
            },
            Err(e) => {
                println!("Error while indexing {}: {}", entry.display(), e);
                errors = true;
                continue
            }
        };
    };

    if errors {
        Err(io::IoError {
            kind: io::OtherIoError,
            desc: "Several errors happened while generating the index.",
            detail: None
        })
    } else {
        Ok(())
    }
}

fn index_item_from_contact(contact: &Contact) -> io::IoResult<String> {
    let name = match contact.component.single_prop("FN") {
        Some(name) => name.value_as_string(),
        None => return Err(io::IoError {
            kind: io::OtherIoError,
            desc: "No name found.",
            detail: None
        })
    };

    let emails = contact.component.all_props("EMAIL");
    let mut rv = String::new();
    for email in emails.iter() {
        rv.push_str(format!("{}\t{}\t{}\n", email.value_as_string(), name, contact.path.display()).as_slice());
    };
    Ok(rv)
}

pub fn cli_main() {
    let env = get_env();
    let mut args = os::args().into_iter();
    let program = args.next().unwrap_or("mates".to_string());

    let help = format!("Usage: {} COMMAND
Commands:
    index:
        Rewrite/create the index.
    mutt-query <query>:
        Search for contact, output is usable for mutt's query_command.
    file-query <query>:
        Search for contact, return just the filename.
    email-query <query>:
        Search for contact, return \"name <email>\".
    add:
        Take mail from stdin, add sender to contacts. Print filename.
    edit <file-or-query>:
        Open contact (given by filepath or search-string) in $MATES_EDITOR. If
        the file is cleared, the contact is removed.", program);

    let print_help = |&:| {
        println!("{}", help);
    };

    let command = args.next().unwrap_or("".to_string());

    match command.as_slice() {
        "index" => {
            let index_file = expect_env(&env, "MATES_INDEX");
            let mates_dir = expect_env(&env, "MATES_DIR");
            println!("Rebuilding index file \"{}\"...", index_file);
            main_try!(build_index(
                &Path::new(index_file.as_slice()),
                &Path::new(mates_dir.as_slice())
            ), "Failed to build index");
        },
        "mutt-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(mutt_query(&env, query.as_slice()), "Failed to execute grep");
        },
        "file-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(file_query(&env, query.as_slice()), "Failed to execute grep");
        },
        "email-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(email_query(&env, query.as_slice()), "Failed to execute grep");
        },
        "add" => {
            let index_file = Path::new(expect_env(&env, "MATES_INDEX"));
            let mates_dir = Path::new(expect_env(&env, "MATES_DIR"));
            let contact = main_try!(add_contact(&mates_dir), "Failed to add contact");
            println!("{}", contact.path.display());

            let mut index_fp = main_try!(io::File::open_mode(
                &index_file,
                io::Append,
                io::Write),
                "Failed to open index"
            );

            let index_entry = main_try!(index_item_from_contact(&contact), "Failed to generate index");
            main_try!(index_fp.write_str(index_entry.as_slice()), "Failed to write to index");
        },
        "edit" => {
            let query = args.next().unwrap_or("".to_string());
            let mates_dir = expect_env(&env, "MATES_DIR");
            main_try!(edit_contact(&env, query.as_slice(), mates_dir.as_slice()),
                      "Failed to edit contact");
        },
        _ => {
            print_help();
            if command != "help" && command != "--help" && command != "-h" {
                os::set_exit_status(1);
            }
        }
    };
}

fn add_contact(contact_dir: &Path) -> io::IoResult<Contact> {
    let stdin = try!(io::stdin().lock().read_to_string());
    let from_header = match read_sender_from_email(stdin.as_slice()) {
        Some(x) => x,
        None => return Err(io::IoError {
            kind: io::InvalidInput,
            desc: "Couldn't find From-header in email.",
            detail: None
        })
    };
    let (fullname, email) = parse_from_header(&from_header);
    let contact = Contact::generate(fullname, email, contact_dir);
    try!(contact.write_create());
    Ok(contact)
}

/// Return a tuple (fullname, email)
fn parse_from_header<'a>(s: &'a String) -> (Option<&'a str>, Option<&'a str>) {
    let mut split = s.rsplitn(1, ' ');
    let email = match split.next() {
        Some(x) => Some(x.trim_left_matches('<').trim_right_matches('>')),
        None => Some(s.as_slice())
    };
    let name = split.next();
    (name, email)
}

/// Given an email, return value of From header.
fn read_sender_from_email(email: &str) -> Option<String> {
    let mut parser = Rfc5322Parser::new(email);
    while !parser.eof() {
        match parser.consume_header() {
            Some(header) => {
                if header.name == "From" {
                    return header.get_value()
                };
            },
            None => return None
        };
    };
    None
}

fn edit_contact(env: &HashMap<String, String>, query: &str, mates_dir: &str) -> Result<(), String> {
    let editor_cmd = match env.get("MATES_EDITOR") {
        Some(x) => x.as_slice(),
        None => match env.get("EDITOR") {
            Some(x) => x.as_slice(),
            None => return Err("Either MATES_EDITOR or EDITOR has to be set.".to_string())
        }
    };

    let results = {
        if Path::new(mates_dir).join(query).exists() {
            vec![query.to_string()]
        } else {
            let results_iter = match index_query(env, query) {
                Ok(x) => x,
                Err(e) => return Err(format!("Error while fetching index: {}", e))
            };

            results_iter.filter_map(|x| {
                if x.filepath.len() > 0 {
                    Some(x.filepath)
                } else {
                    None
                }}).collect()
        }
    };

    if results.len() < 1 {
        return Err("No such contact.".to_string());
    } else if results.len() > 1 {
        for fname in results.iter() {
            println!("{}", fname);
        };
        return Err("Ambiguous query.".to_string());
    }

    let fpath = results[0].as_slice();
    let mut process = match io::Command::new("sh")
        .arg("-c")
        // clear stdin, http://unix.stackexchange.com/a/77593
        .arg(format!("$0 -- \"$1\" < $2"))
        .arg(editor_cmd.as_slice())
        .arg(fpath)
        .arg("/dev/tty")
        .stdin(io::process::InheritFd(0))
        .stdout(io::process::InheritFd(1))
        .stderr(io::process::InheritFd(2))
        .spawn() {
            Ok(x) => x,
            Err(e) => return Err(format!("Error while invoking editor: {}", e))
        };

    match process.wait() {
        Ok(_) => (),
        Err(e) => return Err(format!("Error while invoking editor: {}", e))
    };

    if match io::File::open(&Path::new(fpath)).read_to_string() {
        Ok(x) => x,
        Err(e) => return Err(format!("File can't be read after user edited it: {}", e))
    }.as_slice().trim().len() == 0 {
        return Err(format!("Contact emptied, file removed."));
    };

    Ok(())
}

fn mutt_query<'a>(env: &HashMap<String, String>, query: &str) -> io::IoResult<()> {
    println!("");  // For some reason mutt requires an empty line
    for item in try!(index_query(env, query)) {
        if item.email.len() > 0 && item.name.len() > 0 {
            println!("{}\t{}\t{}", item.email, item.name, item.filepath);
        };
    };
    Ok(())
}

fn file_query<'a>(env: &HashMap<String, String>, query: &str) -> io::IoResult<()> {
    for item in try!(index_query(env, query)) {
        if item.filepath.len() > 0 {
            println!("{}", item.filepath)
        };
    };
    Ok(())
}

fn email_query<'a>(env: &HashMap<String, String>, query: &str) -> io::IoResult<()> {
    for item in try!(index_query(env, query)) {
        if item.name.len() > 0 && item.email.len() > 0 {
            println!("{} <{}>", item.name, item.email);
        };
    };
    Ok(())
}

fn index_query<'a>(env: &HashMap<String, String>, query: &str) -> io::IoResult<IndexIterator<'a>> {
    let default_grep = "grep".to_owned();
    let grep_cmd = match env.get("MATES_GREP") {
        Some(x) => x,
        None => &default_grep
    };

    let index_path = Path::new(expect_env(env, "MATES_INDEX"));
    let mut process = try!(io::Command::new(grep_cmd.as_slice())
        .arg(query.as_slice())
        .stderr(io::process::InheritFd(2))
        .spawn());

    {
        let mut index_fp = try!(io::File::open(&index_path));
        let mut stdin = process.stdin.take().unwrap();
        try!(stdin.write_str(try!(index_fp.read_to_string()).as_slice()));
    }

    let stream = match process.stdout.as_mut() {
        Some(x) => x,
        None => return Err(io::IoError {
            kind: io::IoUnavailable,
            desc: "Failed to get stdout from grep process.",
            detail: None
        })
    };

    let output = try!(stream.read_to_string());
    Ok(IndexIterator::new(&output))
}

struct IndexItem<'a> {
    pub email: String,
    pub name: String,
    pub filepath: String
}

impl<'a> IndexItem<'a> {
    fn new(line: String) -> IndexItem<'a> {
        let mut parts = line.split('\t');

        IndexItem {
            email: parts.next().unwrap_or("").to_string(),
            name: parts.next().unwrap_or("").to_string(),
            filepath: parts.next().unwrap_or("").to_string()
        }
    }
}

struct IndexIterator<'a> {
    linebuffer: Vec<String>
}

impl<'a> IndexIterator<'a> {
    fn new(output: &String) -> IndexIterator<'a> {

        let rv = output.split('\n').map(|x: &str| x.to_string()).collect();
        IndexIterator {
            linebuffer: rv
        }
    }
}

impl<'a> Iterator for IndexIterator<'a> {
    type Item = IndexItem<'a>;

    fn next(&mut self) -> Option<IndexItem<'a>> {
        match self.linebuffer.pop() {
            Some(x) => Some(IndexItem::new(x)),
            None => None
        }
    }
}

struct Contact {
    pub component: Component,
    pub path: Path
}

impl Contact {
    pub fn from_file(path: Path) -> io::IoResult<Contact> {
        let mut contact_file = try!(io::File::open(&path));
        let contact_string = try!(contact_file.read_to_string());
        let item = match parse_component(contact_string.as_slice()) {
            Ok(x) => x,
            Err(e) => return Err(io::IoError {
                kind: io::OtherIoError,
                desc: "Error while parsing contact",
                detail: Some(e)
            })
        };
        Ok(Contact { component: item, path: path })
    }

    pub fn generate(fullname: Option<&str>, email: Option<&str>, dir: &Path) -> Contact {
        let (uid, contact_path) = {
            let mut uid;
            let mut contact_path;
            loop {
                uid = Uuid::new_v4().to_simple_string();
                contact_path = dir.join(Path::new(format!("{}.vcf", uid)));
                if !contact_path.exists() {
                    break
                }
            };
            (uid, contact_path)
        };
        Contact { path: contact_path, component: generate_component(uid, fullname, email) }
    }

    pub fn write_create(&self) -> io::IoResult<()> {
        let string = write_component(&self.component);
        let mut fp = try!(io::File::create(&self.path));
        fp.write_str(string.as_slice())
    }
}

fn generate_component(uid: String, fullname: Option<&str>, email: Option<&str>) -> Component {
    let mut comp = Component::new("VCARD");

    match fullname {
        Some(x) => comp.all_props_mut("FN").push(Property::new(x)),
        None => ()
    };

    match email {
        Some(x) => comp.all_props_mut("EMAIL").push(Property::new(x)),
        None => ()
    };
    comp.all_props_mut("UID").push(Property::new(uid.as_slice()));
    comp
}
<commit_msg>Add proper config obj<commit_after>use std::os;
use std::collections::HashMap;
use std::io;
use std::io::fs::PathExtensions;
use std::borrow::ToOwned;

use vobject::{Component,Property,parse_component,write_component};
use email::rfc5322::Rfc5322Parser;
use uuid::Uuid;

macro_rules! main_try {
    ($result: expr, $errmsg: expr) => (
        match $result {
            Ok(m) => m,
            Err(e) => {
                println!("{}: {}", $errmsg, e);
                os::set_exit_status(1);
                return;
            }
        }
    )
}

struct Configuration {
    index_path: Path,
    vdir_path: Path,
    editor_cmd: String,
    grep_cmd: String
}

impl Configuration {
    fn from_env(env: Vec<(String, String)>) -> Result<Configuration, String> {
        let mut dict = HashMap::new();
        dict.extend(env.into_iter().filter(|&(_, ref v)| v.len() > 0));
        Ok(Configuration {
            index_path: match dict.remove("MATES_INDEX") {
                Some(x) => Path::new(x),
                None => os::make_absolute(&Path::new("~/.mates_index")).unwrap()
            },
            vdir_path: match dict.remove("MATES_DIR") {
                Some(x) => Path::new(x),
                None => return Err("MATES_DIR must be set to your vdir path (directory of vcf-files).".to_owned())
            },
            editor_cmd: match dict.remove("MATES_EDITOR") {
                Some(x) => x,
                None => match dict.remove("EDITOR") {
                    Some(x) => x,
                    None => return Err("MATES_EDITOR or EDITOR must be set.".to_owned())
                }
            },
            grep_cmd: match dict.remove("MATES_GREP") {
                Some(x) => x,
                None => "grep".to_owned()
            }
        })
    }

    fn new() -> Result<Configuration, String> {
        Configuration::from_env(os::env())
    }
}

fn build_index(outfile: &Path, dir: &Path) -> io::IoResult<()> {
    if !dir.is_dir() {
        return Err(io::IoError {
            kind: io::MismatchedFileTypeForOperation,
            desc: "MATES_DIR must be a directory.",
            detail: None
        });
    };

    let mut outf = io::File::create(outfile);
    let entries = try!(io::fs::readdir(dir));
    let mut errors = false;

    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }

        let contact = match Contact::from_file(entry.clone()) {
            Ok(x) => x,
            Err(e) => {
                println!("Error while reading {}: {}", entry.display(), e);
                errors = true;
                continue
            }
        };

        match index_item_from_contact(&contact) {
            Ok(index_string) => {
                try!(outf.write_str(index_string.as_slice()));
            },
            Err(e) => {
                println!("Error while indexing {}: {}", entry.display(), e);
                errors = true;
                continue
            }
        };
    };

    if errors {
        Err(io::IoError {
            kind: io::OtherIoError,
            desc: "Several errors happened while generating the index.",
            detail: None
        })
    } else {
        Ok(())
    }
}

fn index_item_from_contact(contact: &Contact) -> io::IoResult<String> {
    let name = match contact.component.single_prop("FN") {
        Some(name) => name.value_as_string(),
        None => return Err(io::IoError {
            kind: io::OtherIoError,
            desc: "No name found.",
            detail: None
        })
    };

    let emails = contact.component.all_props("EMAIL");
    let mut rv = String::new();
    for email in emails.iter() {
        rv.push_str(format!("{}\t{}\t{}\n", email.value_as_string(), name, contact.path.display()).as_slice());
    };
    Ok(rv)
}

pub fn cli_main() {
    let mut args = os::args().into_iter();
    let program = args.next().unwrap_or("mates".to_string());

    let help = format!("Usage: {} COMMAND
Commands:
    index:
        Rewrite/create the index.
    mutt-query <query>:
        Search for contact, output is usable for mutt's query_command.
    file-query <query>:
        Search for contact, return just the filename.
    email-query <query>:
        Search for contact, return \"name <email>\".
    add:
        Take mail from stdin, add sender to contacts. Print filename.
    edit <file-or-query>:
        Open contact (given by filepath or search-string) in $MATES_EDITOR. If
        the file is cleared, the contact is removed.", program);

    let print_help = |&:| {
        println!("{}", help);
    };

    let command = match args.next() {
        Some(x) => x,
        None => {
            print_help();
            os::set_exit_status(1);
            return;
        }
    };

    if command == "--help" || command == "help" || command == "-h" {
        print_help();
        return;
    }

    let config = match Configuration::new() {
        Ok(x) => x,
        Err(e) => {
            println!("Error while reading configuration: {}", e);
            os::set_exit_status(1);
            return;
        }
    };

    match command.as_slice() {
        "index" => {
            println!("Rebuilding index file \"{}\"...", config.index_path.display());
            main_try!(build_index(&config.index_path, &config.vdir_path), "Failed to build index");
        },
        "mutt-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(mutt_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "file-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(file_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "email-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(email_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "add" => {
            let contact = main_try!(add_contact(&config.vdir_path), "Failed to add contact");
            println!("{}", contact.path.display());

            let mut index_fp = main_try!(io::File::open_mode(
                &config.index_path,
                io::Append,
                io::Write),
                "Failed to open index"
            );

            let index_entry = main_try!(index_item_from_contact(&contact), "Failed to generate index");
            main_try!(index_fp.write_str(index_entry.as_slice()), "Failed to write to index");
        },
        "edit" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(edit_contact(&config, query.as_slice()), "Failed to edit contact");
        },
        _ => {
            println!("Invalid command: {}", command);
            print_help();
            os::set_exit_status(1);
        }
    };
}

fn add_contact(contact_dir: &Path) -> io::IoResult<Contact> {
    let stdin = try!(io::stdin().lock().read_to_string());
    let from_header = match read_sender_from_email(stdin.as_slice()) {
        Some(x) => x,
        None => return Err(io::IoError {
            kind: io::InvalidInput,
            desc: "Couldn't find From-header in email.",
            detail: None
        })
    };
    let (fullname, email) = parse_from_header(&from_header);
    let contact = Contact::generate(fullname, email, contact_dir);
    try!(contact.write_create());
    Ok(contact)
}

/// Return a tuple (fullname, email)
fn parse_from_header<'a>(s: &'a String) -> (Option<&'a str>, Option<&'a str>) {
    let mut split = s.rsplitn(1, ' ');
    let email = match split.next() {
        Some(x) => Some(x.trim_left_matches('<').trim_right_matches('>')),
        None => Some(s.as_slice())
    };
    let name = split.next();
    (name, email)
}

/// Given an email, return value of From header.
fn read_sender_from_email(email: &str) -> Option<String> {
    let mut parser = Rfc5322Parser::new(email);
    while !parser.eof() {
        match parser.consume_header() {
            Some(header) => {
                if header.name == "From" {
                    return header.get_value()
                };
            },
            None => return None
        };
    };
    None
}

fn edit_contact(config: &Configuration, query: &str) -> Result<(), String> {

    let results = {
        if config.vdir_path.join(query).exists() {
            vec![query.to_string()]
        } else {
            let results_iter = match index_query(config, query) {
                Ok(x) => x,
                Err(e) => return Err(format!("Error while fetching index: {}", e))
            };

            results_iter.filter_map(|x| {
                if x.filepath.len() > 0 {
                    Some(x.filepath)
                } else {
                    None
                }}).collect()
        }
    };

    if results.len() < 1 {
        return Err("No such contact.".to_string());
    } else if results.len() > 1 {
        for fname in results.iter() {
            println!("{}", fname);
        };
        return Err("Ambiguous query.".to_string());
    }

    let fpath = results[0].as_slice();
    let mut process = match io::Command::new("sh")
        .arg("-c")
        // clear stdin, http://unix.stackexchange.com/a/77593
        .arg(format!("$0 -- \"$1\" < $2"))
        .arg(config.editor_cmd.as_slice())
        .arg(fpath)
        .arg("/dev/tty")
        .stdin(io::process::InheritFd(0))
        .stdout(io::process::InheritFd(1))
        .stderr(io::process::InheritFd(2))
        .spawn() {
            Ok(x) => x,
            Err(e) => return Err(format!("Error while invoking editor: {}", e))
        };

    match process.wait() {
        Ok(_) => (),
        Err(e) => return Err(format!("Error while invoking editor: {}", e))
    };

    if match io::File::open(&Path::new(fpath)).read_to_string() {
        Ok(x) => x,
        Err(e) => return Err(format!("File can't be read after user edited it: {}", e))
    }.as_slice().trim().len() == 0 {
        return Err(format!("Contact emptied, file removed."));
    };

    Ok(())
}

fn mutt_query<'a>(config: &Configuration, query: &str) -> io::IoResult<()> {
    println!("");  // For some reason mutt requires an empty line
    for item in try!(index_query(config, query)) {
        if item.email.len() > 0 && item.name.len() > 0 {
            println!("{}\t{}\t{}", item.email, item.name, item.filepath);
        };
    };
    Ok(())
}

fn file_query<'a>(config: &Configuration, query: &str) -> io::IoResult<()> {
    for item in try!(index_query(config, query)) {
        if item.filepath.len() > 0 {
            println!("{}", item.filepath)
        };
    };
    Ok(())
}

fn email_query<'a>(config: &Configuration, query: &str) -> io::IoResult<()> {
    for item in try!(index_query(config, query)) {
        if item.name.len() > 0 && item.email.len() > 0 {
            println!("{} <{}>", item.name, item.email);
        };
    };
    Ok(())
}

fn index_query<'a>(config: &Configuration, query: &str) -> io::IoResult<IndexIterator<'a>> {
    let mut process = try!(io::Command::new(config.grep_cmd.as_slice())
        .arg(query.as_slice())
        .stderr(io::process::InheritFd(2))
        .spawn());

    {
        let mut index_fp = try!(io::File::open(&config.index_path));
        let mut stdin = process.stdin.take().unwrap();
        try!(stdin.write_str(try!(index_fp.read_to_string()).as_slice()));
    }

    let stream = match process.stdout.as_mut() {
        Some(x) => x,
        None => return Err(io::IoError {
            kind: io::IoUnavailable,
            desc: "Failed to get stdout from grep process.",
            detail: None
        })
    };

    let output = try!(stream.read_to_string());
    Ok(IndexIterator::new(&output))
}

struct IndexItem<'a> {
    pub email: String,
    pub name: String,
    pub filepath: String
}

impl<'a> IndexItem<'a> {
    fn new(line: String) -> IndexItem<'a> {
        let mut parts = line.split('\t');

        IndexItem {
            email: parts.next().unwrap_or("").to_string(),
            name: parts.next().unwrap_or("").to_string(),
            filepath: parts.next().unwrap_or("").to_string()
        }
    }
}

struct IndexIterator<'a> {
    linebuffer: Vec<String>
}

impl<'a> IndexIterator<'a> {
    fn new(output: &String) -> IndexIterator<'a> {

        let rv = output.split('\n').map(|x: &str| x.to_string()).collect();
        IndexIterator {
            linebuffer: rv
        }
    }
}

impl<'a> Iterator for IndexIterator<'a> {
    type Item = IndexItem<'a>;

    fn next(&mut self) -> Option<IndexItem<'a>> {
        match self.linebuffer.pop() {
            Some(x) => Some(IndexItem::new(x)),
            None => None
        }
    }
}

struct Contact {
    pub component: Component,
    pub path: Path
}

impl Contact {
    pub fn from_file(path: Path) -> io::IoResult<Contact> {
        let mut contact_file = try!(io::File::open(&path));
        let contact_string = try!(contact_file.read_to_string());
        let item = match parse_component(contact_string.as_slice()) {
            Ok(x) => x,
            Err(e) => return Err(io::IoError {
                kind: io::OtherIoError,
                desc: "Error while parsing contact",
                detail: Some(e)
            })
        };
        Ok(Contact { component: item, path: path })
    }

    pub fn generate(fullname: Option<&str>, email: Option<&str>, dir: &Path) -> Contact {
        let (uid, contact_path) = {
            let mut uid;
            let mut contact_path;
            loop {
                uid = Uuid::new_v4().to_simple_string();
                contact_path = dir.join(Path::new(format!("{}.vcf", uid)));
                if !contact_path.exists() {
                    break
                }
            };
            (uid, contact_path)
        };
        Contact { path: contact_path, component: generate_component(uid, fullname, email) }
    }

    pub fn write_create(&self) -> io::IoResult<()> {
        let string = write_component(&self.component);
        let mut fp = try!(io::File::create(&self.path));
        fp.write_str(string.as_slice())
    }
}

fn generate_component(uid: String, fullname: Option<&str>, email: Option<&str>) -> Component {
    let mut comp = Component::new("VCARD");

    match fullname {
        Some(x) => comp.all_props_mut("FN").push(Property::new(x)),
        None => ()
    };

    match email {
        Some(x) => comp.all_props_mut("EMAIL").push(Property::new(x)),
        None => ()
    };
    comp.all_props_mut("UID").push(Property::new(uid.as_slice()));
    comp
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::collections::{Dir2, List, ListIntro, Tree, monoid_of_tree, tree_of_list};
use adapton::trie::*;
use adapton::engine::*;
use std::rc::Rc;

mod add_dups {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(v: Vec<usize>, t: Trie<usize>) -> Trie<usize> {
        v.into_iter().fold(t, |acc, i| {
            let t = Trie::art(cell(name_of_usize(i), acc));
            let t = Trie::name(name_of_usize(i), t);
            Trie::extend(name_unit(), t, i)
        })
    }

    fn bench_naive_add_dups() {
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            naive_input = doit(v.clone(), naive_input.clone());
            let _ = SetElim::mem(&naive_input, i);
        }
    }

    fn bench_dcg_add_dups() {
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            dcg_input = doit(v.clone(), dcg_input.clone());
            let _ = SetElim::mem(&dcg_input, i);
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        // b.iter(|| bench_naive_add_dups())
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            b.iter(|| {
                naive_input = doit(v.clone(), naive_input.clone());
                SetElim::mem(&naive_input, i)
            });
            naive_input = doit(v.clone(), naive_input.clone());
            // b.iter(|| SetElim::mem(&naive_input, i));
        }
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        // b.iter(|| bench_dcg_add_dups())
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            b.iter(|| {
                dcg_input = doit(v.clone(), dcg_input.clone());
                SetElim::mem(&dcg_input, i)
            });
            dcg_input = doit(v.clone(), dcg_input.clone());
            // b.iter(|| SetElim::mem(&dcg_input, i));
        }
    }
}

mod sum_fold {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(t: Trie<usize>) -> usize {
        SetElim::fold(t, 0, Rc::new(|i, acc| i + acc))
    }

    fn push_input(i: usize, t: Trie<usize>) -> Trie<usize> {
        let t = Trie::art(cell(name_of_usize(i), t));
        let t = Trie::name(name_of_usize(i), t);
        Trie::extend(name_unit(), t, i)
    }

    fn bench_naive_fold() {
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            naive_input = push_input(i, naive_input);
            let _ = doit(naive_input.clone());
        }
    }

    fn bench_dcg_fold() {
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            dcg_input = push_input(i, dcg_input);
            let _ = doit(dcg_input.clone());
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        // b.iter(|| bench_naive_fold(b)
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            naive_input = push_input(i, naive_input);
            b.iter(|| doit(naive_input.clone()))
        }
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        // b.iter(|| bench_dcg_fold())
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            dcg_input = push_input(i, dcg_input);
            b.iter(|| doit(dcg_input.clone()))
        }
    }
}

mod tree_benchmarks {
    use super::*;

    fn sum_tree(l:List<usize>) -> usize {
        let t = ns(name_of_str("tree_of_list"),
                   ||tree_of_list::<_,_,Tree<_>,_>(Dir2::Left, l));
        ns(name_of_str("monoid_of_tree"),
           || monoid_of_tree(t, 0, Rc::new(|x, y| x + y)))
    }

    fn push_list(i: usize, l: List<usize>) -> List<usize> {
        let l = List::art(cell(name_of_usize(i), l));
        let l = List::name(name_of_usize(i), l);
        List::cons(i, l)
    }

    #[bench]
    fn benchmark_naive_tree(b: &mut Bencher) {
        init_naive();
        let mut naive_input: List<usize> = List::nil();

        for i in (1..100).into_iter() {
            naive_input = push_list(i, naive_input);
            b.iter(|| sum_tree(naive_input.clone()))
        }
    }

    #[bench]
    fn benchmark_dcg_tree(b: &mut Bencher) {
        init_dcg();
        let mut dcg_input: List<usize> = List::nil();

        for i in (1..100).into_iter() {
            dcg_input = push_list(i, dcg_input);
            b.iter(|| sum_tree(dcg_input.clone()))
        }
    }
}
<commit_msg>Refactor benchmarks<commit_after>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::collections::{Dir2, List, ListIntro, Tree, monoid_of_tree, tree_of_list};
use adapton::trie::*;
use adapton::engine::*;
use std::rc::Rc;

mod list_input {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(l: List<usize>) -> usize {
        let t = trie_of_list::<_,Trie<_>,_>(l);
        SetElim::fold(t, 0, Rc::new(|i, acc| i + acc))
    }

    fn push_input(i: usize, l: List<usize>) -> List<usize> {
        let l = List::art(cell(name_of_usize(i), l));
        let l = List::name(name_of_usize(i), l);
        List::cons(i, l)
    }

    fn run_bench(b: &mut Bencher) {
        let mut input: List<usize> = List::nil();

        for i in (1..100).into_iter() {
            input = push_input(i, input);
            b.iter(|| doit(input.clone()))
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        init_naive();
        run_bench(b);
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        init_dcg();
        run_bench(b);
    }
}

mod trie_input {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(t: Trie<usize>) -> usize {
        SetElim::fold(t, 0, Rc::new(|i, acc| i + acc))
    }

    fn push_input(i: usize, t: Trie<usize>) -> Trie<usize> {
        let t = Trie::art(cell(name_of_usize(i), t));
        let t = Trie::name(name_of_usize(i), t);
        Trie::extend(name_unit(), t, i)
    }

    fn run_bench(b: &mut Bencher) {
        let mut input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            input = push_input(i, input);
            b.iter(|| doit(input.clone()))
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        init_naive();
        run_bench(b);
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        init_dcg();
        run_bench(b);
    }
}

mod tree_benchmarks {
    use super::*;

    fn sum_tree(l:List<usize>) -> usize {
        let t = ns(name_of_str("tree_of_list"),
                   ||tree_of_list::<_,_,Tree<_>,_>(Dir2::Left, l));
        ns(name_of_str("monoid_of_tree"),
           || monoid_of_tree(t, 0, Rc::new(|x, y| x + y)))
    }

    fn push_list(i: usize, l: List<usize>) -> List<usize> {
        let l = List::art(cell(name_of_usize(i), l));
        let l = List::name(name_of_usize(i), l);
        List::cons(i, l)
    }

    #[bench]
    fn benchmark_naive_tree(b: &mut Bencher) {
        init_naive();
        let mut naive_input: List<usize> = List::nil();

        for i in (1..100).into_iter() {
            naive_input = push_list(i, naive_input);
            b.iter(|| sum_tree(naive_input.clone()))
        }
    }

    #[bench]
    fn benchmark_dcg_tree(b: &mut Bencher) {
        init_dcg();
        let mut dcg_input: List<usize> = List::nil();

        for i in (1..100).into_iter() {
            dcg_input = push_list(i, dcg_input);
            b.iter(|| sum_tree(dcg_input.clone()))
        }
    }
}
<|endoftext|>


<commit_before>pub static RUSTUP_HELP: &'static str =
r"DISCUSSION:
    rustup installs The Rust Programming Language from the official
    release channels, enabling you to easily switch between stable,
    beta, and nightly compilers and keep them updated. It makes
    cross-compiling simpler with binary builds of the standard library
    for common platforms.

    If you are new to Rust consider running `rustup doc --book` to
    learn Rust.";

pub static SHOW_HELP: &'static str =
r"DISCUSSION:
    Shows the name of the active toolchain and the version of `rustc`.

    If the active toolchain has installed support for additional
    compilation targets, then they are listed as well.

    If there are multiple toolchains installed then all installed
    toolchains are listed as well.";

pub static UPDATE_HELP: &'static str =
r"DISCUSSION:
    With no toolchain specified, the `update` command updates each of
    the installed toolchains from the official release channels, then
    updates rustup itself.

    If given a toolchain argument then `update` updates that
    toolchain, the same as `rustup toolchain install`.

    'toolchain' specifies a toolchain name, such as 'stable',
    'nightly', or '1.8.0'. For more information see `rustup help
    toolchain`.";

pub static INSTALL_HELP: &'static str =
r"DISCUSSION:
    Installs a specific rust toolchain.

    The 'install' command is an alias for 'rustup update <toolchain>'.

    'toolchain' specifies a toolchain name, such as 'stable',
    'nightly', or '1.8.0'. For more information see `rustup help
    toolchain`.";

pub static DEFAULT_HELP: &'static str =
r"DISCUSSION:
    Sets the default toolchain to the one specified. If the toolchain
    is not already installed then it is installed first.";

pub static TOOLCHAIN_HELP: &'static str =
r"DISCUSSION:
    Many `rustup` commands deal with *toolchains*, a single
    installation of the Rust compiler. `rustup` supports multiple
    types of toolchains. The most basic track the official release
    channels: 'stable', 'beta' and 'nightly'; but `rustup` can also
    install toolchains from the official archives, for alternate host
    platforms, and from local builds.

    Standard release channel toolchain names have the following form:

        <channel>[-<date>][-<host>]

        <channel>       = stable|beta|nightly|<version>
        <date>          = YYYY-MM-DD
        <host>          = <target-triple>

    'channel' is either a named release channel or an explicit version
    number, such as '1.8.0'. Channel names can be optionally appended
    with an archive date, as in 'nightly-2017-05-09', in which case
    the toolchain is downloaded from the archive for that date.

    Finally, the host may be specified as a target triple. This is
    most useful for installing a 32-bit compiler on a 64-bit platform,
    or for installing the [MSVC-based toolchain] on Windows. For
    example:

        rustup toolchain install stable-x86_64-pc-windows-msvc

    For convenience, elements of the target triple that are omitted
    will be inferred, so the above could be written:

        $ rustup default stable-msvc

    rustup can also manage symlinked local toolchain builds, which are
    often used to for developing Rust itself. For more information see
    `rustup toolchain help link`.";

pub static TOOLCHAIN_LINK_HELP: &'static str =
r"DISCUSSION:
    'toolchain' is the custom name to be assigned to the new toolchain.
    Any name is permitted as long as it does not fully match an initial
    substring of a standard release channel. For example, you can use
    the names 'latest' or '2017-04-01' but you cannot use 'stable' or
    'beta-i686' or 'nightly-x86_64-unknown-linux-gnu'.

    'path' specifies the directory where the binaries and libraries for
    the custom toolchain can be found. For example, when used for
    development of Rust itself, toolchains can be linked directly out of
    the build directory. After building, you can test out different
    compiler versions as follows:

        $ rustup toolchain link latest-stage1 build/x86_64-unknown-linux-gnu/stage1
        $ rustup override set latest-stage1

    If you now compile a crate in the current directory, the custom
    toolchain 'latest-stage1' will be used.";

pub static OVERRIDE_HELP: &'static str =
r"DISCUSSION:
    Overrides configure rustup to use a specific toolchain when
    running in a specific directory.

    Directories can be assigned their own Rust toolchain with `rustup
    override`. When a directory has an override then any time `rustc`
    or `cargo` is run inside that directory, or one of its child
    directories, the override toolchain will be invoked.

    To pin to a specific nightly:

        rustup override set nightly-2014-12-18

    Or a specific stable release:

        rustup override set 1.0.0

    To see the active toolchain use `rustup show`. To remove the
    override and use the default toolchain again, `rustup override
    unset`.";

pub static OVERRIDE_UNSET_HELP: &'static str =
r"DISCUSSION:
    If `--path` argument is present, removes the override toolchain
    for the specified directory. If `--nonexistent` argument is
    present, removes the override toolchain for all nonexistent
    directories. Otherwise, removes the override toolchain for the
    current directory.";

pub static RUN_HELP: &'static str =
r"DISCUSSION:
    Configures an environment to use the given toolchain and then runs
    the specified program. The command may be any program, not just
    rustc or cargo. This can be used for testing arbitrary toolchains
    without setting an override.

    Commands explicitly proxied by `rustup` (such as `rustc` and
    `cargo`) also have a shorthand for this available. The toolchain
    can be set by using `+toolchain` as the first argument. These are
    equivalent:

        cargo +nightly build

        rustup run nightly cargo build";

pub static DOC_HELP: &'static str =
r"DISCUSSION:
    Opens the documentation for the currently active toolchain with
    the default browser.

    By default, it opens the documentation index. Use the various
    flags to open specific pieces of documentation.";

pub static COMPLETIONS_HELP: &'static str =
r"DISCUSSION:
    One can generate a completion script for `rustup` that is
    compatible with a given shell. The script is output on `stdout`
    allowing one to re-direct the output to the file of their
    choosing. Where you place the file will depend on which shell, and
    which operating system you are using. Your particular
    configuration may also determine where these scripts need to be
    placed.

    Here are some common set ups for the three supported shells under
    Unix and similar operating systems (such as GNU/Linux).

    BASH:

    Completion files are commonly stored in `/etc/bash_completion.d/`

    Run the command:

    `rustup completions bash > /etc/bash_completion.d/rustup.bash-completion`

    This installs the completion script. You may have to log out and
    log back in to your shell session for the changes to take affect.

    BASH (macOS/Homebrew):

    Homebrew stores bash completion files within the Homebrew directory.
    With the `bash-completion` brew formula installed, run the command:

    `rustup completions bash > $(brew --prefix)/etc/bash_completion.d/rustup.bash-completion`

    FISH:

    Fish completion files are commonly stored in
    `$HOME/.config/fish/completions`

    Run the command:
    `rustup completions fish > ~/.config/fish/completions/rustup.fish`

    This installs the completion script. You may have to log out and
    log back in to your shell session for the changes to take affect.

    ZSH:

    ZSH completions are commonly stored in any directory listed in
    your `$fpath` variable. To use these completions, you must either
    add the generated script to one of those directories, or add your
    own to this list.

    Adding a custom directory is often the safest bet if you are
    unsure of which directory to use. First create the directory; for
    this example we'll create a hidden directory inside our `$HOME`
    directory

    `mkdir ~/.zfunc`

    Then add the following lines to your `.zshrc` just before
    `compinit`

    `fpath+=~/.zfunc`

    Now you can install the completions script using the following
    command

    `rustup completions zsh > ~/.zfunc/_rustup`

    You must then either log out and log back in, or simply run

    `exec zsh`

    For the new completions to take affect.

    CUSTOM LOCATIONS:

    Alternatively, you could save these files to the place of your
    choosing, such as a custom directory inside your $HOME. Doing so
    will require you to add the proper directives, such as `source`ing
    inside your login script. Consult your shells documentation for
    how to add such directives.

    POWERSHELL:

    The powershell completion scripts require PowerShell v5.0+ (which
    comes Windows 10, but can be downloaded separately for windows 7
    or 8.1).

    First, check if a profile has already been set

    `PS C:\> Test-Path $profile`

    If the above command returns `False` run the following

    `PS C:\> New-Item -path $profile -type file -force`

    Now open the file provided by `$profile` (if you used the
    `New-Item` command it will be
    `%USERPROFILE%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`

    Next, we either save the completions file into our profile, or
    into a separate file and source it inside our profile. To save the
    completions into our profile simply use

    `PS C:\> rustup completions powershell >> %USERPROFILE%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`";
<commit_msg>Consistently prefix commands with $<commit_after>pub static RUSTUP_HELP: &'static str =
r"DISCUSSION:
    rustup installs The Rust Programming Language from the official
    release channels, enabling you to easily switch between stable,
    beta, and nightly compilers and keep them updated. It makes
    cross-compiling simpler with binary builds of the standard library
    for common platforms.

    If you are new to Rust consider running `rustup doc --book` to
    learn Rust.";

pub static SHOW_HELP: &'static str =
r"DISCUSSION:
    Shows the name of the active toolchain and the version of `rustc`.

    If the active toolchain has installed support for additional
    compilation targets, then they are listed as well.

    If there are multiple toolchains installed then all installed
    toolchains are listed as well.";

pub static UPDATE_HELP: &'static str =
r"DISCUSSION:
    With no toolchain specified, the `update` command updates each of
    the installed toolchains from the official release channels, then
    updates rustup itself.

    If given a toolchain argument then `update` updates that
    toolchain, the same as `rustup toolchain install`.

    'toolchain' specifies a toolchain name, such as 'stable',
    'nightly', or '1.8.0'. For more information see `rustup help
    toolchain`.";

pub static INSTALL_HELP: &'static str =
r"DISCUSSION:
    Installs a specific rust toolchain.

    The 'install' command is an alias for 'rustup update <toolchain>'.

    'toolchain' specifies a toolchain name, such as 'stable',
    'nightly', or '1.8.0'. For more information see `rustup help
    toolchain`.";

pub static DEFAULT_HELP: &'static str =
r"DISCUSSION:
    Sets the default toolchain to the one specified. If the toolchain
    is not already installed then it is installed first.";

pub static TOOLCHAIN_HELP: &'static str =
r"DISCUSSION:
    Many `rustup` commands deal with *toolchains*, a single
    installation of the Rust compiler. `rustup` supports multiple
    types of toolchains. The most basic track the official release
    channels: 'stable', 'beta' and 'nightly'; but `rustup` can also
    install toolchains from the official archives, for alternate host
    platforms, and from local builds.

    Standard release channel toolchain names have the following form:

        <channel>[-<date>][-<host>]

        <channel>       = stable|beta|nightly|<version>
        <date>          = YYYY-MM-DD
        <host>          = <target-triple>

    'channel' is either a named release channel or an explicit version
    number, such as '1.8.0'. Channel names can be optionally appended
    with an archive date, as in 'nightly-2017-05-09', in which case
    the toolchain is downloaded from the archive for that date.

    Finally, the host may be specified as a target triple. This is
    most useful for installing a 32-bit compiler on a 64-bit platform,
    or for installing the [MSVC-based toolchain] on Windows. For
    example:

        $ rustup toolchain install stable-x86_64-pc-windows-msvc

    For convenience, elements of the target triple that are omitted
    will be inferred, so the above could be written:

        $ rustup default stable-msvc

    rustup can also manage symlinked local toolchain builds, which are
    often used to for developing Rust itself. For more information see
    `rustup toolchain help link`.";

pub static TOOLCHAIN_LINK_HELP: &'static str =
r"DISCUSSION:
    'toolchain' is the custom name to be assigned to the new toolchain.
    Any name is permitted as long as it does not fully match an initial
    substring of a standard release channel. For example, you can use
    the names 'latest' or '2017-04-01' but you cannot use 'stable' or
    'beta-i686' or 'nightly-x86_64-unknown-linux-gnu'.

    'path' specifies the directory where the binaries and libraries for
    the custom toolchain can be found. For example, when used for
    development of Rust itself, toolchains can be linked directly out of
    the build directory. After building, you can test out different
    compiler versions as follows:

        $ rustup toolchain link latest-stage1 build/x86_64-unknown-linux-gnu/stage1
        $ rustup override set latest-stage1

    If you now compile a crate in the current directory, the custom
    toolchain 'latest-stage1' will be used.";

pub static OVERRIDE_HELP: &'static str =
r"DISCUSSION:
    Overrides configure rustup to use a specific toolchain when
    running in a specific directory.

    Directories can be assigned their own Rust toolchain with `rustup
    override`. When a directory has an override then any time `rustc`
    or `cargo` is run inside that directory, or one of its child
    directories, the override toolchain will be invoked.

    To pin to a specific nightly:

        $ rustup override set nightly-2014-12-18

    Or a specific stable release:

        $ rustup override set 1.0.0

    To see the active toolchain use `rustup show`. To remove the
    override and use the default toolchain again, `rustup override
    unset`.";

pub static OVERRIDE_UNSET_HELP: &'static str =
r"DISCUSSION:
    If `--path` argument is present, removes the override toolchain
    for the specified directory. If `--nonexistent` argument is
    present, removes the override toolchain for all nonexistent
    directories. Otherwise, removes the override toolchain for the
    current directory.";

pub static RUN_HELP: &'static str =
r"DISCUSSION:
    Configures an environment to use the given toolchain and then runs
    the specified program. The command may be any program, not just
    rustc or cargo. This can be used for testing arbitrary toolchains
    without setting an override.

    Commands explicitly proxied by `rustup` (such as `rustc` and
    `cargo`) also have a shorthand for this available. The toolchain
    can be set by using `+toolchain` as the first argument. These are
    equivalent:

        $ cargo +nightly build

        $ rustup run nightly cargo build";

pub static DOC_HELP: &'static str =
r"DISCUSSION:
    Opens the documentation for the currently active toolchain with
    the default browser.

    By default, it opens the documentation index. Use the various
    flags to open specific pieces of documentation.";

pub static COMPLETIONS_HELP: &'static str =
r"DISCUSSION:
    One can generate a completion script for `rustup` that is
    compatible with a given shell. The script is output on `stdout`
    allowing one to re-direct the output to the file of their
    choosing. Where you place the file will depend on which shell, and
    which operating system you are using. Your particular
    configuration may also determine where these scripts need to be
    placed.

    Here are some common set ups for the three supported shells under
    Unix and similar operating systems (such as GNU/Linux).

    BASH:

    Completion files are commonly stored in `/etc/bash_completion.d/`

    Run the command:

        $ rustup completions bash > /etc/bash_completion.d/rustup.bash-completion

    This installs the completion script. You may have to log out and
    log back in to your shell session for the changes to take affect.

    BASH (macOS/Homebrew):

    Homebrew stores bash completion files within the Homebrew directory.
    With the `bash-completion` brew formula installed, run the command:

        $ rustup completions bash > $(brew --prefix)/etc/bash_completion.d/rustup.bash-completion

    FISH:

    Fish completion files are commonly stored in
    `$HOME/.config/fish/completions`

    Run the command:

        $ rustup completions fish > ~/.config/fish/completions/rustup.fish

    This installs the completion script. You may have to log out and
    log back in to your shell session for the changes to take affect.

    ZSH:

    ZSH completions are commonly stored in any directory listed in
    your `$fpath` variable. To use these completions, you must either
    add the generated script to one of those directories, or add your
    own to this list.

    Adding a custom directory is often the safest bet if you are
    unsure of which directory to use. First create the directory; for
    this example we'll create a hidden directory inside our `$HOME`
    directory

        $ mkdir ~/.zfunc

    Then add the following lines to your `.zshrc` just before
    `compinit`

    `fpath+=~/.zfunc`

    Now you can install the completions script using the following
    command

        $ rustup completions zsh > ~/.zfunc/_rustup

    You must then either log out and log back in, or simply run

        $ exec zsh

    For the new completions to take affect.

    CUSTOM LOCATIONS:

    Alternatively, you could save these files to the place of your
    choosing, such as a custom directory inside your $HOME. Doing so
    will require you to add the proper directives, such as `source`ing
    inside your login script. Consult your shells documentation for
    how to add such directives.

    POWERSHELL:

    The powershell completion scripts require PowerShell v5.0+ (which
    comes Windows 10, but can be downloaded separately for windows 7
    or 8.1).

    First, check if a profile has already been set

        PS C:\> Test-Path $profile

    If the above command returns `False` run the following

        PS C:\> New-Item -path $profile -type file -force

    Now open the file provided by `$profile` (if you used the
    `New-Item` command it will be
    `%USERPROFILE%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`

    Next, we either save the completions file into our profile, or
    into a separate file and source it inside our profile. To save the
    completions into our profile simply use

        PS C:\> rustup completions powershell >> %USERPROFILE%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1";
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;$cpu_state.S = $val >= 0x80 );
}

macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 | $cpu_state.sp,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=1;
            $mem.read8(0x0100 | $cpu_state.sp;)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc-1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {},
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.decode_register.value_final.unwrap()));
                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<commit_msg>implmented CLC opcode<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;$cpu_state.S = $val >= 0x80 );
}

macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 | $cpu_state.sp,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=1;
            $mem.read8(0x0100 | $cpu_state.sp;)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
    		},
    		OpcodeClass::CLC => {
                cpu_state.C = false;
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc-1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {
            },
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.decode_register.value_final.unwrap()));
                cpu_state.pc = cpu_state.pc + (cpu_state.decode_register.info.len as u16 -1);
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => (set_z!($cpu_state,$val);set_s!($cpu_state,$val););
}
macro_rules! set_z {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;);
}
macro_rules! set_s {
    ($cpu_state:expr,$val:expr) => ($cpu_state.S = $val >= 0x80;);
}
macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 | $cpu_state.sp,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=1;
            $mem.read8(0x0100 | $cpu_state.sp;)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BCC => {
                if !cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BEQ => {
                if cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BIT => {
                cpu_state.V = (cpu_state.decode_register.value_final.unwrap() >> 6) & 1 > 0;
                set_z!(cpu_state,cpu_state.decode_register.value_final.unwrap() & cpu_state.a);
                set_s!(cpu_state,cpu_state.decode_register.value_final.unwrap());

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BNE => {
                if !cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BVS => {
                if cpu_state.V { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::CLC => {
                cpu_state.C = false;
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc-1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {
            },
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::STA => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.a));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.x));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<commit_msg>implemented BVC opcode<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => (set_z!($cpu_state,$val);set_s!($cpu_state,$val););
}
macro_rules! set_z {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;);
}
macro_rules! set_s {
    ($cpu_state:expr,$val:expr) => ($cpu_state.S = $val >= 0x80;);
}
macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 | $cpu_state.sp,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=1;
            $mem.read8(0x0100 | $cpu_state.sp;)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BCC => {
                if !cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BEQ => {
                if cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BIT => {
                cpu_state.V = (cpu_state.decode_register.value_final.unwrap() >> 6) & 1 > 0;
                set_z!(cpu_state,cpu_state.decode_register.value_final.unwrap() & cpu_state.a);
                set_s!(cpu_state,cpu_state.decode_register.value_final.unwrap());

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BNE => {
                if !cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BVC => {
                if !cpu_state.V { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BVS => {
                if cpu_state.V { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::CLC => {
                cpu_state.C = false;
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc-1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {
            },
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::STA => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.a));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.x));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<|endoftext|>