<commit_before>// Copyright 2014 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use util::foreach_html5lib_test;

use std::{num, char};
use std::mem::replace;
use std::default::Default;
use std::path::Path;
use std::thunk::Thunk;
use test::{TestDesc, TestDescAndFn, DynTestName, DynTestFn};
use test::ShouldFail::No;
use serialize::json;
use serialize::json::Json;
use std::collections::BTreeMap;
use std::borrow::Cow::Borrowed;
use std::vec::IntoIter;

use html5ever::tokenizer::{Doctype, Attribute, StartTag, EndTag, Tag};
use html5ever::tokenizer::{Token, DoctypeToken, TagToken, CommentToken};
use html5ever::tokenizer::{CharacterTokens, NullCharacterToken, EOFToken, ParseError};
use html5ever::tokenizer::{TokenSink, Tokenizer, TokenizerOpts};
use html5ever::tokenizer::states::{Plaintext, RawData, Rcdata, Rawtext};

use string_cache::{Atom, QualName};

// Return all ways of splitting the string into at most n
// possibly-empty pieces.
fn splits(s: &str, n: uint) -> Vec<Vec<String>> {
    if n == 1 {
        return vec!(vec!(s.to_string()));
    }

    let mut points: Vec<uint> = s.char_indices().map(|(n,_)| n).collect();
    points.push(s.len());

    // do this with iterators?
    let mut out = vec!();
    for p in points.into_iter() {
        let y = &s[p..];
        for mut x in splits(&s[..p], n-1).into_iter() {
            x.push(y.to_string());
            out.push(x);
        }
    }

    out.extend(splits(s, n-1).into_iter());
    out
}

struct TokenLogger {
    tokens: Vec<Token>,
    current_str: String,
    exact_errors: bool,
}

impl TokenLogger {
    fn new(exact_errors: bool) -> TokenLogger {
        TokenLogger {
            tokens: vec!(),
            current_str: String::new(),
            exact_errors: exact_errors,
        }
    }

    // Push anything other than character tokens
    fn push(&mut self, token: Token) {
        self.finish_str();
        self.tokens.push(token);
    }

    fn finish_str(&mut self) {
        if self.current_str.len() > 0 {
            let s = replace(&mut self.current_str, String::new());
            self.tokens.push(CharacterTokens(s));
        }
    }

    fn get_tokens(mut self) -> Vec<Token> {
        self.finish_str();
        self.tokens
    }
}

impl TokenSink for TokenLogger {
    fn process_token(&mut self, token: Token) {
        match token {
            CharacterTokens(b) => {
                self.current_str.push_str(b.as_slice());
            }

            NullCharacterToken => {
                self.current_str.push('\0');
            }

            ParseError(_) => if self.exact_errors {
                self.push(ParseError(Borrowed("")));
            },

            TagToken(mut t) => {
                // The spec seems to indicate that one can emit
                // erroneous end tags with attrs, but the test
                // cases don't contain them.
                match t.kind {
                    EndTag => {
                        t.self_closing = false;
                        t.attrs = vec!();
                    }
                    _ => t.attrs.sort_by(|a1, a2| a1.name.cmp(&a2.name)),
                }
                self.push(TagToken(t));
            }

            EOFToken => (),

            _ => self.push(token),
        }
    }
}

fn tokenize(input: Vec<String>, opts: TokenizerOpts) -> Vec<Token> {
    let sink = TokenLogger::new(opts.exact_errors);
    let mut tok = Tokenizer::new(sink, opts);
    for chunk in input.into_iter() {
        tok.feed(chunk);
    }
    tok.end();
    tok.unwrap().get_tokens()
}

trait JsonExt {
    fn get_str(&self) -> String;
    fn get_nullable_str(&self) -> Option<String>;
    fn get_bool(&self) -> bool;
    fn get_obj<'t>(&'t self) -> &'t BTreeMap<String, Self>;
    fn get_list<'t>(&'t self) -> &'t Vec<Self>;
    fn find<'t>(&'t self, key: &str) -> &'t Self;
}

impl JsonExt for Json {
    fn get_str(&self) -> String {
        match *self {
            Json::String(ref s) => s.to_string(),
            _ => panic!("Json::get_str: not a String"),
        }
    }

    fn get_nullable_str(&self) -> Option<String> {
        match *self {
            Json::Null => None,
            Json::String(ref s) => Some(s.to_string()),
            _ => panic!("Json::get_nullable_str: not a String"),
        }
    }

    fn get_bool(&self) -> bool {
        match *self {
            Json::Boolean(b) => b,
            _ => panic!("Json::get_bool: not a Boolean"),
        }
    }

    fn get_obj<'t>(&'t self) -> &'t BTreeMap<String, Json> {
        match *self {
            Json::Object(ref m) => &*m,
            _ => panic!("Json::get_obj: not an Object"),
        }
    }

    fn get_list<'t>(&'t self) -> &'t Vec<Json> {
        match *self {
            Json::Array(ref m) => m,
            _ => panic!("Json::get_list: not an Array"),
        }
    }

    fn find<'t>(&'t self, key: &str) -> &'t Json {
        self.get_obj().get(&key.to_string()).unwrap()
    }
}

// Parse a JSON object (other than "ParseError") to a token.
fn json_to_token(js: &Json) -> Token {
    let parts = js.get_list();
    // Collect refs here so we don't have to use "ref" in all the patterns below.
    let args: Vec<&Json> = parts[1..].iter().collect();
    match (parts[0].get_str().as_slice(), args.as_slice()) {
        ("DOCTYPE", [name, public_id, system_id, correct]) => DoctypeToken(Doctype {
            name: name.get_nullable_str(),
            public_id: public_id.get_nullable_str(),
            system_id: system_id.get_nullable_str(),
            force_quirks: !correct.get_bool(),
        }),

        ("StartTag", [name, attrs, rest..]) => TagToken(Tag {
            kind: StartTag,
            name: Atom::from_slice(name.get_str().as_slice()),
            attrs: attrs.get_obj().iter().map(|(k,v)| {
                Attribute {
                    name: QualName::new(ns!(""), Atom::from_slice(k.as_slice())),
                    value: v.get_str()
                }
            }).collect(),
            self_closing: match rest {
                [ref b, ..] => b.get_bool(),
                _ => false,
            }
        }),

        ("EndTag", [name]) => TagToken(Tag {
            kind: EndTag,
            name: Atom::from_slice(name.get_str().as_slice()),
            attrs: vec!(),
            self_closing: false
        }),

        ("Comment", [txt]) => CommentToken(txt.get_str()),

        ("Character", [txt]) => CharacterTokens(txt.get_str()),

        // We don't need to produce NullCharacterToken because
        // the TokenLogger will convert them to CharacterTokens.

        _ => panic!("don't understand token {:?}", parts),
    }
}

// Parse the "output" field of the test case into a vector of tokens.
fn json_to_tokens(js: &Json, exact_errors: bool) -> Vec<Token> {
    // Use a TokenLogger so that we combine character tokens separated
    // by an ignored error.
    let mut sink = TokenLogger::new(exact_errors);
    for tok in js.get_list().iter() {
        match *tok {
            Json::String(ref s)
                if s.as_slice() == "ParseError" => sink.process_token(ParseError(Borrowed(""))),
            _ => sink.process_token(json_to_token(tok)),
        }
    }
    sink.get_tokens()
}

// Undo the escaping in "doubleEscaped" tests.
fn unescape(s: &str) -> Option<String> {
    let mut out = String::with_capacity(s.len());
    let mut it = s.chars().peekable();
    loop {
        match it.next() {
            None => return Some(out),
            Some('\\') => {
                if it.peek() != Some(&'u') {
                    panic!("can't understand escape");
                }
                drop(it.next());
                let hex: String = it.by_ref().take(4).collect();
                match num::from_str_radix(hex.as_slice(), 16)
                          .and_then(char::from_u32) {
                    // Some of the tests use lone surrogates, but we have no
                    // way to represent them in the UTF-8 input to our parser.
                    // Since these can only come from script, we will catch
                    // them there.
                    None => return None,
                    Some(c) => out.push(c),
                }
            }
            Some(c) => out.push(c),
        }
    }
}

fn unescape_json(js: &Json) -> Json {
    match *js {
        // unwrap is OK here because the spec'd *output* of the tokenizer never
        // contains a lone surrogate.
        Json::String(ref s) => Json::String(unescape(s.as_slice()).unwrap()),
        Json::Array(ref xs) => Json::Array(xs.iter().map(unescape_json).collect()),
        Json::Object(ref obj) => {
            let mut new_obj = BTreeMap::new();
            for (k,v) in obj.iter() {
                new_obj.insert(k.clone(), unescape_json(v));
            }
            Json::Object(new_obj)
        }
        _ => js.clone(),
    }
}

fn mk_test(desc: String, insplits: Vec<Vec<String>>, expect: Vec<Token>, opts: TokenizerOpts)
        -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(desc),
            ignore: false,
            should_fail: No,
        },
        testfn: DynTestFn(Thunk::new(move || {
            for input in insplits.into_iter() {
                // Clone 'input' so we have it for the failure message.
                // Also clone opts.  If we don't, we get the wrong
                // result but the compiler doesn't catch it!
                // Possibly mozilla/rust#12223.
                let output = tokenize(input.clone(), opts.clone());
                if output != expect {
                    panic!("\ninput: {:?}\ngot: {:?}\nexpected: {:?}",
                        input, output, expect);
                }
            }
        })),
    }
}

fn mk_tests(tests: &mut Vec<TestDescAndFn>, path_str: &str, js: &Json) {
    let obj = js.get_obj();
    let mut input = js.find("input").unwrap().get_str();
    let mut expect = js.find("output").unwrap().clone();
    let desc = format!("tok: {}: {}",
        path_str, js.find("description").unwrap().get_str());

    // "Double-escaped" tests require additional processing of
    // the input and output.
    if obj.get(&"doubleEscaped".to_string()).map_or(false, |j| j.get_bool()) {
        match unescape(input.as_slice()) {
            None => return,
            Some(i) => input = i,
        }
        expect = unescape_json(&expect);
    }

    // Split up the input at different points to test incremental tokenization.
    let insplits = splits(input.as_slice(), 3);

    // Some tests have a last start tag name.
    let start_tag = obj.get(&"lastStartTag".to_string()).map(|s| s.get_str());

    // Some tests want to start in a state other than Data.
    let state_overrides = match obj.get(&"initialStates".to_string()) {
        Some(&Json::Array(ref xs)) => xs.iter().map(|s|
            Some(match s.get_str().as_slice() {
                "PLAINTEXT state" => Plaintext,
                "RAWTEXT state"   => RawData(Rawtext),
                "RCDATA state"    => RawData(Rcdata),
                s => panic!("don't know state {}", s),
            })).collect(),
        None => vec!(None),
        _ => panic!("don't understand initialStates value"),
    };

    // Build the tests.
    for state in state_overrides.into_iter() {
        for &exact_errors in [false, true].iter() {
            let mut newdesc = desc.clone();
            match state {
                Some(s) => newdesc = format!("{} (in state {:?})", newdesc, s),
                None  => (),
            };
            if exact_errors {
                newdesc = format!("{} (exact errors)", newdesc);
            }

            let expect_toks = json_to_tokens(&expect, exact_errors);
            tests.push(mk_test(newdesc, insplits.clone(), expect_toks, TokenizerOpts {
                exact_errors: exact_errors,
                initial_state: state,
                last_start_tag_name: start_tag.clone(),

                // Not discarding a BOM is what the test suite expects; see
                // https://github.com/html5lib/html5lib-tests/issues/2
                discard_bom: false,

                .. Default::default()
            }));
        }
    }
}

pub fn tests(src_dir: Path) -> IntoIter<TestDescAndFn> {
    let mut tests = vec!();

    foreach_html5lib_test(src_dir, "tokenizer", ".test", |path_str, mut file| {
        let js = json::from_reader(&mut file as &mut Reader)
            .ok().expect("json parse error");

        match js.get_obj().get(&"tests".to_string()) {
            Some(&Json::Array(ref lst)) => {
                for test in lst.iter() {
                    mk_tests(&mut tests, path_str.as_slice(), test);
                }
            }

            // xmlViolation.test doesn't follow this format.
            _ => (),
        }
    });

    tests.into_iter()
}
<commit_msg>Speed up html5ever-external-test startup time<commit_after>// Copyright 2014 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use util::foreach_html5lib_test;

use std::{num, char};
use std::mem::replace;
use std::default::Default;
use std::path::Path;
use std::thunk::Thunk;
use test::{TestDesc, TestDescAndFn, DynTestName, DynTestFn};
use test::ShouldFail::No;
use serialize::json;
use serialize::json::Json;
use std::collections::BTreeMap;
use std::borrow::Cow::Borrowed;
use std::vec::IntoIter;

use html5ever::tokenizer::{Doctype, Attribute, StartTag, EndTag, Tag};
use html5ever::tokenizer::{Token, DoctypeToken, TagToken, CommentToken};
use html5ever::tokenizer::{CharacterTokens, NullCharacterToken, EOFToken, ParseError};
use html5ever::tokenizer::{TokenSink, Tokenizer, TokenizerOpts};
use html5ever::tokenizer::states::{Plaintext, RawData, Rcdata, Rawtext};

use string_cache::{Atom, QualName};

// Return all ways of splitting the string into at most n
// possibly-empty pieces.
fn splits(s: &str, n: uint) -> Vec<Vec<String>> {
    if n == 1 {
        return vec!(vec!(s.to_string()));
    }

    let mut points: Vec<uint> = s.char_indices().map(|(n,_)| n).collect();
    points.push(s.len());

    // do this with iterators?
    let mut out = vec!();
    for p in points.into_iter() {
        let y = &s[p..];
        for mut x in splits(&s[..p], n-1).into_iter() {
            x.push(y.to_string());
            out.push(x);
        }
    }

    out.extend(splits(s, n-1).into_iter());
    out
}

struct TokenLogger {
    tokens: Vec<Token>,
    current_str: String,
    exact_errors: bool,
}

impl TokenLogger {
    fn new(exact_errors: bool) -> TokenLogger {
        TokenLogger {
            tokens: vec!(),
            current_str: String::new(),
            exact_errors: exact_errors,
        }
    }

    // Push anything other than character tokens
    fn push(&mut self, token: Token) {
        self.finish_str();
        self.tokens.push(token);
    }

    fn finish_str(&mut self) {
        if self.current_str.len() > 0 {
            let s = replace(&mut self.current_str, String::new());
            self.tokens.push(CharacterTokens(s));
        }
    }

    fn get_tokens(mut self) -> Vec<Token> {
        self.finish_str();
        self.tokens
    }
}

impl TokenSink for TokenLogger {
    fn process_token(&mut self, token: Token) {
        match token {
            CharacterTokens(b) => {
                self.current_str.push_str(b.as_slice());
            }

            NullCharacterToken => {
                self.current_str.push('\0');
            }

            ParseError(_) => if self.exact_errors {
                self.push(ParseError(Borrowed("")));
            },

            TagToken(mut t) => {
                // The spec seems to indicate that one can emit
                // erroneous end tags with attrs, but the test
                // cases don't contain them.
                match t.kind {
                    EndTag => {
                        t.self_closing = false;
                        t.attrs = vec!();
                    }
                    _ => t.attrs.sort_by(|a1, a2| a1.name.cmp(&a2.name)),
                }
                self.push(TagToken(t));
            }

            EOFToken => (),

            _ => self.push(token),
        }
    }
}

fn tokenize(input: Vec<String>, opts: TokenizerOpts) -> Vec<Token> {
    let sink = TokenLogger::new(opts.exact_errors);
    let mut tok = Tokenizer::new(sink, opts);
    for chunk in input.into_iter() {
        tok.feed(chunk);
    }
    tok.end();
    tok.unwrap().get_tokens()
}

trait JsonExt {
    fn get_str(&self) -> String;
    fn get_nullable_str(&self) -> Option<String>;
    fn get_bool(&self) -> bool;
    fn get_obj<'t>(&'t self) -> &'t BTreeMap<String, Self>;
    fn get_list<'t>(&'t self) -> &'t Vec<Self>;
    fn find<'t>(&'t self, key: &str) -> &'t Self;
}

impl JsonExt for Json {
    fn get_str(&self) -> String {
        match *self {
            Json::String(ref s) => s.to_string(),
            _ => panic!("Json::get_str: not a String"),
        }
    }

    fn get_nullable_str(&self) -> Option<String> {
        match *self {
            Json::Null => None,
            Json::String(ref s) => Some(s.to_string()),
            _ => panic!("Json::get_nullable_str: not a String"),
        }
    }

    fn get_bool(&self) -> bool {
        match *self {
            Json::Boolean(b) => b,
            _ => panic!("Json::get_bool: not a Boolean"),
        }
    }

    fn get_obj<'t>(&'t self) -> &'t BTreeMap<String, Json> {
        match *self {
            Json::Object(ref m) => &*m,
            _ => panic!("Json::get_obj: not an Object"),
        }
    }

    fn get_list<'t>(&'t self) -> &'t Vec<Json> {
        match *self {
            Json::Array(ref m) => m,
            _ => panic!("Json::get_list: not an Array"),
        }
    }

    fn find<'t>(&'t self, key: &str) -> &'t Json {
        self.get_obj().get(&key.to_string()).unwrap()
    }
}

// Parse a JSON object (other than "ParseError") to a token.
fn json_to_token(js: &Json) -> Token {
    let parts = js.get_list();
    // Collect refs here so we don't have to use "ref" in all the patterns below.
    let args: Vec<&Json> = parts[1..].iter().collect();
    match (parts[0].get_str().as_slice(), args.as_slice()) {
        ("DOCTYPE", [name, public_id, system_id, correct]) => DoctypeToken(Doctype {
            name: name.get_nullable_str(),
            public_id: public_id.get_nullable_str(),
            system_id: system_id.get_nullable_str(),
            force_quirks: !correct.get_bool(),
        }),

        ("StartTag", [name, attrs, rest..]) => TagToken(Tag {
            kind: StartTag,
            name: Atom::from_slice(name.get_str().as_slice()),
            attrs: attrs.get_obj().iter().map(|(k,v)| {
                Attribute {
                    name: QualName::new(ns!(""), Atom::from_slice(k.as_slice())),
                    value: v.get_str()
                }
            }).collect(),
            self_closing: match rest {
                [ref b, ..] => b.get_bool(),
                _ => false,
            }
        }),

        ("EndTag", [name]) => TagToken(Tag {
            kind: EndTag,
            name: Atom::from_slice(name.get_str().as_slice()),
            attrs: vec!(),
            self_closing: false
        }),

        ("Comment", [txt]) => CommentToken(txt.get_str()),

        ("Character", [txt]) => CharacterTokens(txt.get_str()),

        // We don't need to produce NullCharacterToken because
        // the TokenLogger will convert them to CharacterTokens.

        _ => panic!("don't understand token {:?}", parts),
    }
}

// Parse the "output" field of the test case into a vector of tokens.
fn json_to_tokens(js: &Json, exact_errors: bool) -> Vec<Token> {
    // Use a TokenLogger so that we combine character tokens separated
    // by an ignored error.
    let mut sink = TokenLogger::new(exact_errors);
    for tok in js.get_list().iter() {
        match *tok {
            Json::String(ref s)
                if s.as_slice() == "ParseError" => sink.process_token(ParseError(Borrowed(""))),
            _ => sink.process_token(json_to_token(tok)),
        }
    }
    sink.get_tokens()
}

// Undo the escaping in "doubleEscaped" tests.
fn unescape(s: &str) -> Option<String> {
    let mut out = String::with_capacity(s.len());
    let mut it = s.chars().peekable();
    loop {
        match it.next() {
            None => return Some(out),
            Some('\\') => {
                if it.peek() != Some(&'u') {
                    panic!("can't understand escape");
                }
                drop(it.next());
                let hex: String = it.by_ref().take(4).collect();
                match num::from_str_radix(hex.as_slice(), 16)
                          .and_then(char::from_u32) {
                    // Some of the tests use lone surrogates, but we have no
                    // way to represent them in the UTF-8 input to our parser.
                    // Since these can only come from script, we will catch
                    // them there.
                    None => return None,
                    Some(c) => out.push(c),
                }
            }
            Some(c) => out.push(c),
        }
    }
}

fn unescape_json(js: &Json) -> Json {
    match *js {
        // unwrap is OK here because the spec'd *output* of the tokenizer never
        // contains a lone surrogate.
        Json::String(ref s) => Json::String(unescape(s.as_slice()).unwrap()),
        Json::Array(ref xs) => Json::Array(xs.iter().map(unescape_json).collect()),
        Json::Object(ref obj) => {
            let mut new_obj = BTreeMap::new();
            for (k,v) in obj.iter() {
                new_obj.insert(k.clone(), unescape_json(v));
            }
            Json::Object(new_obj)
        }
        _ => js.clone(),
    }
}

fn mk_test(desc: String, input: String, expect: Vec<Token>, opts: TokenizerOpts)
        -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(desc),
            ignore: false,
            should_fail: No,
        },
        testfn: DynTestFn(Thunk::new(move || {
            // Split up the input at different points to test incremental tokenization.
            let insplits = splits(input.as_slice(), 3);
            for input in insplits.into_iter() {
                // Clone 'input' so we have it for the failure message.
                // Also clone opts.  If we don't, we get the wrong
                // result but the compiler doesn't catch it!
                // Possibly mozilla/rust#12223.
                let output = tokenize(input.clone(), opts.clone());
                if output != expect {
                    panic!("\ninput: {:?}\ngot: {:?}\nexpected: {:?}",
                        input, output, expect);
                }
            }
        })),
    }
}

fn mk_tests(tests: &mut Vec<TestDescAndFn>, path_str: &str, js: &Json) {
    let obj = js.get_obj();
    let mut input = js.find("input").unwrap().get_str();
    let mut expect = js.find("output").unwrap().clone();
    let desc = format!("tok: {}: {}",
        path_str, js.find("description").unwrap().get_str());

    // "Double-escaped" tests require additional processing of
    // the input and output.
    if obj.get(&"doubleEscaped".to_string()).map_or(false, |j| j.get_bool()) {
        match unescape(input.as_slice()) {
            None => return,
            Some(i) => input = i,
        }
        expect = unescape_json(&expect);
    }

    // Some tests have a last start tag name.
    let start_tag = obj.get(&"lastStartTag".to_string()).map(|s| s.get_str());

    // Some tests want to start in a state other than Data.
    let state_overrides = match obj.get(&"initialStates".to_string()) {
        Some(&Json::Array(ref xs)) => xs.iter().map(|s|
            Some(match s.get_str().as_slice() {
                "PLAINTEXT state" => Plaintext,
                "RAWTEXT state"   => RawData(Rawtext),
                "RCDATA state"    => RawData(Rcdata),
                s => panic!("don't know state {}", s),
            })).collect(),
        None => vec!(None),
        _ => panic!("don't understand initialStates value"),
    };

    // Build the tests.
    for state in state_overrides.into_iter() {
        for &exact_errors in [false, true].iter() {
            let mut newdesc = desc.clone();
            match state {
                Some(s) => newdesc = format!("{} (in state {:?})", newdesc, s),
                None  => (),
            };
            if exact_errors {
                newdesc = format!("{} (exact errors)", newdesc);
            }

            let expect_toks = json_to_tokens(&expect, exact_errors);
            tests.push(mk_test(newdesc, input.clone(), expect_toks, TokenizerOpts {
                exact_errors: exact_errors,
                initial_state: state,
                last_start_tag_name: start_tag.clone(),

                // Not discarding a BOM is what the test suite expects; see
                // https://github.com/html5lib/html5lib-tests/issues/2
                discard_bom: false,

                .. Default::default()
            }));
        }
    }
}

pub fn tests(src_dir: Path) -> IntoIter<TestDescAndFn> {
    let mut tests = vec!();

    foreach_html5lib_test(src_dir, "tokenizer", ".test", |path_str, mut file| {
        let js = json::from_reader(&mut file as &mut Reader)
            .ok().expect("json parse error");

        match js.get_obj().get(&"tests".to_string()) {
            Some(&Json::Array(ref lst)) => {
                for test in lst.iter() {
                    mk_tests(&mut tests, path_str.as_slice(), test);
                }
            }

            // xmlViolation.test doesn't follow this format.
            _ => (),
        }
    });

    tests.into_iter()
}
<|endoftext|>


<commit_before>#![crate_name = "uu_install"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Ben Eills <ben@beneills.com>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

mod mode;

#[macro_use]
extern crate uucore;

use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::result::Result;

static NAME: &'static str = "install";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

const DEFAULT_MODE: libc::mode_t = 755;

pub struct Behaviour {
    main_function: MainFunction,
    specified_mode: Option<u16>,
    suffix: String,
    verbose: bool
}

#[derive(Clone, Eq, PartialEq)]
pub enum MainFunction {
    /// Display version information.
    Version,
    /// Display help, including command line arguments.
    Help,
    /// Create directories
    Directory,
    /// Install files to locations (primary functionality)
    Standard
}

impl Behaviour {
    /// Determine the mode for chmod after copy.
    pub fn mode(&self) -> libc::mode_t {
        match self.specified_mode {
            Some(x) => x,
            None => DEFAULT_MODE
        }
    }
}

/// Main install utility function, called from main.rs.
///
/// Returns a program return code.
///
pub fn uumain(args: Vec<String>) -> i32 {
    let opts = opts();

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            show_error!("Invalid options\n{}", f);
            return 1;
        }
    };

    let usage = opts.usage("Copy SOURCE to DEST or multiple SOURCE(s) to the existing\n \
                            DIRECTORY, while setting permission modes and owner/group");

    if let Err(s) = check_unimplemented(&matches) {
        show_error!("Unimplemented feature: {}", s);
        return 2;
    }

    let behaviour = match behaviour(&matches) {
        Ok(x) => x,
        Err(ret) => {
            return ret;
        }
    };

    let paths: Vec<PathBuf> = {
        fn string_to_path<'a>(s: &'a String) -> &'a Path {
            Path::new(s)
        };
        let to_owned = |p: &Path| p.to_owned();
        let arguments = matches.free.iter().map(string_to_path);

        arguments.map(to_owned).collect()
    };

    match behaviour.main_function {
        MainFunction::Version => {
            println!("{} {}", NAME, VERSION);
            0
        },
        MainFunction::Help => {
            help(&usage);
            0
        },
        MainFunction::Directory => {
            directory(&paths[..], behaviour)
        },
        MainFunction::Standard => {
            standard(&paths[..], behaviour)
        }
    }
}

/// Build a specification of the comamnd line.
///
/// Returns a getopts::Options struct.
///
fn opts() -> getopts::Options {
    let mut opts = getopts::Options::new();

    // TODO implement flag
    opts.optflagopt("",  "backup", "(unimplemented) make a backup of each existing destination\n \
                                    file", "CONTROL");

    // TODO implement flag
    opts.optflag("b", "", "(unimplemented) like --backup but does not accept an argument");

    // TODO implement flag
    opts.optflag("C", "compare", "(unimplemented) compare each pair of source and destination\n \
                                  files, and in some cases, do not modify the destination at all");

    opts.optflag("d", "directory", "treat all arguments as directory names;\n \
                                    create all components of the specified directories");

    // TODO implement flag
    opts.optflag("D", "", "(unimplemented) create all leading components of DEST except the\n \
                           last, then copy SOURCE to DEST");

    // TODO implement flag
    opts.optflagopt("g", "group", "(unimplemented) set group ownership, instead of process'\n \
                                   current group", "GROUP");

    opts.optflagopt("m", "mode", "set permission mode (as in chmod), instead\n \
                                  of rwxr-xr-x", "MODE");

    // TODO implement flag
    opts.optflagopt("o", "owner", "(unimplemented) set ownership (super-user only)",
                    "OWNER");

    // TODO implement flag
    opts.optflag("p", "preserve-timestamps", "(unimplemented) apply access/modification times\n \
                                              of SOURCE files to corresponding destination files");

    // TODO implement flag
    opts.optflag("s", "strip", "(unimplemented) strip symbol tables");

    // TODO implement flag
    opts.optflagopt("", "strip-program", "(unimplemented) program used to strip binaries",
                    "PROGRAM");

    // TODO implement flag
    opts.optopt("S", "suffix", "(unimplemented) override the usual backup suffix", "SUFFIX");

    // TODO implement flag
    opts.optopt("t", "target-directory", "(unimplemented) move all SOURCE arguments into\n \
                                          DIRECTORY", "DIRECTORY");

    // TODO implement flag
    opts.optflag("T", "no-target-directory", "(unimplemented) treat DEST as a normal file");

    // TODO implement flag
    opts.optflag("v", "verbose", "(unimplemented) explain what is being done");

    // TODO implement flag
    opts.optflag("P", "preserve-context", "(unimplemented) preserve security context");

    // TODO implement flag
    opts.optflagopt("Z", "context", "(unimplemented) set security context of files and\n \
                                     directories", "CONTEXT");

    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    opts
}

/// Check for unimplemented command line arguments.
///
/// Either return the degenerate Ok value, or an Err with string.
///
/// # Errors
///
/// Error datum is a string of the unimplemented argument.
///
fn check_unimplemented(matches: &getopts::Matches) -> Result<(), &str> {
    if matches.opt_present("backup") {
        Err("--backup")
    } else if matches.opt_present("b") {
        Err("-b")
    } else if matches.opt_present("compare") {
        Err("--compare, -C")
    } else if matches.opt_present("D") {
        Err("-D")
    } else if matches.opt_present("group") {
        Err("--group, -g")
    } else if matches.opt_present("owner") {
        Err("--owner, -o")
    } else if matches.opt_present("preserve-timestamps") {
        Err("--preserve-timestamps, -p")
    } else if matches.opt_present("strip") {
        Err("--strip, -s")
    } else if matches.opt_present("strip-program") {
        Err("--strip-program")
    } else if matches.opt_present("suffix") {
        Err("--suffix, -S")
    } else if matches.opt_present("target-directory") {
        Err("--target-directory, -t")
    } else if matches.opt_present("no-target-directory") {
        Err("--no-target-directory, -T")
    } else if matches.opt_present("verbose") {
        Err("--verbose, -v")
    } else if matches.opt_present("preserve-context") {
        Err("--preserve-context, -P")
    } else if matches.opt_present("context") {
        Err("--context, -Z")
    } else {
        Ok(())
    }
}

/// Determine behaviour, given command line arguments.
///
/// If successful, returns a filled-out Behaviour struct.
///
/// # Errors
///
/// In event of failure, returns an integer intended as a program return code.
///
fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
    let main_function = if matches.opt_present("version") {
        MainFunction::Version
    } else if matches.opt_present("help") {
        MainFunction::Help
    } else if matches.opt_present("directory") {
        MainFunction::Directory
    } else {
        MainFunction::Standard
    };

    let considering_dir: bool = MainFunction::Directory == main_function;

    let specified_mode: Option<libc::mode_t> = if matches.opt_present("mode") {
        match matches.opt_str("mode") {
            Some(x) => {
                match mode::parse(&x[..], considering_dir) {
                    Ok(y) => Some(y),
                    Err(err) => {
                        show_error!("Invalid mode string: {}", err);
                        return Err(1);
                    }
                }
            },
            None => {
                show_error!("option '--mode' requires an argument\n \
                            Try '{} --help' for more information.", NAME);
                return Err(1);
            }
        }
    } else {
        None
    };

    let backup_suffix = if matches.opt_present("suffix") {
        match matches.opt_str("suffix") {
            Some(x) => x,
            None => {
                show_error!("option '--suffix' requires an argument\n\
                            Try '{} --help' for more information.", NAME);
                return Err(1);
            }
        }
    } else {
        "~".to_owned()
    };

    Ok(Behaviour {
        main_function: main_function,
        specified_mode: specified_mode,
        suffix: backup_suffix,
        verbose: matches.opt_present("v"),
    })
}

/// Print utility help to stdout.
///
fn help(usage: &str) {
    println!("{0} {1}\n\n\
    Usage: {0} SOURCE DEST\n   \
       or: {0} SOURCE... DIRECTORY\n\n\
    {2}", NAME, VERSION, usage);
}

/// Creates directories.
///
/// GNU man pages describe this functionality as creating 'all components of
/// the specified directories'.
///
/// Returns an integer intended as a program return code.
///
fn directory(paths: &[PathBuf], b: Behaviour) -> i32 {
    if paths.len() < 1 {
        println!("{} with -d requires at least one argument.", NAME);
        1
    } else {
        let mut all_successful = true;

        for directory in paths.iter() {
            let path = directory.as_path();

            if path.exists() {
                show_info!("cannot create directory '{}': File exists", path.display());
                all_successful = false;
            }

            if let Err(e) = fs::create_dir(directory) {
                show_info!("{}: {}", path.display(), e.to_string());
                all_successful = false;
            }

            if mode::chmod(&path, b.mode()).is_err() {
                all_successful = false;
            }

            if b.verbose {
                show_info!("created directory '{}'", path.display());
            }
        }
        if all_successful { 0 } else { 1 }
    }
}

/// Perform an install, given a list of paths and behaviour.
///
/// Returns an integer intended as a program return code.
///
fn standard(paths: &[PathBuf], b: Behaviour) -> i32 {
    if paths.len() < 2 {
        println!("{} requires at least 2 arguments.", NAME);
        1
    } else {
        let sources = &paths[0..paths.len() - 1];
        let target_directory = &paths[paths.len() - 1];

        copy_files_into_dir(sources, target_directory, &b)
    }
}

/// Copy some files into a directory.
///
/// Prints verbose information and error messages.
/// Returns an integer intended as a program return code.
///
/// # Parameters
///
/// _files_ must all exist as non-directories.
/// _target_dir_ must be a directory.
///
fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target ‘{}’ is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.as_os_str().to_str() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!("cannot stat ‘{}’: No such file or directory",
                            sourcepath.display());

                all_successful = false;
                continue;
            }
        };

        if copy(sourcepath, &targetpath, b).is_err() {
            all_successful = false;
        }
    };
    if all_successful { 0 } else { 1 }
}

/// Copy one file to a new location, changing metadata.
///
/// # Parameters
///
/// _from_ must exist as a non-directory.
/// _to_ must be a non-existent file, whose parent directory exists.
///
/// # Errors
///
/// If the copy system call fails, we print a verbose error and return an empty error value.
///
fn copy(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<(), ()> {
    let io_result = fs::copy(from, to);

    if let Err(err) = io_result {
        show_error!("install: cannot install ‘{}’ to ‘{}’: {}",
                    from.display(), to.display(), err);
        return Err(());
    }

    if mode::chmod(&to, b.mode()).is_err() {
        return Err(());
    }

    if b.verbose {
        print!("‘{}’ -> ‘{}’", from.display(), to.display());
    }

    Ok(())
}
<commit_msg>Fix type synonym relaiance which breaks some rusts<commit_after>#![crate_name = "uu_install"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Ben Eills <ben@beneills.com>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

mod mode;

#[macro_use]
extern crate uucore;

use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::result::Result;

static NAME: &'static str = "install";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

const DEFAULT_MODE: libc::mode_t = 755;

pub struct Behaviour {
    main_function: MainFunction,
    specified_mode: Option<libc::mode_t>,
    suffix: String,
    verbose: bool
}

#[derive(Clone, Eq, PartialEq)]
pub enum MainFunction {
    /// Display version information.
    Version,
    /// Display help, including command line arguments.
    Help,
    /// Create directories
    Directory,
    /// Install files to locations (primary functionality)
    Standard
}

impl Behaviour {
    /// Determine the mode for chmod after copy.
    pub fn mode(&self) -> libc::mode_t {
        match self.specified_mode {
            Some(x) => x,
            None => DEFAULT_MODE
        }
    }
}

/// Main install utility function, called from main.rs.
///
/// Returns a program return code.
///
pub fn uumain(args: Vec<String>) -> i32 {
    let opts = opts();

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            show_error!("Invalid options\n{}", f);
            return 1;
        }
    };

    let usage = opts.usage("Copy SOURCE to DEST or multiple SOURCE(s) to the existing\n \
                            DIRECTORY, while setting permission modes and owner/group");

    if let Err(s) = check_unimplemented(&matches) {
        show_error!("Unimplemented feature: {}", s);
        return 2;
    }

    let behaviour = match behaviour(&matches) {
        Ok(x) => x,
        Err(ret) => {
            return ret;
        }
    };

    let paths: Vec<PathBuf> = {
        fn string_to_path<'a>(s: &'a String) -> &'a Path {
            Path::new(s)
        };
        let to_owned = |p: &Path| p.to_owned();
        let arguments = matches.free.iter().map(string_to_path);

        arguments.map(to_owned).collect()
    };

    match behaviour.main_function {
        MainFunction::Version => {
            println!("{} {}", NAME, VERSION);
            0
        },
        MainFunction::Help => {
            help(&usage);
            0
        },
        MainFunction::Directory => {
            directory(&paths[..], behaviour)
        },
        MainFunction::Standard => {
            standard(&paths[..], behaviour)
        }
    }
}

/// Build a specification of the comamnd line.
///
/// Returns a getopts::Options struct.
///
fn opts() -> getopts::Options {
    let mut opts = getopts::Options::new();

    // TODO implement flag
    opts.optflagopt("",  "backup", "(unimplemented) make a backup of each existing destination\n \
                                    file", "CONTROL");

    // TODO implement flag
    opts.optflag("b", "", "(unimplemented) like --backup but does not accept an argument");

    // TODO implement flag
    opts.optflag("C", "compare", "(unimplemented) compare each pair of source and destination\n \
                                  files, and in some cases, do not modify the destination at all");

    opts.optflag("d", "directory", "treat all arguments as directory names;\n \
                                    create all components of the specified directories");

    // TODO implement flag
    opts.optflag("D", "", "(unimplemented) create all leading components of DEST except the\n \
                           last, then copy SOURCE to DEST");

    // TODO implement flag
    opts.optflagopt("g", "group", "(unimplemented) set group ownership, instead of process'\n \
                                   current group", "GROUP");

    opts.optflagopt("m", "mode", "set permission mode (as in chmod), instead\n \
                                  of rwxr-xr-x", "MODE");

    // TODO implement flag
    opts.optflagopt("o", "owner", "(unimplemented) set ownership (super-user only)",
                    "OWNER");

    // TODO implement flag
    opts.optflag("p", "preserve-timestamps", "(unimplemented) apply access/modification times\n \
                                              of SOURCE files to corresponding destination files");

    // TODO implement flag
    opts.optflag("s", "strip", "(unimplemented) strip symbol tables");

    // TODO implement flag
    opts.optflagopt("", "strip-program", "(unimplemented) program used to strip binaries",
                    "PROGRAM");

    // TODO implement flag
    opts.optopt("S", "suffix", "(unimplemented) override the usual backup suffix", "SUFFIX");

    // TODO implement flag
    opts.optopt("t", "target-directory", "(unimplemented) move all SOURCE arguments into\n \
                                          DIRECTORY", "DIRECTORY");

    // TODO implement flag
    opts.optflag("T", "no-target-directory", "(unimplemented) treat DEST as a normal file");

    // TODO implement flag
    opts.optflag("v", "verbose", "(unimplemented) explain what is being done");

    // TODO implement flag
    opts.optflag("P", "preserve-context", "(unimplemented) preserve security context");

    // TODO implement flag
    opts.optflagopt("Z", "context", "(unimplemented) set security context of files and\n \
                                     directories", "CONTEXT");

    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    opts
}

/// Check for unimplemented command line arguments.
///
/// Either return the degenerate Ok value, or an Err with string.
///
/// # Errors
///
/// Error datum is a string of the unimplemented argument.
///
fn check_unimplemented(matches: &getopts::Matches) -> Result<(), &str> {
    if matches.opt_present("backup") {
        Err("--backup")
    } else if matches.opt_present("b") {
        Err("-b")
    } else if matches.opt_present("compare") {
        Err("--compare, -C")
    } else if matches.opt_present("D") {
        Err("-D")
    } else if matches.opt_present("group") {
        Err("--group, -g")
    } else if matches.opt_present("owner") {
        Err("--owner, -o")
    } else if matches.opt_present("preserve-timestamps") {
        Err("--preserve-timestamps, -p")
    } else if matches.opt_present("strip") {
        Err("--strip, -s")
    } else if matches.opt_present("strip-program") {
        Err("--strip-program")
    } else if matches.opt_present("suffix") {
        Err("--suffix, -S")
    } else if matches.opt_present("target-directory") {
        Err("--target-directory, -t")
    } else if matches.opt_present("no-target-directory") {
        Err("--no-target-directory, -T")
    } else if matches.opt_present("verbose") {
        Err("--verbose, -v")
    } else if matches.opt_present("preserve-context") {
        Err("--preserve-context, -P")
    } else if matches.opt_present("context") {
        Err("--context, -Z")
    } else {
        Ok(())
    }
}

/// Determine behaviour, given command line arguments.
///
/// If successful, returns a filled-out Behaviour struct.
///
/// # Errors
///
/// In event of failure, returns an integer intended as a program return code.
///
fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
    let main_function = if matches.opt_present("version") {
        MainFunction::Version
    } else if matches.opt_present("help") {
        MainFunction::Help
    } else if matches.opt_present("directory") {
        MainFunction::Directory
    } else {
        MainFunction::Standard
    };

    let considering_dir: bool = MainFunction::Directory == main_function;

    let specified_mode: Option<libc::mode_t> = if matches.opt_present("mode") {
        match matches.opt_str("mode") {
            Some(x) => {
                match mode::parse(&x[..], considering_dir) {
                    Ok(y) => Some(y),
                    Err(err) => {
                        show_error!("Invalid mode string: {}", err);
                        return Err(1);
                    }
                }
            },
            None => {
                show_error!("option '--mode' requires an argument\n \
                            Try '{} --help' for more information.", NAME);
                return Err(1);
            }
        }
    } else {
        None
    };

    let backup_suffix = if matches.opt_present("suffix") {
        match matches.opt_str("suffix") {
            Some(x) => x,
            None => {
                show_error!("option '--suffix' requires an argument\n\
                            Try '{} --help' for more information.", NAME);
                return Err(1);
            }
        }
    } else {
        "~".to_owned()
    };

    Ok(Behaviour {
        main_function: main_function,
        specified_mode: specified_mode,
        suffix: backup_suffix,
        verbose: matches.opt_present("v"),
    })
}

/// Print utility help to stdout.
///
fn help(usage: &str) {
    println!("{0} {1}\n\n\
    Usage: {0} SOURCE DEST\n   \
       or: {0} SOURCE... DIRECTORY\n\n\
    {2}", NAME, VERSION, usage);
}

/// Creates directories.
///
/// GNU man pages describe this functionality as creating 'all components of
/// the specified directories'.
///
/// Returns an integer intended as a program return code.
///
fn directory(paths: &[PathBuf], b: Behaviour) -> i32 {
    if paths.len() < 1 {
        println!("{} with -d requires at least one argument.", NAME);
        1
    } else {
        let mut all_successful = true;

        for directory in paths.iter() {
            let path = directory.as_path();

            if path.exists() {
                show_info!("cannot create directory '{}': File exists", path.display());
                all_successful = false;
            }

            if let Err(e) = fs::create_dir(directory) {
                show_info!("{}: {}", path.display(), e.to_string());
                all_successful = false;
            }

            if mode::chmod(&path, b.mode()).is_err() {
                all_successful = false;
            }

            if b.verbose {
                show_info!("created directory '{}'", path.display());
            }
        }
        if all_successful { 0 } else { 1 }
    }
}

/// Perform an install, given a list of paths and behaviour.
///
/// Returns an integer intended as a program return code.
///
fn standard(paths: &[PathBuf], b: Behaviour) -> i32 {
    if paths.len() < 2 {
        println!("{} requires at least 2 arguments.", NAME);
        1
    } else {
        let sources = &paths[0..paths.len() - 1];
        let target_directory = &paths[paths.len() - 1];

        copy_files_into_dir(sources, target_directory, &b)
    }
}

/// Copy some files into a directory.
///
/// Prints verbose information and error messages.
/// Returns an integer intended as a program return code.
///
/// # Parameters
///
/// _files_ must all exist as non-directories.
/// _target_dir_ must be a directory.
///
fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target ‘{}’ is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.as_os_str().to_str() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!("cannot stat ‘{}’: No such file or directory",
                            sourcepath.display());

                all_successful = false;
                continue;
            }
        };

        if copy(sourcepath, &targetpath, b).is_err() {
            all_successful = false;
        }
    };
    if all_successful { 0 } else { 1 }
}

/// Copy one file to a new location, changing metadata.
///
/// # Parameters
///
/// _from_ must exist as a non-directory.
/// _to_ must be a non-existent file, whose parent directory exists.
///
/// # Errors
///
/// If the copy system call fails, we print a verbose error and return an empty error value.
///
fn copy(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<(), ()> {
    let io_result = fs::copy(from, to);

    if let Err(err) = io_result {
        show_error!("install: cannot install ‘{}’ to ‘{}’: {}",
                    from.display(), to.display(), err);
        return Err(());
    }

    if mode::chmod(&to, b.mode()).is_err() {
        return Err(());
    }

    if b.verbose {
        print!("‘{}’ -> ‘{}’", from.display(), to.display());
    }

    Ok(())
}
<|endoftext|>


<commit_before>use system::SystemData;
use system::Registers;

// Returns clock system_data.cycle passed during opcode
pub fn parse_opcode(system_data_original: &mut SystemData, registers_original: &mut Registers)
{
    //Borrow splitting
    let mut system_data = system_data_original;
    let mut registers = registers_original;

    system_data.cycles = 0;
    let opcode: u8 = system_data.mem_map[registers.program_counter as usize];
    println!("{:x}: {:x}", registers.program_counter, opcode);

    //inc
    if (opcode & 0xC7) == 0x04
    {
        increment(&mut system_data, &mut registers, opcode);
    }
    //8bit ld group
    else if (opcode & 0xC7) == 0x06
    {
        load_n_to_8bit_register(&mut system_data, &mut registers, opcode);
    }
    //0b01xxxxxx group
    else if (opcode & 0xC0) == 0x40
    {
        //LD (HL), r
        if (opcode & 0xF8) == 0x70
        {
            load_hl_address_with_register(&mut system_data, &mut registers, opcode);
        }
        //ld r, r'
        else
        {
            load_8_bit_register_to_register(&mut system_data, &mut registers, opcode);
        }
    }
    //ld (FF00+C), A
    else if opcode == 0xE2
    {
        load_accumulator_to_io_port_with_c_offset(&mut system_data, &mut registers);
    }
    //ld (FF00+n), A
    else if opcode == 0xE0
    {
        load_accumulator_to_io_port_with_n_offset(&mut system_data, &mut registers);
    }
    //16 bit ld group
    else if (opcode & 0xCF) == 0x01
    {
        load_nn_to_16bit_register(&mut system_data, &mut registers, opcode);
    }
    else if opcode == 0x1A
    {
        load_accumulator_with_de_address(&mut system_data, &mut registers);
    }
    //xor
    else if (opcode & 0xF8) == 0xA8
    {
        xor_register(&mut system_data, &mut registers, opcode);
    }
    //jump nz, dis
    else if (opcode == 0x20)
    {
        jump_displacement_on_nonzero_flag(&mut system_data, &mut registers);
    }

    //LDD (HL), A
    else if (opcode == 0x32)
    {
        load_decrement_hl_register_location_with_accumulator(&mut system_data, &mut registers);
    }
    //cb codes
    else if (opcode == 0xCB){
        cb_codes(&mut system_data, &mut registers);
    }
    else
    {
        println!("No Opcode Found");
    }
}

pub fn increment(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        system_data.cycles = 1;
        registers.flags = registers.flags & 0x10;
        if (opcode & 0x38) == 0x38{
            registers.accumulator += 1;
            if registers.accumulator == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            if registers.b_register == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
        else if (opcode & 0x38) == 0x00{
            registers.b_register += 1;
            if registers.b_register == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            if registers.b_register == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
        else if (opcode & 0x38) == 0x08{
            registers.c_register += 1;
            if registers.c_register == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            if registers.c_register == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
        else if (opcode & 0x38) == 0x10{
            registers.d_register += 1;
            if registers.d_register == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            if registers.d_register == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
        else if (opcode & 0x38) == 0x18{
            registers.e_register += 1;
            if registers.e_register == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            if registers.e_register == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
        else if (opcode & 0x38) == 0x20{
            registers.h_register += 1;
            if registers.h_register == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            if registers.h_register == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
        else if (opcode & 0x38) == 0x28{
            registers.l_register += 1;
            if registers.l_register == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            if registers.l_register == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
}

pub fn load_n_to_8bit_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        system_data.cycles = 2;
        if(opcode & 0x38) == 0x38
        {
            registers.accumulator = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if(opcode & 0x38) == 0x00
        {
            registers.b_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if(opcode & 0x38) == 0x08
        {
            registers.c_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if(opcode & 0x38) == 0x10
        {
            registers.d_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if(opcode & 0x38) == 0x18
        {
            registers.e_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if(opcode & 0x38) == 0x20
        {
            registers.h_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if(opcode & 0x38) == 0x28
        {
            registers.l_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
        registers.program_counter += 2;
}

pub fn load_8_bit_register_to_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        system_data.cycles = 1;
        let mut register_value = 0;
        //Load value from register
        if (opcode & 0x07) == 0x07
        {
            register_value = registers.accumulator;
        }
        else if (opcode & 0x07) == 0x00
        {
            register_value = registers.b_register;
        }
        else if (opcode & 0x07) == 0x01
        {
            register_value = registers.c_register;
        }
        else if (opcode & 0x07) == 0x02
        {
            register_value = registers.d_register;
        }
        else if (opcode & 0x07) == 0x03
        {
            register_value = registers.e_register;
        }
        else if (opcode & 0x07) == 0x04
        {
            register_value = registers.h_register;
        }
        else if (opcode & 0x07) == 0x05
        {
            register_value = registers.l_register;
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }

        //Load into new register
        if (opcode & 0x38) == 0x38
        {
            registers.accumulator = register_value;
        }
        else if (opcode & 0x38) == 0x00
        {
            registers.b_register = register_value;
        }
        else if (opcode & 0x38) == 0x08
        {
            registers.c_register = register_value;
        }
        else if (opcode & 0x38) == 0x10
        {
            registers.d_register = register_value;
        }
        else if (opcode & 0x38) == 0x18
        {
            registers.e_register = register_value;
        }
        else if (opcode & 0x38) == 0x20
        {
           registers.h_register = register_value;
        }
        else if (opcode & 0x38) == 0x28
        {
            registers.l_register = register_value;
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
        registers.program_counter += 1;
}

pub fn load_accumulator_to_io_port_with_c_offset(system_data: &mut SystemData, registers: &mut Registers)
{
        system_data.cycles = 2;
        system_data.mem_map[(0xFF00 + registers.c_register as u16) as usize] = registers.accumulator;
        registers.program_counter += 1;
}

pub fn load_accumulator_to_io_port_with_n_offset(system_data: &mut SystemData, registers: &mut Registers)
{
        system_data.cycles = 3;
        let n = system_data.mem_map[(registers.program_counter + 1) as usize];
        system_data.mem_map[(0xFF00 + n as u16) as usize] = registers.accumulator;
        registers.program_counter += 1;
}

pub fn load_nn_to_16bit_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8){
    system_data.cycles = 2;

        if (opcode & 0x30) == 0x30
        {
            registers.stack_pointer = system_data.mem_map[(registers.program_counter + 1) as usize] as u16 | (system_data.mem_map[(registers.program_counter + 2) as usize] as u16) << 8;
        }
        else if (opcode & 0x30) == 0x20
        {
            registers.h_register = system_data.mem_map[(registers.program_counter + 2) as usize];
            registers.l_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if (opcode & 0x30) == 0x10
        {
            registers.d_register = system_data.mem_map[(registers.program_counter + 2) as usize];
            registers.e_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if (opcode & 0x30) == 0x00
        {
            registers.b_register = system_data.mem_map[(registers.program_counter + 2) as usize];
            registers.c_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }

        registers.program_counter += 3;
}

pub fn xor_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        if (opcode & 0x07) == 0x07
        {
            registers.accumulator = registers.accumulator ^ registers.accumulator;
            registers.program_counter += 1;
            system_data.cycles = 1;
            registers.flags = 0x00;
            if  registers.accumulator ==  0 {
                registers.flags = registers.flags | 0x80;
            }
        }
        else
        {
            println!("No Opcode Found");
        }
}

pub fn jump_displacement_on_nonzero_flag(system_data: &mut SystemData, registers: &mut Registers)
{
        if (registers.flags & 0x80) != 0x80 {
            system_data.cycles = 3;
            let pc_dest: i8 = (system_data.mem_map[(registers.program_counter + 1) as usize] + 2) as i8;
            registers.program_counter = (registers.program_counter as i32 + pc_dest as i32) as u16;
        }
        else {
            system_data.cycles = 2;
            registers.program_counter += 2;
        }
}

pub fn load_decrement_hl_register_location_with_accumulator(system_data: &mut SystemData, registers: &mut Registers)
{
    let mut mem_loc: u16 = registers.l_register as u16 | (registers.h_register as u16) << 8;
    system_data.mem_map[mem_loc as usize] = registers.accumulator;
    mem_loc -= 1;
    registers.l_register = (mem_loc & 0x00FF) as u8;
    registers.h_register = ((mem_loc & 0xFF00) >> 8) as u8;
    system_data.cycles = 2;
    registers.program_counter += 1;
}

pub fn load_hl_address_with_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
    let mut mem_loc: u16 = registers.l_register as u16 | (registers.h_register as u16) << 8;
    system_data.cycles = 2;
    if (opcode & 0x07) == 0x07
    {
        system_data.mem_map[mem_loc as usize] = registers.accumulator;
    }
    else if (opcode & 0x07) == 0x00
    {
        system_data.mem_map[mem_loc as usize] = registers.b_register;
    }
    else if (opcode & 0x07) == 0x01
    {
        system_data.mem_map[mem_loc as usize] = registers.c_register;
    }
    else if (opcode & 0x07) == 0x02
    {
        system_data.mem_map[mem_loc as usize] = registers.d_register;
    }
    else if (opcode & 0x07) == 0x03
    {
        system_data.mem_map[mem_loc as usize] = registers.e_register;
    }
    else if (opcode & 0x07) == 0x04
    {
        system_data.mem_map[mem_loc as usize] = registers.h_register;
    }
    else if (opcode & 0x07) == 0x05
    {
        system_data.mem_map[mem_loc as usize] = registers.l_register;
    }
    else
    {
        system_data.cycles = 0;
        println!("No Opcode Found");
    }
    registers.program_counter += 1;
}

pub fn load_accumulator_with_de_address(system_data: &mut SystemData, registers: &mut Registers)
{
    system_data.cycles = 2;
    let mut mem_loc: u16 = registers.e_register as u16 | (registers.d_register as u16) << 8;
    registers.accumulator = system_data.mem_map[mem_loc as usize];
    registers.program_counter += 1;
}

///////////////////
//CB
///////////////////

pub fn cb_codes(system_data_original: &mut SystemData, registers_original: &mut Registers)
{
    //Borrow splitting
    let mut system_data = system_data_original;
    let mut registers = registers_original;
    let opcode :u8 = system_data.mem_map[(registers.program_counter + 1) as usize];
    //bit b, r
    if (opcode & 0xC0) == 0x40
    {
        system_data.cycles = 2;
        let test_bit: u8 = (opcode & 0x38) >> 3;
        bit_check_register(&mut system_data, &mut registers, opcode, test_bit)
    }
    else
    {
        println!("No Opcode Found");
    }
}

pub fn bit_check_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8, test_bit: u8)
{
        if (opcode & 0x07) == 0x04
        {
            registers.flags = registers.flags & 0x10;

            if ((registers.h_register >> test_bit) & 0x01) == 0x00
            {
                registers.flags = registers.flags | 0xA0;
            }
            else
            {
                registers.flags = registers.flags | 0x20;
            }
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
        registers.program_counter += 2;
}<commit_msg>refactored opcode functions<commit_after>use system::SystemData;
use system::Registers;

// Returns clock system_data.cycle passed during opcode
pub fn parse_opcode(system_data_original: &mut SystemData, registers_original: &mut Registers)
{
    //Borrow splitting
    let mut system_data = system_data_original;
    let mut registers = registers_original;

    system_data.cycles = 0;
    let opcode: u8 = system_data.mem_map[registers.program_counter as usize];
    println!("{:x}: {:x}", registers.program_counter, opcode);

    //inc
    if (opcode & 0xC7) == 0x04
    {
        increment(&mut system_data, &mut registers, opcode);
    }
    //8bit ld group
    else if (opcode & 0xC7) == 0x06
    {
        load_n_to_8bit_register(&mut system_data, &mut registers, opcode);
    }
    //0b01xxxxxx group
    else if (opcode & 0xC0) == 0x40
    {
        //LD (HL), r
        if (opcode & 0xF8) == 0x70
        {
            load_hl_address_with_register(&mut system_data, &mut registers, opcode);
        }
        //ld r, r'
        else
        {
            load_8_bit_register_to_register(&mut system_data, &mut registers, opcode);
        }
    }
    //ld (FF00+C), A
    else if opcode == 0xE2
    {
        load_accumulator_to_io_port_with_c_offset(&mut system_data, &mut registers);
    }
    //ld (FF00+n), A
    else if opcode == 0xE0
    {
        load_accumulator_to_io_port_with_n_offset(&mut system_data, &mut registers);
    }
    //16 bit ld group
    else if (opcode & 0xCF) == 0x01
    {
        load_nn_to_16bit_register(&mut system_data, &mut registers, opcode);
    }
    else if opcode == 0x1A
    {
        load_accumulator_with_de_address(&mut system_data, &mut registers);
    }
    //xor
    else if (opcode & 0xF8) == 0xA8
    {
        xor_register(&mut system_data, &mut registers, opcode);
    }
    //jump nz, dis
    else if (opcode == 0x20)
    {
        jump_displacement_on_nonzero_flag(&mut system_data, &mut registers);
    }

    //LDD (HL), A
    else if (opcode == 0x32)
    {
        load_decrement_hl_register_location_with_accumulator(&mut system_data, &mut registers);
    }
    //cb codes
    else if (opcode == 0xCB){
        cb_codes(&mut system_data, &mut registers);
    }
    else
    {
        println!("No Opcode Found");
    }
}

pub fn increment(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        system_data.cycles = 1;
        registers.flags = registers.flags & 0x10;
        let mut register_code = ((opcode & 0x38) >> 3) + 1;
        if register_code == 8
        {
            register_code = 0;
        }

        if register_code == 7
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
        else
        {
            let current_register_value = registers.mapped_register_getter(register_code);
            registers.mapped_register_setter(register_code, current_register_value + 1);
            if registers.mapped_register_getter(register_code) == 0
            {
                registers.flags = registers.flags | 0x80;
            }
            else if registers.mapped_register_getter(register_code) == 0x10
            {
                registers.flags = registers.flags | 0x20;
            }
            registers.program_counter += 1;
        }
}

pub fn load_n_to_8bit_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        system_data.cycles = 2;
        let mut register_code = ((opcode & 0x38) >> 3) + 1;
        if register_code == 8
        {
            register_code = 0;
        }

        if register_code == 7{
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
        else
        {
            let mem_loc: usize = registers.program_counter as usize + 1;
            registers.mapped_register_setter(register_code, system_data.mem_map[mem_loc])
        }

        registers.program_counter += 2;
}

pub fn load_8_bit_register_to_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        system_data.cycles = 1;
        let mut register_value = 0;
        //Load value from register
        let mut register_set_code = ((opcode & 0x38) >> 3) + 1;
        if register_set_code == 8
        {
            register_set_code = 0;
        }
        let mut register_get_code = (opcode & 0x07) + 1;
        if register_get_code == 8
        {
            register_get_code = 0;
        }

        if register_set_code == 7 || register_get_code == 7
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
        else {
            register_value = registers.mapped_register_getter(register_get_code);
            registers.mapped_register_setter(register_set_code, register_value)
        }

        registers.program_counter += 1;
}

pub fn load_accumulator_to_io_port_with_c_offset(system_data: &mut SystemData, registers: &mut Registers)
{
        system_data.cycles = 2;
        system_data.mem_map[(0xFF00 + registers.c_register as u16) as usize] = registers.accumulator;
        registers.program_counter += 1;
}

pub fn load_accumulator_to_io_port_with_n_offset(system_data: &mut SystemData, registers: &mut Registers)
{
        system_data.cycles = 3;
        let n = system_data.mem_map[(registers.program_counter + 1) as usize];
        system_data.mem_map[(0xFF00 + n as u16) as usize] = registers.accumulator;
        registers.program_counter += 1;
}

pub fn load_nn_to_16bit_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8){
    system_data.cycles = 2;

        if (opcode & 0x30) == 0x30
        {
            registers.stack_pointer = system_data.mem_map[(registers.program_counter + 1) as usize] as u16 | (system_data.mem_map[(registers.program_counter + 2) as usize] as u16) << 8;
        }
        else if (opcode & 0x30) == 0x20
        {
            registers.h_register = system_data.mem_map[(registers.program_counter + 2) as usize];
            registers.l_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if (opcode & 0x30) == 0x10
        {
            registers.d_register = system_data.mem_map[(registers.program_counter + 2) as usize];
            registers.e_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else if (opcode & 0x30) == 0x00
        {
            registers.b_register = system_data.mem_map[(registers.program_counter + 2) as usize];
            registers.c_register = system_data.mem_map[(registers.program_counter + 1) as usize];
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }

        registers.program_counter += 3;
}

pub fn xor_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
        if (opcode & 0x07) == 0x07
        {
            registers.accumulator = registers.accumulator ^ registers.accumulator;
            registers.program_counter += 1;
            system_data.cycles = 1;
            registers.flags = 0x00;
            if  registers.accumulator ==  0 {
                registers.flags = registers.flags | 0x80;
            }
        }
        else
        {
            println!("No Opcode Found");
        }
}

pub fn jump_displacement_on_nonzero_flag(system_data: &mut SystemData, registers: &mut Registers)
{
        if (registers.flags & 0x80) != 0x80 {
            system_data.cycles = 3;
            let pc_dest: i8 = (system_data.mem_map[(registers.program_counter + 1) as usize] + 2) as i8;
            registers.program_counter = (registers.program_counter as i32 + pc_dest as i32) as u16;
        }
        else {
            system_data.cycles = 2;
            registers.program_counter += 2;
        }
}

pub fn load_decrement_hl_register_location_with_accumulator(system_data: &mut SystemData, registers: &mut Registers)
{
    let mut mem_loc: u16 = registers.l_register as u16 | (registers.h_register as u16) << 8;
    system_data.mem_map[mem_loc as usize] = registers.accumulator;
    mem_loc -= 1;
    registers.l_register = (mem_loc & 0x00FF) as u8;
    registers.h_register = ((mem_loc & 0xFF00) >> 8) as u8;
    system_data.cycles = 2;
    registers.program_counter += 1;
}

pub fn load_hl_address_with_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8)
{
    let mut mem_loc: u16 = registers.l_register as u16 | (registers.h_register as u16) << 8;
    system_data.cycles = 2;
    let mut register_code = (opcode & 0x07) + 1;
    if register_code == 8
    {
        register_code = 0;
    }
    if register_code == 7
    {
        system_data.cycles = 0;
        println!("No Opcode Found");
    }
    else
    {
        system_data.mem_map[mem_loc as usize] = registers.mapped_register_getter(register_code);
    }

    registers.program_counter += 1;
}

pub fn load_accumulator_with_de_address(system_data: &mut SystemData, registers: &mut Registers)
{
    system_data.cycles = 2;
    let mut mem_loc: u16 = registers.e_register as u16 | (registers.d_register as u16) << 8;
    registers.accumulator = system_data.mem_map[mem_loc as usize];
    registers.program_counter += 1;
}

///////////////////
//CB
///////////////////

pub fn cb_codes(system_data_original: &mut SystemData, registers_original: &mut Registers)
{
    //Borrow splitting
    let mut system_data = system_data_original;
    let mut registers = registers_original;
    let opcode :u8 = system_data.mem_map[(registers.program_counter + 1) as usize];
    //bit b, r
    if (opcode & 0xC0) == 0x40
    {
        system_data.cycles = 2;
        let test_bit: u8 = (opcode & 0x38) >> 3;
        bit_check_register(&mut system_data, &mut registers, opcode, test_bit)
    }
    else
    {
        println!("No Opcode Found");
    }
}

pub fn bit_check_register(system_data: &mut SystemData, registers: &mut Registers, opcode: u8, test_bit: u8)
{
        if (opcode & 0x07) == 0x04
        {
            registers.flags = registers.flags & 0x10;

            if ((registers.h_register >> test_bit) & 0x01) == 0x00
            {
                registers.flags = registers.flags | 0xA0;
            }
            else
            {
                registers.flags = registers.flags | 0x20;
            }
        }
        else
        {
            system_data.cycles = 0;
            println!("No Opcode Found");
        }
        registers.program_counter += 2;
}<|endoftext|>