<commit_before>pub mod responses {

    use std::fmt;

    use serde::Deserialize;
    use serde_json as json;

    #[derive(Deserialize, Debug)]
    pub struct AuthResponse {
        pub session: SessionResponse,
    }

    #[derive(Deserialize, Debug, Clone)]
    pub struct SessionResponse {
        pub key: String,
        pub subscriber: i64,
        pub name: String,
    }

    #[derive(Deserialize)]
    pub struct NowPlayingResponseWrapper {
        pub nowplaying: NowPlayingResponse,
    }

    #[derive(Deserialize, Debug)]
    pub struct NowPlayingResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
    }

    #[derive(Deserialize)]
    pub struct ScrobbleResponseWrapper {
        pub scrobbles: SingleScrobble,
    }

    #[derive(Deserialize)]
    pub struct SingleScrobble {
        pub scrobble: ScrobbleResponse,
    }

    #[derive(Deserialize, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleList"]
    #[CollectionDerives = "Debug, Deserialize"]
    pub struct ScrobbleResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
        pub timestamp: String,
        //  TODO: Ignored field here? (#20)
    }

    #[derive(Debug)]
    pub struct BatchScrobbleResponse {
        pub scrobbles: ScrobbleList,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbleResponseWrapper {
        pub scrobbles: BatchScrobbles,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbles {
        #[serde(rename = "scrobble")]
        pub scrobbles: ScrobbleList,
    }

    #[derive(Deserialize, Debug)]
    pub struct CorrectableString {
        #[serde(deserialize_with = "CorrectableString::deserialize_corrected_field")]
        pub corrected: bool,
        #[serde(rename = "#text", default)]
        pub text: String,
    }

    impl CorrectableString {
        fn deserialize_corrected_field<'de, D>(de: D) -> Result<bool, D::Error>
        where
            D: serde::Deserializer<'de>,
        {
            let deser_result: json::Value = r#try!(serde::Deserialize::deserialize(de));
            match deser_result {
                json::Value::String(ref s) if &*s == "1" => Ok(true),
                json::Value::String(ref s) if &*s == "0" => Ok(false),
                _ => Err(serde::de::Error::custom("Unexpected value")),
            }
        }
    }

    impl fmt::Display for CorrectableString {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", self.text)
        }
    }
}

pub mod metadata {

    use std::collections::HashMap;

    /// Repesents a single track play (aka a "scrobble")
    #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleBatch"]
    #[CollectionDoc = "A batch of Scrobbles to be submitted to Last.fm together."]
    #[CollectionDerives = "Clone, Debug"]
    pub struct Scrobble {
        artist: String,
        track: String,
        album: String,

        timestamp: Option<u64>,
    }

    impl Scrobble {
        /// Constructs a new Scrobble instance, representing a music track
        /// played in the past.
        pub fn new(artist: &str, track: &str, album: &str) -> Scrobble {
            Scrobble {
                artist: artist.to_owned(),
                track: track.to_owned(),
                album: album.to_owned(),
                timestamp: None,
            }
        }

        pub fn with_timestamp(&mut self, timestamp: u64) -> &mut Scrobble {
            self.timestamp = Some(timestamp);
            self
        }

        /// Converts the Scrobble metadata (track name, artist & album name)
        /// into a HashMap. Map keys are `"track"`, `"artist"` and `"album"`,
        /// respectively.
        pub fn as_map(&self) -> HashMap<String, String> {
            let mut params = HashMap::new();
            params.insert("track".to_string(), self.track.clone());
            params.insert("artist".to_string(), self.artist.clone());
            params.insert("album".to_string(), self.album.clone());

            if let Some(timestamp) = self.timestamp {
                params.insert("timestamp".to_string(), timestamp.to_string());
            }

            params
        }

        pub fn artist(&self) -> &str {
            &self.artist
        }

        pub fn track(&self) -> &str {
            &self.track
        }

        pub fn album(&self) -> &str {
            &self.album
        }
    }
}
<commit_msg>tests for mod models<commit_after>pub mod responses {

    use std::fmt;

    use serde::Deserialize;
    use serde_json as json;

    #[derive(Deserialize, Debug)]
    pub struct AuthResponse {
        pub session: SessionResponse,
    }

    #[derive(Deserialize, Debug, Clone)]
    pub struct SessionResponse {
        pub key: String,
        pub subscriber: i64,
        pub name: String,
    }

    #[derive(Deserialize)]
    pub struct NowPlayingResponseWrapper {
        pub nowplaying: NowPlayingResponse,
    }

    #[derive(Deserialize, Debug)]
    pub struct NowPlayingResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
    }

    #[derive(Deserialize)]
    pub struct ScrobbleResponseWrapper {
        pub scrobbles: SingleScrobble,
    }

    #[derive(Deserialize)]
    pub struct SingleScrobble {
        pub scrobble: ScrobbleResponse,
    }

    #[derive(Deserialize, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleList"]
    #[CollectionDerives = "Debug, Deserialize"]
    pub struct ScrobbleResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
        pub timestamp: String,
        //  TODO: Ignored field here? (#20)
    }

    #[derive(Debug)]
    pub struct BatchScrobbleResponse {
        pub scrobbles: ScrobbleList,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbleResponseWrapper {
        pub scrobbles: BatchScrobbles,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbles {
        #[serde(rename = "scrobble")]
        pub scrobbles: ScrobbleList,
    }

    #[derive(Deserialize, Debug)]
    pub struct CorrectableString {
        #[serde(deserialize_with = "CorrectableString::deserialize_corrected_field")]
        pub corrected: bool,
        #[serde(rename = "#text", default)]
        pub text: String,
    }

    impl CorrectableString {
        fn deserialize_corrected_field<'de, D>(de: D) -> Result<bool, D::Error>
        where
            D: serde::Deserializer<'de>,
        {
            let deser_result: json::Value = r#try!(serde::Deserialize::deserialize(de));
            match deser_result {
                json::Value::String(ref s) if &*s == "1" => Ok(true),
                json::Value::String(ref s) if &*s == "0" => Ok(false),
                _ => Err(serde::de::Error::custom("Unexpected value")),
            }
        }
    }

    impl fmt::Display for CorrectableString {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", self.text)
        }
    }
}

pub mod metadata {

    use std::collections::HashMap;

    /// Repesents a single track play (aka a "scrobble")
    #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleBatch"]
    #[CollectionDoc = "A batch of Scrobbles to be submitted to Last.fm together."]
    #[CollectionDerives = "Clone, Debug"]
    pub struct Scrobble {
        artist: String,
        track: String,
        album: String,

        timestamp: Option<u64>,
    }

    impl Scrobble {
        /// Constructs a new Scrobble instance, representing a music track
        /// played in the past.
        pub fn new(artist: &str, track: &str, album: &str) -> Scrobble {
            Scrobble {
                artist: artist.to_owned(),
                track: track.to_owned(),
                album: album.to_owned(),
                timestamp: None,
            }
        }

        pub fn with_timestamp(&mut self, timestamp: u64) -> &mut Scrobble {
            self.timestamp = Some(timestamp);
            self
        }

        /// Converts the Scrobble metadata (track name, artist & album name)
        /// into a HashMap. Map keys are `"track"`, `"artist"` and `"album"`,
        /// respectively.
        pub fn as_map(&self) -> HashMap<String, String> {
            let mut params = HashMap::new();
            params.insert("track".to_string(), self.track.clone());
            params.insert("artist".to_string(), self.artist.clone());
            params.insert("album".to_string(), self.album.clone());

            if let Some(timestamp) = self.timestamp {
                params.insert("timestamp".to_string(), timestamp.to_string());
            }

            params
        }

        pub fn artist(&self) -> &str {
            &self.artist
        }

        pub fn track(&self) -> &str {
            &self.track
        }

        pub fn album(&self) -> &str {
            &self.album
        }
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn make_scrobble() {
            let mut scrobble = Scrobble::new(
                "foo floyd and the fruit flies",
                "old bananas",
                "old bananas",
            );
            scrobble.with_timestamp(1337);
            assert_eq!(scrobble.artist(), "foo floyd and the fruit flies");
            assert_eq!(scrobble.track(), "old bananas");
            assert_eq!(scrobble.album(), "old bananas");
            assert_eq!(scrobble.timestamp, Some(1337));
        }

        #[test]
        fn make_scrobble_check_map() {
            let scrobble = Scrobble::new(
                "foo floyd and the fruit flies",
                "old bananas",
                "old bananas",
            );

            let params = scrobble.as_map();
            assert_eq!(params["artist"], "foo floyd and the fruit flies");
            assert_eq!(params["track"], "old bananas");
            assert_eq!(params["album"], "old bananas");
        }
    }
}
<|endoftext|>


<commit_before>use serde_json;
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::Path;

use search::SearchIndex;

#[derive(Deserialize, Debug)]
pub struct User {
    // NOTE: when changing these fields, be sure to update
    // `.remove_empty_strings()` and `.with_str_fields()` below
    pub name: Option<String>,
    pub irc: Option<String>,
    #[serde(default)]
    pub irc_channels: Vec<String>,
    #[serde(default)]
    pub show_avatar: bool,
    pub email: Option<String>,
    pub discourse: Option<String>,
    pub reddit: Option<String>,
    pub twitter: Option<String>,
    pub blog: Option<String>,
    pub website: Option<String>,
    pub notes: Option<String>,
}

impl User {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<User, serde_json::Error> {
        let reader = BufReader::new(File::open(path)?);
        let mut user: User = serde_json::from_reader(reader)?;
        user.remove_empty_strings();
        for channel in &mut user.irc_channels {
            if channel.starts_with("#") {
                *channel = channel.trim_left_matches('#').to_string();
            }
        }
        Ok(user)
    }

    /// Some users use empty strings to mean "not applicable", instead of using
    /// `null` or omitting the field as they should be doing. We fix up their
    /// carelessness here.
    fn remove_empty_strings(&mut self) {
        macro_rules! fixup {
            ($($field:ident)*) => {
                $(
                    // If the field is just whitespace, replace it with `None`
                    // This boolean dance is needed to satisfy borrowck
                    let should_replace = match self.$field {
                        Some(ref s) if is_whitespace(s) => true,
                        _ => false,
                    };
                    if should_replace {
                        self.$field = None;
                    }
                )*
            }
        }
        fixup!(name irc email discourse reddit twitter blog website notes);
    }

    /// Applies the given callback to every searchable field in this entry.
    ///
    /// Used by the full-text search machinery.
    fn with_str_fields<F>(&self, mut callback: F) where F: FnMut(&str, u64) {
        macro_rules! callme {
            ($callback:ident, $($field:ident ($weight:expr))*) => {
                $(
                    if let Some(ref s) = self.$field {
                        $callback(s, $weight);
                    }
                )*
            }
        }
        callme!(
            callback,
            // Give names more weight than URLs and notes
            name(20) irc(10) email(1) discourse(10) reddit(10)
            twitter(10) blog(1) website(1) notes(1)
            );
        for channel in &self.irc_channels {
            callback(channel, 1);
        }
    }
}

fn is_whitespace(s: &str) -> bool {
    s.chars().all(char::is_whitespace)
}

#[derive(Debug)]
pub struct Users {
    data: BTreeMap<String, Result<User, String>>,
    index: SearchIndex<String>,
}

impl Users {
    pub fn load<P: AsRef<Path>>(base: P) -> Result<Users, serde_json::Error> {
        let mut data = BTreeMap::new();
        for entry in fs::read_dir(base)? {
            let path = entry?.path();
            if path.extension() == Some(OsStr::new("json")) {
                let id = path.file_stem().unwrap().to_string_lossy().into_owned();
                // Some users' entries actually fail to parse!
                // Instead of bailing on these, just record the error and move on.
                let user = User::from_path(&path).map_err(|e| {
                    warn!("could not parse entry for {}: {}", id, e);
                    e.to_string()
                });
                data.insert(id, user);
            }
        }
        let mut index = SearchIndex::new();
        for (id, user) in &data {
            index.add(id.clone(), id, 10);
            if let Ok(ref user) = *user {
                user.with_str_fields(|s, w| index.add(id.clone(), s, w));
            }
        }
        info!("loaded {} rustaceans", data.len());
        Ok(Users { data: data, index: index })
    }

    pub fn get(&self, id: &str) -> Option<Result<&User, &str>> {
        self.data.get(id).map(|r| r.as_ref().map_err(|e| &e[..]))
    }

    pub fn search(&self, query: &str) -> (Vec<(String, u64)>, Option<String>) {
        self.index.query(query)
    }
}

#[test]
fn smoke() {
    const DATA: &'static str = r#"
        {
            "name": "Bors",
            "show_avatar": true,
            "irc": "bors",
            "irc_channels": ["rust", "rust-bots"],
            "website": "http://rust-lang.org/"
        }
    "#;
    let _: User = serde_json::from_str(DATA).unwrap();
}
<commit_msg>Fix Clippy warnings (again)<commit_after>use serde_json;
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::Path;

use search::SearchIndex;

#[derive(Deserialize, Debug)]
pub struct User {
    // NOTE: when changing these fields, be sure to update
    // `.remove_empty_strings()` and `.with_str_fields()` below
    pub name: Option<String>,
    pub irc: Option<String>,
    #[serde(default)]
    pub irc_channels: Vec<String>,
    #[serde(default)]
    pub show_avatar: bool,
    pub email: Option<String>,
    pub discourse: Option<String>,
    pub reddit: Option<String>,
    pub twitter: Option<String>,
    pub blog: Option<String>,
    pub website: Option<String>,
    pub notes: Option<String>,
}

impl User {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<User, serde_json::Error> {
        let reader = BufReader::new(File::open(path)?);
        let mut user: User = serde_json::from_reader(reader)?;
        user.remove_empty_strings();
        for channel in &mut user.irc_channels {
            if channel.starts_with('#') {
                *channel = channel.trim_left_matches('#').to_string();
            }
        }
        Ok(user)
    }

    /// Some users use empty strings to mean "not applicable", instead of using
    /// `null` or omitting the field as they should be doing. We fix up their
    /// carelessness here.
    fn remove_empty_strings(&mut self) {
        macro_rules! fixup {
            ($($field:ident)*) => {
                $(
                    // If the field is just whitespace, replace it with `None`
                    // This boolean dance is needed to satisfy borrowck
                    let should_replace = match self.$field {
                        Some(ref s) if is_whitespace(s) => true,
                        _ => false,
                    };
                    if should_replace {
                        self.$field = None;
                    }
                )*
            }
        }
        fixup!(name irc email discourse reddit twitter blog website notes);
    }

    /// Applies the given callback to every searchable field in this entry.
    ///
    /// Used by the full-text search machinery.
    fn with_str_fields<F>(&self, mut callback: F) where F: FnMut(&str, u64) {
        macro_rules! callme {
            ($callback:ident, $($field:ident ($weight:expr))*) => {
                $(
                    if let Some(ref s) = self.$field {
                        $callback(s, $weight);
                    }
                )*
            }
        }
        callme!(
            callback,
            // Give names more weight than URLs and notes
            name(20) irc(10) email(1) discourse(10) reddit(10)
            twitter(10) blog(1) website(1) notes(1)
            );
        for channel in &self.irc_channels {
            callback(channel, 1);
        }
    }
}

fn is_whitespace(s: &str) -> bool {
    s.chars().all(char::is_whitespace)
}

#[derive(Debug)]
pub struct Users {
    data: BTreeMap<String, Result<User, String>>,
    index: SearchIndex<String>,
}

impl Users {
    pub fn load<P: AsRef<Path>>(base: P) -> Result<Users, serde_json::Error> {
        let mut data = BTreeMap::new();
        for entry in fs::read_dir(base)? {
            let path = entry?.path();
            if path.extension() == Some(OsStr::new("json")) {
                let id = path.file_stem().unwrap().to_string_lossy().into_owned();
                // Some users' entries actually fail to parse!
                // Instead of bailing on these, just record the error and move on.
                let user = User::from_path(&path).map_err(|e| {
                    warn!("could not parse entry for {}: {}", id, e);
                    e.to_string()
                });
                data.insert(id, user);
            }
        }
        let mut index = SearchIndex::new();
        for (id, user) in &data {
            index.add(id.clone(), id, 10);
            if let Ok(ref user) = *user {
                user.with_str_fields(|s, w| index.add(id.clone(), s, w));
            }
        }
        info!("loaded {} rustaceans", data.len());
        Ok(Users { data: data, index: index })
    }

    pub fn get(&self, id: &str) -> Option<Result<&User, &str>> {
        self.data.get(id).map(|r| r.as_ref().map_err(|e| &e[..]))
    }

    pub fn search(&self, query: &str) -> (Vec<(String, u64)>, Option<String>) {
        self.index.query(query)
    }
}

#[test]
fn smoke() {
    const DATA: &'static str = r#"
        {
            "name": "Bors",
            "show_avatar": true,
            "irc": "bors",
            "irc_channels": ["rust", "rust-bots"],
            "website": "http://rust-lang.org/"
        }
    "#;
    let _: User = serde_json::from_str(DATA).unwrap();
}
<|endoftext|>


<commit_before>//! Checks if an AST is valid

use ast::*;
use ast::span::*;
use ast::visit::*;

use term::Terminal;

use std::fmt;
use std::io::{self, Write};

#[derive(Clone, Debug)]
pub struct CheckError {
    pub msg: &'static str,
    pub detail: Option<String>,
    pub span: Span,
}

impl CheckError {
    /// Prints this `CheckError` to a terminal.
    pub fn format<W: Write>(&self, code: &str, source_name: &str, t: &mut Terminal<Output=W>)
    -> io::Result<()> {
        let mut msg = self.msg.to_string();
        if let Some(ref detail) = self.detail {
            msg.push_str(detail.as_ref());
        }
        try!(self.span.print_with_err(code, source_name, msg.as_ref(), t));

        Ok(())
    }
}

impl fmt::Display for CheckError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.detail {
            None => {
                fmt.write_fmt(format_args!("{}", self.msg))
            },
            Some(ref detail) => {
                fmt.write_fmt(format_args!("{}: {}", self.msg, detail))
            }
        }
    }
}

struct Checker {
    errs: Vec<CheckError>,
    vararg_func: bool,
    looplvl: u32,
}

impl <'a> Visitor<'a> for Checker {
    fn visit_stmt(&mut self, s: &Stmt) {
        match s.value {
            SBreak => {
                if self.looplvl == 0 {
                    self.errs.push(CheckError {
                        msg: "use of `break` outside of loop",
                        detail: None,
                        span: s.span,
                    });
                }
            },
            SFor{..} | SWhile{..} | SRepeat{..} => {
                self.looplvl += 1;
                walk_stmt_ref(s, self);
                self.looplvl -= 1;
            },
            _ => walk_stmt_ref(s, self),
        }
    }

    fn visit_expr(&mut self, e: &Expr) {
        match e.value {
            EVarArgs => {
                if !self.vararg_func {
                    self.errs.push(CheckError {
                        msg: "use of ... outside varargs function",
                        detail: None,
                        span: e.span,
                    });
                }
            },
            _ => {},
        }
    }

    fn visit_func(&mut self, func: &Function) {
        let res = check_func(func);
        if let Err(mut new_errs) = res {
            self.errs.append(&mut new_errs);
        }
    }
}

/// Checks that the passed function contains only valid statements.
///
/// Returns a non-empty `Vec<CheckError>` if any errors were found.
pub fn check_func(func: &Function) -> Result<(), Vec<CheckError>> {
    let mut ch = Checker {
        errs: vec![],
        vararg_func: func.varargs,
        looplvl: 0,
    };

    ch.visit_block(&func.body);

    if ch.errs.len() == 0 {
        Ok(())
    } else {
        Err(ch.errs)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::parse_main;

    #[test]
    fn test() {
        assert!(check_func(&parse_main("break").unwrap()).is_err());
        assert!(check_func(&parse_main("for i=0,1 do break end").unwrap()).is_ok());
        assert!(check_func(&parse_main("i = ...").unwrap()).is_ok());
        assert!(check_func(&parse_main(r#"
function f(a)
    a = ...
end
"#).unwrap()).is_err());
    }
}
<commit_msg>check: remove detail field<commit_after>//! Checks if an AST is valid

use ast::*;
use ast::span::*;
use ast::visit::*;

use term::Terminal;

use std::io::{self, Write};

#[derive(Clone, Debug)]
pub struct CheckError {
    pub msg: &'static str,
    pub span: Span,
}

impl CheckError {
    /// Prints this `CheckError` to a terminal.
    pub fn format<W: Write>(&self, code: &str, source_name: &str, t: &mut Terminal<Output=W>)
    -> io::Result<()> {
        try!(self.span.print_with_err(code, source_name, self.msg, t));

        Ok(())
    }
}

struct Checker {
    errs: Vec<CheckError>,
    vararg_func: bool,
    looplvl: u32,
}

impl <'a> Visitor<'a> for Checker {
    fn visit_stmt(&mut self, s: &Stmt) {
        match s.value {
            SBreak => {
                if self.looplvl == 0 {
                    self.errs.push(CheckError {
                        msg: "use of `break` outside of loop",
                        span: s.span,
                    });
                }
            },
            SFor{..} | SWhile{..} | SRepeat{..} => {
                self.looplvl += 1;
                walk_stmt_ref(s, self);
                self.looplvl -= 1;
            },
            _ => walk_stmt_ref(s, self),
        }
    }

    fn visit_expr(&mut self, e: &Expr) {
        match e.value {
            EVarArgs => {
                if !self.vararg_func {
                    self.errs.push(CheckError {
                        msg: "use of ... outside varargs function",
                        span: e.span,
                    });
                }
            },
            _ => {},
        }
    }

    fn visit_func(&mut self, func: &Function) {
        let varargs_old = self.vararg_func;
        let looplvl_old = self.looplvl;
        self.vararg_func = func.varargs;
        self.looplvl = 0;

        self.visit_block(&func.body);

        self.vararg_func = varargs_old;
        self.looplvl = looplvl_old;
    }
}

/// Checks that the passed function contains only valid statements.
///
/// Returns a non-empty `Vec<CheckError>` if any errors were found.
pub fn check_func(func: &Function) -> Result<(), Vec<CheckError>> {
    let mut ch = Checker {
        errs: vec![],
        vararg_func: func.varargs,
        looplvl: 0,
    };

    ch.visit_block(&func.body);

    if ch.errs.len() == 0 {
        Ok(())
    } else {
        Err(ch.errs)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::parse_main;

    #[test]
    fn test() {
        assert!(check_func(&parse_main("break").unwrap()).is_err());
        assert!(check_func(&parse_main("for i=0,1 do break end").unwrap()).is_ok());
        assert!(check_func(&parse_main("i = ...").unwrap()).is_ok());
        assert!(check_func(&parse_main(r#"
for i=0,1 do
    function i() break end
end
"#).unwrap()).is_err());
        assert!(check_func(&parse_main(r#"
function f(a)
    a = ...
end
"#).unwrap()).is_err());
    }
}
<|endoftext|>


<commit_before>extern crate git2;

use git2::*;
use std::process::Command;
use std::path::Path;

pub trait RepoHost  {
    fn remote_url(&self, &str) -> String;
    fn create_project(&self, &str) -> Result<(), Error>;

}

pub struct Scratch<'a> {
    repo: Repository,
    host: &'a RepoHost,
}

impl<'a> Scratch<'a> {
    pub fn new(path: &Path, host: &'a RepoHost) -> Scratch<'a> {
        Scratch {
            repo: Repository::init_bare(&path).expect("could not init scratch"),
            host: host,
        }
    }

    fn tracking(&self, module: &str, branch: &str) -> Object {
        println!("tracking_branch remotes/{}/{}",module,branch);

        let remote_name = format!("{}", module);
        let remote_url = self.host.remote_url(&module);
        println!("  create remote (remote_name:{}, remote_url:{})",
            &remote_name,
            &remote_url
        );
        if !self.repo.find_remote(&remote_name).is_ok() {
            self.repo.remote(&remote_name, &remote_url).expect("can't create remote");
        }

        self.call_git("fetch --all").expect("could not fetch");
        self.repo.revparse_single(&format!("remotes/{}/{}",module,branch))
            .expect("no tracking branch")
    }

    fn call_git(self: &Scratch<'a>, cmd: &str) -> Result<(), Error> {
        println!("Scratch::call_git ({:?}): git {}", &self.repo.path(), &cmd);
        let args: Vec<&str> = cmd.split(" ").collect();
        let repo_path = get_repo_path(&self.repo);
        let mut c = Command::new("git");
        c.env("GIT_DIR", repo_path.as_os_str());
        c.args(&args);

        println!("call_git {:?} command in {:?}", c, repo_path);
        let output = c
            .output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
        println!("{}{}",
                 String::from_utf8_lossy(&output.stdout),
                 String::from_utf8_lossy(&output.stderr));
        Ok(())
    }

    fn create_projects(&self,
             central: &str,
             rev: &str)
        -> Result<(), Error> {

        println!(" ####### create_projects scratch repo for remote: {}\n #######\tlocation: {:?}",
            &self.host.remote_url(central),
            &self.repo.path()
        );

        // create remote for each module
        for module in self.module_paths(rev).unwrap().iter() {
            try!(self.host.create_project(module));
        }

        Ok(())
    }

    // force push of the new revision-object to temp repo
    fn transfer(&self, rev: &str, source: &Path) {
        let target = get_repo_path(&self.repo);
        println!("---> transfer_to_scratch in {}", source.display());
        //" create tmp branch
        call_command("git", &["branch", "-f", "tmp", rev], Some(source));
        // force push
        call_command(
            "git", &["push", "--force", &target.to_string_lossy(), "tmp"], Some(source)
        );
        // delete tmp branch
        call_command("git", &["branch", "-D", "tmp"], Some(source));
        println!("<--- transfer_to_scratch done...");
    }

    // takes everything from base except it's tree and replaces it with the tree given
    fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Result<Oid, Error> {
        if parents.len() != 0 {
            try!(self.repo.set_head_detached(parents[0].id()));
        }
        self.repo.commit(
            Some("HEAD"),
            &base.author(),
            &base.committer(),
            &base.message().unwrap_or("no message"),
            tree,
            parents
        )
    }

    fn push(&self, oid: Oid, module: &str, target: &str) {
        let commit = &self.repo.find_commit(oid).expect("can't find commit");
        self.repo.set_head_detached(commit.id()).expect("can't detach HEAD");
        self.call_git(
            &format!("push {} HEAD:{}", self.host.remote_url(module), target)
        ).expect("can't push");
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Tree {
        let oid = tree.get_path(path).map(|x| x.id()).expect("can't find subtree");
        return self.repo.find_tree(oid).expect("can't find oid");
    }

    fn module_to_subfolder(&self,
                           module_path: &Path,
                           module_tree: &Tree,
                           master_tree: &Tree)
        -> Result<Oid, Error>
    {
        assert!(module_path.components().count() == 2); // FIXME: drop this requirement
        let parent_path = module_path.parent().expect("module not in subdir");
        let module_name = module_path.file_name().expect("no module name");

        let modules_tree = {
            let mut builder =
                try!(self.repo.treebuilder(Some(&self.subtree(master_tree, parent_path))));
            try!(builder.insert(module_name, module_tree.id(), 0o0040000)); // GIT_FILEMODE_TREE
            try!(builder.write())
        };

        let full_tree = {
            let mut builder = try!(self.repo.treebuilder(Some(master_tree)));
            try!(builder.insert(parent_path, modules_tree, 0o0040000)); // GIT_FILEMODE_TREE
            try!(builder.write())
        };
        println!("module_to_subfolder {}", full_tree);
        Ok(full_tree)
    }

    fn module_paths(&self, rev: &str) -> Result<Vec<String>, Error> {
        println!("---> get_module_paths");
        let object = try!(self.repo.revparse_single(rev));
        let commit = try!(object.as_commit()
                          .ok_or(Error::from_str("could not get commit from obj")));
        let tree: Tree = try!(commit.tree());

        let tree_object = try!(tree.get_path(&Path::new("modules")));
        let modules_o = try!(tree_object.to_object(&self.repo));
        let modules = try!(modules_o.as_tree()
                           .ok_or(Error::from_str("could not get tree from path")));

        let mut names = Vec::<String>::new();
        for module in modules.iter() {
            names.push(format!("modules/{}",try!(module.name()
                            .ok_or(Error::from_str("could not get name for module")))
                       .to_string()));
        }
        println!("<--- get_module_paths returns: {:?}", names);
        Ok(names)
    }
}

pub fn module_review_upload(module: &str,
                            scratch: &Scratch,
                            newrev: &str,
                            central: &str) -> Result<(), Error> {
    println!("in module_review_upload for module {}", &module);
    scratch.transfer(newrev,Path::new("."));

    let mut parent_commit_oid: Oid = scratch.tracking(central, "master").id();

    let object = scratch.tracking(&module, "master").id();
    let oldrev = format!("{}", object);

    {
        let old = try!(scratch.repo.revparse_single(&oldrev).map(|x| x.id()));
        let new = try!(scratch.repo.revparse_single(&newrev).map(|x| x.id()));

        if !try!(scratch.repo.graph_descendant_of(new, old)) {
            println!(".");
            println!("===========================================================");
            println!("======== Commit not based on master, rebase first! ========");
            println!("===========================================================");
            return Ok(());
        }
    }

    let walk = {
        let mut walk = try!(scratch.repo.revwalk());
        walk.set_sorting(SORT_REVERSE | SORT_TIME);
        try!(walk.push_range(&format!("{}..{}", oldrev, newrev)));
        walk
    };

    println!("===== module path: {}", module);
    println!("===== Rewrite commits from {} to {}", oldrev, newrev);

    for rev in walk {
        let newrev = format!("{}", try!(rev));
        if oldrev == newrev {
            continue;
        }
        println!("===== Rewrite commit {}", newrev);

        let module_commit_obj = try!(scratch.repo.revparse_single(&newrev));
        let module_commit = try!(module_commit_obj.as_commit()
                                 .ok_or(Error::from_str("object is not actually a commit")));
        let module_tree = try!(module_commit.tree());

        let parent_commit = try!(scratch.repo.find_commit(parent_commit_oid));

        let new_tree = {
            let master_tree: Tree = try!(parent_commit.tree());
            let new_tree_oid = try!(
                scratch.module_to_subfolder(Path::new(module), &module_tree, &master_tree)
            );
            try!(scratch.repo.find_tree(new_tree_oid))
        };

        parent_commit_oid =
            try!(scratch.rewrite(module_commit, &vec![&parent_commit], &new_tree));
    }

    println!("");
    println!("");
    println!("===================== Doing actual upload in central git ========================");

    scratch.push(parent_commit_oid, central, "refs/for/master");

    // let commit = &try!(scratch.repo.find_commit(parent_commit_oid));
    // try!(scratch.repo.set_head_detached(commit.id()));
    // try!(scratch.call_git(
    //     &format!("push {} HEAD:{}", &host.remote_url(central), "refs/for/master")
    // ));
    println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
    Ok(())
}

pub fn central_submit(newrev: &str,//sha1 of refered commit
                      host: &RepoHost,
                      central: &str,
                      repo_path: &Path,
                      scratch: &Scratch) -> Result<(), Error> {
    println!(" ---> central_submit (remote addr:{}, sha1 of commit: {})",
        &host.remote_url(central),
        &newrev
    );

    println!("    ########### SCRATCH: create scratch repo ########### ");
    scratch.transfer(newrev, &repo_path);
    try!(scratch.create_projects(
        &central,
        newrev,
    ));

    let central_commit_obj = try!(scratch.repo.revparse_single(newrev));
    let central_commit = try!(central_commit_obj.as_commit()
                              .ok_or(Error::from_str("could not get commit from obj")));
    let central_tree = try!(central_commit.tree());
    println!("    ########### create {} branch in scratch.repo and point to the central_commit ########### ", &central);
    // create central branch in scratch.repo and point to the central_commit
    // is identical to master in central
    // marker for other hooks
    try!(scratch.repo.branch(central, central_commit, true));

    for module_path in scratch.module_paths(newrev).unwrap() {
        println!(" ####### prepare commit from scratch.repo to module {}", &module_path);
        let module_master_commit_obj = scratch.tracking(&module_path, "master");
        let module_master_commit = try!(module_master_commit_obj.as_commit()
                                        .ok_or(Error::from_str("could not get commit from obj")));

        let parents = vec![module_master_commit];

        println!("\tpath for module in central repository... => {:?}", module_path);

        // new tree is sub-tree of complete central tree
        let new_tree_oid = try!(central_tree.get_path(&Path::new(&module_path))).id();
        let old_tree_oid = try!(module_master_commit.tree()).id();

        // if sha1's are equal the content is equal
        if new_tree_oid != old_tree_oid {
            println!("\tmodule repository for {} is behind, => updating", &module_path);

            let new_tree = try!(scratch.repo.find_tree(new_tree_oid));

            let module_commit =
                try!(scratch.rewrite(central_commit, &parents, &new_tree));
            // do the push to the module git

            scratch.push(module_commit, &module_path, "master");
        }
    }
    Ok(())
}

pub fn call_command(command: &str, args: &[&str], cwd: Option<&Path>)
{
    let mut c = Command::new(&command);
    c.args(args);
    println!("call_git {:?} (in {:?})", c, cwd);
    if let Some(path) = cwd {
        // c.env("GIT_DIR", format!("{}.git",path.to_str().unwrap()));
        c.current_dir(path);
    }
    let output = c
        .output()
        .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
    println!("{}{}",
             String::from_utf8_lossy(&output.stdout),
             String::from_utf8_lossy(&output.stderr));
}

pub fn get_repo_path(repo: &Repository) -> &Path {
    if repo.is_bare() {
        return repo.path();
    }
    return repo.workdir().expect("get workdir from repo");
}

<commit_msg>some simplifications<commit_after>extern crate git2;

use git2::*;
use std::process::Command;
use std::path::Path;

pub trait RepoHost  {
    fn remote_url(&self, &str) -> String;
    fn create_project(&self, &str) -> Result<(), Error>;

}

pub struct Scratch<'a> {
    repo: Repository,
    host: &'a RepoHost,
}

impl<'a> Scratch<'a> {
    pub fn new(path: &Path, host: &'a RepoHost) -> Scratch<'a> {
        Scratch {
            repo: Repository::init_bare(&path).expect("could not init scratch"),
            host: host,
        }
    }

    fn tracking(&self, module: &str, branch: &str) -> Object {
        println!("tracking_branch remotes/{}/{}",module,branch);

        let remote_name = format!("{}", module);
        let remote_url = self.host.remote_url(&module);
        println!("  create remote (remote_name:{}, remote_url:{})",
            &remote_name,
            &remote_url
        );
        if !self.repo.find_remote(&remote_name).is_ok() {
            self.repo.remote(&remote_name, &remote_url).expect("can't create remote");
        }

        self.call_git("fetch --all").expect("could not fetch");
        self.repo.revparse_single(&format!("remotes/{}/{}",module,branch))
            .expect("no tracking branch")
    }

    fn call_git(self: &Scratch<'a>, cmd: &str) -> Result<(), Error> {
        println!("Scratch::call_git ({:?}): git {}", &self.repo.path(), &cmd);
        let args: Vec<&str> = cmd.split(" ").collect();
        let repo_path = get_repo_path(&self.repo);
        let mut c = Command::new("git");
        c.env("GIT_DIR", repo_path.as_os_str());
        c.args(&args);

        println!("call_git {:?} command in {:?}", c, repo_path);
        let output = c
            .output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
        println!("{}{}",
                 String::from_utf8_lossy(&output.stdout),
                 String::from_utf8_lossy(&output.stderr));
        Ok(())
    }

    fn create_projects(&self,
             central: &str,
             rev: &str)
        -> Result<(), Error> {

        println!(" ####### create_projects scratch repo for remote: {}\n #######\tlocation: {:?}",
            &self.host.remote_url(central),
            &self.repo.path()
        );

        // create remote for each module
        for module in self.module_paths(rev).unwrap().iter() {
            try!(self.host.create_project(module));
        }

        Ok(())
    }

    // force push of the new revision-object to temp repo
    fn transfer(&self, rev: &str, source: &Path) -> Object {
        let target = get_repo_path(&self.repo);
        println!("---> transfer_to_scratch in {}", source.display());
        //" create tmp branch
        call_command("git", &["branch", "-f", "tmp", rev], Some(source));
        // force push
        call_command(
            "git", &["push", "--force", &target.to_string_lossy(), "tmp"], Some(source)
        );
        // delete tmp branch
        call_command("git", &["branch", "-D", "tmp"], Some(source));
        println!("<--- transfer_to_scratch done...");

        let obj = self.repo.revparse_single(rev).expect("can't find transfered ref");
        return obj;
    }

    // takes everything from base except it's tree and replaces it with the tree given
    fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Result<Oid, Error> {
        if parents.len() != 0 {
            try!(self.repo.set_head_detached(parents[0].id()));
        }
        self.repo.commit(
            Some("HEAD"),
            &base.author(),
            &base.committer(),
            &base.message().unwrap_or("no message"),
            tree,
            parents
        )
    }

    fn push(&self, oid: Oid, module: &str, target: &str) {
        let commit = &self.repo.find_commit(oid).expect("can't find commit");
        self.repo.set_head_detached(commit.id()).expect("can't detach HEAD");
        self.call_git(
            &format!("push {} HEAD:{}", self.host.remote_url(module), target)
        ).expect("can't push");
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Tree {
        let oid = tree.get_path(path).map(|x| x.id()).expect("can't find subtree");
        return self.repo.find_tree(oid).expect("can't find oid");
    }

    fn module_to_subfolder(&self,
                           module_path: &Path,
                           module_tree: &Tree,
                           master_tree: &Tree)
        -> Result<Oid, Error>
    {
        assert!(module_path.components().count() == 2); // FIXME: drop this requirement
        let parent_path = module_path.parent().expect("module not in subdir");
        let module_name = module_path.file_name().expect("no module name");

        let modules_tree = {
            let mut builder =
                try!(self.repo.treebuilder(Some(&self.subtree(master_tree, parent_path))));
            try!(builder.insert(module_name, module_tree.id(), 0o0040000)); // GIT_FILEMODE_TREE
            try!(builder.write())
        };

        let full_tree = {
            let mut builder = try!(self.repo.treebuilder(Some(master_tree)));
            try!(builder.insert(parent_path, modules_tree, 0o0040000)); // GIT_FILEMODE_TREE
            try!(builder.write())
        };
        println!("module_to_subfolder {}", full_tree);
        Ok(full_tree)
    }

    fn module_paths(&self, rev: &str) -> Result<Vec<String>, Error> {
        println!("---> get_module_paths");
        let object = try!(self.repo.revparse_single(rev));
        let commit = try!(object.as_commit()
                          .ok_or(Error::from_str("could not get commit from obj")));
        let tree: Tree = try!(commit.tree());

        let tree_object = try!(tree.get_path(&Path::new("modules")));
        let modules_o = try!(tree_object.to_object(&self.repo));
        let modules = try!(modules_o.as_tree()
                           .ok_or(Error::from_str("could not get tree from path")));

        let mut names = Vec::<String>::new();
        for module in modules.iter() {
            names.push(format!("modules/{}",try!(module.name()
                            .ok_or(Error::from_str("could not get name for module")))
                       .to_string()));
        }
        println!("<--- get_module_paths returns: {:?}", names);
        Ok(names)
    }
}

pub fn module_review_upload(module: &str,
                            scratch: &Scratch,
                            newrev: &str,
                            central: &str) -> Result<(), Error> {
    println!("in module_review_upload for module {}", &module);

    let new = scratch.transfer(newrev,Path::new(".")).id();
    let old = scratch.tracking(&module, "master").id();

    if !try!(scratch.repo.graph_descendant_of(new, old)) {
        println!(".");
        println!("===========================================================");
        println!("======== Commit not based on master, rebase first! ========");
        println!("===========================================================");
        return Ok(());
    }

    let walk = {
        let mut walk = try!(scratch.repo.revwalk());
        walk.set_sorting(SORT_REVERSE | SORT_TIME);
        try!(walk.push_range(&format!("{}..{}", old, new)));
        walk
    };

    println!("===== module path: {}", module);
    println!("===== Rewrite commits from {} to {}", old, new);

    let mut parent_commit_oid: Oid = scratch.tracking(central, "master").id();
    for rev in walk {
        let currev = format!("{}", try!(rev));
        let oldrev = format!("{}", old);
        if oldrev == currev {
            continue;
        }
        println!("===== Rewrite commit {}",currev );

        let module_commit_obj = try!(scratch.repo.revparse_single(&currev ));
        let module_commit = try!(module_commit_obj.as_commit()
                                 .ok_or(Error::from_str("object is not actually a commit")));
        let module_tree = try!(module_commit.tree());

        let parent_commit = try!(scratch.repo.find_commit(parent_commit_oid));

        let new_tree = {
            let master_tree: Tree = try!(parent_commit.tree());
            let new_tree_oid = try!(
                scratch.module_to_subfolder(Path::new(module), &module_tree, &master_tree)
            );
            try!(scratch.repo.find_tree(new_tree_oid))
        };

        parent_commit_oid =
            try!(scratch.rewrite(module_commit, &vec![&parent_commit], &new_tree));
    }

    println!("");
    println!("");
    println!("===================== Doing actual upload in central git ========================");

    scratch.push(parent_commit_oid, central, "refs/for/master");

    println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
    Ok(())
}

pub fn central_submit(newrev: &str,//sha1 of refered commit
                      host: &RepoHost,
                      central: &str,
                      repo_path: &Path,
                      scratch: &Scratch) -> Result<(), Error> {
    println!(" ---> central_submit (remote addr:{}, sha1 of commit: {})",
        &host.remote_url(central),
        &newrev
    );

    println!("    ########### SCRATCH: create scratch repo ########### ");
    let central_commit_obj = scratch.transfer(newrev, &repo_path);

    try!(scratch.create_projects(
        &central,
        newrev,
    ));

    let central_commit = try!(central_commit_obj.as_commit()
                              .ok_or(Error::from_str("could not get commit from obj")));
    let central_tree = try!(central_commit.tree());

    for module in scratch.module_paths(newrev).unwrap() {
        println!(" ####### prepare commit from scratch.repo to module {}", &module);
        let module_master_commit_obj = scratch.tracking(&module, "master");
        let parents = vec!(
            module_master_commit_obj.as_commit().expect("could not get commit from obj")
        );

        println!("\tpath for module in central repository... => {:?}", module);

        // new tree is sub-tree of complete central tree
        let old_tree = try!(parents[0].tree());
        let new_tree = try!(
            scratch.repo.find_tree(try!(
                central_tree.get_path(&Path::new(&module))).id()
            )
        );

        // if sha1's are equal the content is equal
        if new_tree.id() != old_tree.id() {
            println!("\tmodule repository for {} is behind, => updating", &module);
            let module_commit = try!(scratch.rewrite(central_commit, &parents, &new_tree));
            scratch.push(module_commit, &module, "master");
        }
    }
    Ok(())
}

pub fn call_command(command: &str, args: &[&str], cwd: Option<&Path>)
{
    let mut c = Command::new(&command);
    c.args(args);
    println!("call_git {:?} (in {:?})", c, cwd);
    if let Some(path) = cwd {
        // c.env("GIT_DIR", format!("{}.git",path.to_str().unwrap()));
        c.current_dir(path);
    }
    let output = c.output().unwrap_or_else(|e| panic!("failed to execute process: {}", e));

    println!("{}{}",
        String::from_utf8_lossy(&output.stdout),
        String::from_utf8_lossy(&output.stderr)
    );
}

pub fn get_repo_path(repo: &Repository) -> &Path {
    if repo.is_bare() {
        return repo.path();
    }
    return repo.workdir().expect("get workdir from repo");
}

<|endoftext|>


<commit_before>extern crate std;
use core::prelude::*;
use self::std::io::Error as IoError;
use stack;

#[cfg(unix)]
#[path = "os/unix.rs"] mod sys;

#[allow(raw_pointer_derive)]
#[derive(Debug)]
pub struct Stack {
  ptr: *mut u8,
  len: usize
}

pub struct StackSource;

impl stack::StackSource for StackSource {
  type Output = Stack;
  type Error = IoError;

  fn get_stack(size: usize) -> Result<Stack, IoError> {
    Stack::new(size)
  }
}

impl stack::Stack for Stack {
  fn top(&mut self) -> *mut u8 {
    unsafe {
      self.ptr.offset(self.len as isize)
    }
  }

  fn limit(&self) -> *const u8 {
    unsafe {
      self.ptr.offset(sys::page_size() as isize)
    }
  }
}

impl Stack {
  fn new(size: usize) -> Result<Stack, IoError> {
    let page_size = sys::page_size();

    // round the page size up,
    // using the fact that it is a power of two
    let len = (size + page_size - 1) & !(page_size - 1);

    let stack = unsafe {
      let ptr = try!(match sys::map_stack(size) {
        None => Err(IoError::last_os_error()),
        Some(ptr) => Ok(ptr)
      });

      Stack { ptr: ptr as *mut u8, len: len }
    };

    try!(unsafe {
      if sys::protect_stack(stack.ptr) { Ok(()) }
      else { Err(IoError::last_os_error()) }
    });

    Ok(stack)
  }
}

impl Drop for Stack {
  fn drop(&mut self) {
    unsafe {
      if !sys::unmap_stack(self.ptr, self.len) {
        panic!("munmap for stack {:p} of size {} failed: {}",
               self.ptr, self.len, IoError::last_os_error())
      }
    }
  }
}
<commit_msg>rearrange the os::{Stack, StackSource} code a little<commit_after>extern crate std;
use core::prelude::*;
use self::std::io::Error as IoError;
use stack;

#[cfg(unix)]
#[path = "os/unix.rs"] mod sys;

pub struct StackSource;

#[allow(raw_pointer_derive)]
#[derive(Debug)]
pub struct Stack {
  ptr: *mut u8,
  len: usize
}

impl stack::StackSource for StackSource {
  type Output = Stack;
  type Error = IoError;

  fn get_stack(size: usize) -> Result<Stack, IoError> {
    let page_size = sys::page_size();

    // round the page size up,
    // using the fact that it is a power of two
    let len = (size + page_size - 1) & !(page_size - 1);

    let stack = unsafe {
      let ptr = try!(match sys::map_stack(size) {
        None => Err(IoError::last_os_error()),
        Some(ptr) => Ok(ptr)
      });

      Stack { ptr: ptr as *mut u8, len: len }
    };

    try!(unsafe {
      if sys::protect_stack(stack.ptr) { Ok(()) }
      else { Err(IoError::last_os_error()) }
    });

    Ok(stack)
  }
}

impl stack::Stack for Stack {
  fn top(&mut self) -> *mut u8 {
    unsafe {
      self.ptr.offset(self.len as isize)
    }
  }

  fn limit(&self) -> *const u8 {
    unsafe {
      self.ptr.offset(sys::page_size() as isize)
    }
  }
}

impl Drop for Stack {
  fn drop(&mut self) {
    unsafe {
      if !sys::unmap_stack(self.ptr, self.len) {
        panic!("munmap for stack {:p} of size {} failed: {}",
               self.ptr, self.len, IoError::last_os_error())
      }
    }
  }
}
<|endoftext|>


<commit_before>use std::iter::repeat;
use std::io::{self, Write};
use time::{self, Timespec, Duration};
use tty::{Width, terminal_size};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)),
            $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)),
            $n if $n >= kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

#[derive(Debug)]
pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
    pub is_finish: bool,
    pub show_bar: bool,
    pub show_speed: bool,
    pub show_percent: bool,
    pub show_counter: bool,
    pub show_time_left: bool,
}

impl ProgressBar {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: true,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    /// set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::{ProgressBar, Units};
    ///
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is `[=>-]`
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(...);
    /// pb.format("[=>_]");
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_owned();
            self.bar_current = v[2].to_owned();
            self.bar_current_n = v[3].to_owned();
            self.bar_remain = v[4].to_owned();
            self.bar_end = v[5].to_owned();
        }
    }

    /// Add to current value
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(10);
    /// pb.add(5);
    /// pb.finish();
    /// ```
    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> usize {
        self.add(1)
    }

    fn draw(&self) {
        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = match self.units {
                Units::Default => suffix + &format!("{:.*}/s ", 2, speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.clone();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use pb::ProgressBar;

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2,
                "should add the given `n` to current");
        assert!(pb.add(2) == pb.current,
                "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let fmt = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(fmt);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain +
                &pb.bar_end == fmt);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total,
                "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }

    #[test]
    fn kb_fmt() {
        let kb = 1024f64;
        let mb = kb.powf(2f64);
        let gb = kb.powf(3f64);
        let tb = kb.powf(4f64);
        assert_eq!(kb_fmt!(kb), "1.00 KB");
        assert_eq!(kb_fmt!(mb), "1.00 MB");
        assert_eq!(kb_fmt!(gb), "1.00 GB");
        assert_eq!(kb_fmt!(tb), "1.00 TB");
    }
}
<commit_msg>feat(pb): add finish_print fn<commit_after>use std::iter::repeat;
use std::io::{self, Write};
use time::{self, Timespec, Duration};
use tty::{Width, terminal_size};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)),
            $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)),
            $n if $n >= kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

#[derive(Debug)]
pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
    pub is_finish: bool,
    pub show_bar: bool,
    pub show_speed: bool,
    pub show_percent: bool,
    pub show_counter: bool,
    pub show_time_left: bool,
}

impl ProgressBar {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: true,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    /// set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::{ProgressBar, Units};
    ///
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is `[=>-]`
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(...);
    /// pb.format("[=>_]");
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_owned();
            self.bar_current = v[2].to_owned();
            self.bar_current_n = v[3].to_owned();
            self.bar_remain = v[4].to_owned();
            self.bar_end = v[5].to_owned();
        }
    }

    /// Add to current value
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let mut pb = ProgressBar::new(10);
    /// pb.add(5);
    /// pb.finish();
    /// ```
    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> usize {
        self.add(1)
    }

    fn draw(&self) {
        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = match self.units {
                Units::Default => suffix + &format!("{:.*}/s ", 2, speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.clone();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }

    /// Call finish and write string 's'
    pub fn finish_print(&mut self, s: &str) {
        self.finish();
        println!("{}", s)
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use pb::ProgressBar;

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2,
                "should add the given `n` to current");
        assert!(pb.add(2) == pb.current,
                "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let fmt = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(fmt);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain +
                &pb.bar_end == fmt);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total,
                "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }

    #[test]
    fn kb_fmt() {
        let kb = 1024f64;
        let mb = kb.powf(2f64);
        let gb = kb.powf(3f64);
        let tb = kb.powf(4f64);
        assert_eq!(kb_fmt!(kb), "1.00 KB");
        assert_eq!(kb_fmt!(mb), "1.00 MB");
        assert_eq!(kb_fmt!(gb), "1.00 GB");
        assert_eq!(kb_fmt!(tb), "1.00 TB");
    }
}
<|endoftext|>


<commit_before>
pub type OpcodeResult = Result<Opcode, OpcodeError>;

#[derive(Debug)]
pub enum OpcodeError {
    UnrecognizedOpcode(u16),
    InvalidModeForSetRegToReg(u8),
}

#[derive(Debug)]
pub enum SetRegMode {
    Copy = 0x0, // VX = VY

    Or = 0x1, // VX |= VY
    And = 0x2, // VX &= VY
    Xor = 0x3, // VX ^= VY

    Add = 0x4, // VX += VY
    Subtract = 0x5, // VX -= VY
    InverseSubtract = 0x7, // VX = VY - VX

    // For these, Y is ignored.  I am not sure why
    ShiftLeft = 0xE, // VX <<= 1
    ShiftRight = 0x6, // VX >>= 1
}

impl SetRegMode {
    pub fn from_u8(byte: u8) -> Option<SetRegMode> {
        use self::SetRegMode::*;

        match byte {
            0x0 => Some(Copy),
            0x1 => Some(Or),
            0x2 => Some(And),
            0x3 => Some(Xor),
            0x4 => Some(Add),
            0x5 => Some(Subtract),
            0x7 => Some(InverseSubtract),
            0xE => Some(ShiftLeft),
            0x6 => Some(ShiftRight),
            _ => None,
        }
    }
}

#[derive(Debug)]
pub enum Opcode {
    /* KEY
     * NNNN => address,
     * NN => 8 bit number,
     * N => 4 bit number,
     * X and Y => regs
     */
    ClearScreen,    // 0x00E0
    Return,         // 0x00EE
    JumpTo {        // 1NNN | BNNN
        addr: u16,
        plus_v0: bool
    },
    Call(u16),      // 2NNN

    SkipIfRegEqualConst {   // 3XNN or 4XNN
        not_equal: bool, // Check to see if reg is NOT equal to value instead

        reg: u8,
        value: u8,
    },
    SkipIfRegsEqual { // 5XY0 | 9XY0
        not_equal: bool, // Check to see if regs are NOT equal instead
        regs: (u8, u8),
    },

    SetRegToConst { // 6XNN | 7XNN
        add: bool,

        reg: u8,
        value: u8,
    },
    SetRegToReg { // 8XY[0 - E]
        regs: (u8, u8),
        mode: SetRegMode,
    },

    SetAddressReg(u16), // ANNN
    SetRegToRandom { // CXNN
        reg: u8,
        mask: u8,
    },

    DrawSprite { // DXYN, draws from sprite addr stored in I. Each sprite is 8 bits wide
        regs: (u8, u8),
        bytes: u8,
    },

    SkipIfKeyInRegPressed { // EX9E | EXA1
        not_pressed: bool,
        reg: u8,
    },

    SetRegToDelayTimer(u8), // FX07
    PutKeyInReg(u8), // FX0A

    SetDelayTimerToReg(u8), // FX15
    SetSoundTimerToReg(u8), // FX18

    AddRegToAddressReg(u8), // FX1E
    RegToBCD(u8), // FX33, see http://en.wikipedia.org/wiki/Binary-coded_decimal

    DumpRegsToAddr(u8), // FX55
    RestoreRegsFromAddr(u8), // FX65
}

impl Opcode {
    pub fn from_u16(bytes: u16) -> OpcodeResult {
        use self::Opcode::*;
        use self::OpcodeError::*;

        let msb = bytes & 0xF000;
        match msb {
            0x0000 => {
                match bytes & 0x0F00 {
                    0x000 => match bytes & 0x00F0 {
                        0xE0 => match bytes & 0x000F {
                            0x0 => Ok(Opcode::ClearScreen),
                            0xE => Ok(Opcode::Return),
                            _ => Err(UnrecognizedOpcode(bytes)),
                        },
                        _ => Err(UnrecognizedOpcode(bytes)),
                    },
                    _ => Err(UnrecognizedOpcode(bytes)),
                }
            },
            0x1000 | 0xB000 => {
                Ok(JumpTo {
                    addr: bytes & 0x0FFF,
                    plus_v0: msb == 0xB000 })
            },
            0x2000 => Ok(Call(bytes & 0x0FFF)),
            0x3000 | 0x4000 => {
                Ok(SkipIfRegEqualConst {
                    not_equal: msb == 0x4000,

                    reg: ((bytes & 0x0F00) >> 8) as u8,
                    value: (bytes & 0x00FF) as u8,
                })
            },
            0x5000 | 0x9000 => {
                match bytes & 0x000F {
                    0x0 => {
                        Ok(SkipIfRegsEqual {
                            not_equal: msb == 0x9000,
                            regs: (((bytes & 0x0F00) >> 8) as u8,
                                   ((bytes & 0x00F0) >> 4) as u8),
                        })
                    },
                    _ => Err(UnrecognizedOpcode(bytes)),
                }
            },
            0x6000 | 0x7000 => {
                Ok(SetRegToConst {
                    add: msb == 0x7000,

                    reg: ((bytes & 0x0F00) >> 8) as u8,
                    value: (bytes & 0x00FF) as u8,
                })
            },
            0x8000 => {
                let mode_byte = (bytes & 0x000F) as u8;
                let mode = match SetRegMode::from_u8(mode_byte) {
                    Some(mode) => mode,
                    None => return Err(InvalidModeForSetRegToReg(mode_byte)),
                };

                Ok(SetRegToReg {
                    regs: (((bytes & 0x0F00) >> 8) as u8,
                           ((bytes & 0x00F0) >> 4) as u8),
                    mode: mode,
                })
            },
            0xA000 => Ok(SetAddressReg(bytes & 0x0FFF)),
            0xC000 => {
                Ok(SetRegToRandom {
                    reg: ((bytes & 0x0F00) >> 8) as u8,
                    mask: (bytes & 0x0FF) as u8,
                })
            },
            0xD000 => {

            },
            _ => Err(UnrecognizedOpcode(bytes)),
        }
    }
}
<commit_msg>Finalize opcode implementation<commit_after>
pub type OpcodeResult = Result<Opcode, OpcodeError>;

#[derive(Debug)]
pub enum OpcodeError {
    UnrecognizedOpcode(u16),
    InvalidModeForSetRegToReg(u8),
}

#[derive(Debug)]
pub enum SetRegMode {
    Copy = 0x0, // VX = VY

    Or = 0x1, // VX |= VY
    And = 0x2, // VX &= VY
    Xor = 0x3, // VX ^= VY

    Add = 0x4, // VX += VY
    Subtract = 0x5, // VX -= VY
    InverseSubtract = 0x7, // VX = VY - VX

    // For these, Y is ignored.  I am not sure why
    ShiftLeft = 0xE, // VX <<= 1
    ShiftRight = 0x6, // VX >>= 1
}

impl SetRegMode {
    pub fn from_u8(byte: u8) -> Option<SetRegMode> {
        use self::SetRegMode::*;

        match byte {
            0x0 => Some(Copy),
            0x1 => Some(Or),
            0x2 => Some(And),
            0x3 => Some(Xor),
            0x4 => Some(Add),
            0x5 => Some(Subtract),
            0x7 => Some(InverseSubtract),
            0xE => Some(ShiftLeft),
            0x6 => Some(ShiftRight),
            _ => None,
        }
    }
}

#[derive(Debug)]
pub enum Opcode {
    /* KEY
     * NNNN => address,
     * NN => 8 bit number,
     * N => 4 bit number,
     * X and Y => regs
     */
    ClearScreen,    // 0x00E0
    Return,         // 0x00EE
    JumpTo {        // 1NNN | BNNN
        addr: u16,
        plus_v0: bool
    },
    Call(u16),      // 2NNN

    SkipIfRegEqualConst {   // 3XNN or 4XNN
        not_equal: bool, // Check to see if reg is NOT equal to value instead

        reg: u8,
        value: u8,
    },
    SkipIfRegsEqual { // 5XY0 | 9XY0
        not_equal: bool, // Check to see if regs are NOT equal instead
        regs: (u8, u8),
    },

    SetRegToConst { // 6XNN | 7XNN
        add: bool,

        reg: u8,
        value: u8,
    },
    SetRegToReg { // 8XY[0 - E]
        regs: (u8, u8),
        mode: SetRegMode,
    },

    SetAddressReg(u16), // ANNN
    SetRegToRandom { // CXNN
        reg: u8,
        mask: u8,
    },

    DrawSprite { // DXYN, draws from sprite addr stored in I. Each sprite is 8 bits wide
        regs: (u8, u8),
        bytes: u8,
    },

    SkipIfKeyInRegPressed { // EX9E | EXA1
        not_pressed: bool,
        reg: u8,
    },

    SetRegToDelayTimer(u8), // FX07
    WaitForKeyInReg(u8), // FX0A

    SetDelayTimerToReg(u8), // FX15
    SetSoundTimerToReg(u8), // FX18

    AddRegToAddressReg(u8), // FX1E
    SetAddressRegToCharInReg(u8), // FX29, sets the address pointer to point to the text character specified in X
    RegToBCD(u8), // FX33, see http://en.wikipedia.org/wiki/Binary-coded_decimal

    DumpRegsToAddr(u8), // FX55
    LoadRegsFromAddr(u8), // FX65
}

impl Opcode {
    pub fn from_u16(bytes: u16) -> OpcodeResult
        use self::Opcode::*;
        use self::OpcodeError::*;

        let msb = bytes & 0xF000;
        match msb {
            0x0000 => {
                match bytes & 0x0F00 {
                    0x000 => match bytes & 0x00F0 {
                        0xE0 => match bytes & 0x000F {
                            0x0 => Ok(Opcode::ClearScreen),
                            0xE => Ok(Opcode::Return),
                            _ => Err(UnrecognizedOpcode(bytes)),
                        },
                        _ => Err(UnrecognizedOpcode(bytes)),
                    },
                    _ => Err(UnrecognizedOpcode(bytes)),
                }
            },
            0x1000 | 0xB000 => {
                Ok(JumpTo {
                    addr: bytes & 0x0FFF,
                    plus_v0: msb == 0xB000 })
            },
            0x2000 => Ok(Call(bytes & 0x0FFF)),
            0x3000 | 0x4000 => {
                Ok(SkipIfRegEqualConst {
                    not_equal: msb == 0x4000,

                    reg: ((bytes & 0x0F00) >> 8) as u8,
                    value: (bytes & 0x00FF) as u8,
                })
            },
            0x5000 | 0x9000 => match bytes & 0x000F {
                0x0 => {
                    Ok(SkipIfRegsEqual {
                        not_equal: msb == 0x9000,
                        regs: (((bytes & 0x0F00) >> 8) as u8,
                               ((bytes & 0x00F0) >> 4) as u8),
                    })
                },
                _ => Err(UnrecognizedOpcode(bytes)),
            },
            0x6000 | 0x7000 => {
                Ok(SetRegToConst {
                    add: msb == 0x7000,

                    reg: ((bytes & 0x0F00) >> 8) as u8,
                    value: (bytes & 0x00FF) as u8,
                })
            },
            0x8000 => {
                let mode_byte = (bytes & 0x000F) as u8;
                let mode = match SetRegMode::from_u8(mode_byte) {
                    Some(mode) => mode,
                    None => return Err(InvalidModeForSetRegToReg(mode_byte)),
                };

                Ok(SetRegToReg {
                    regs: (((bytes & 0x0F00) >> 8) as u8,
                           ((bytes & 0x00F0) >> 4) as u8),
                    mode: mode,
                })
            },
            0xA000 => Ok(SetAddressReg(bytes & 0x0FFF)),
            0xC000 => {
                Ok(SetRegToRandom {
                    reg: ((bytes & 0x0F00) >> 8) as u8,
                    mask: (bytes & 0x0FF) as u8,
                })
            },
            0xD000 => {
                Ok(DrawSprite {
                    regs: (((bytes & 0x0F00) >> 8) as u8,
                           ((bytes & 0x00F0) >> 4) as u8),
                    bytes: (bytes & 0x000F) as u8,
                })
            },
            0xE000 => match bytes & 0x00FF {
                0x9E | 0xA1 => {
                    Ok(SkipIfKeyInRegPressed {
                        not_pressed: (bytes & 0x00FF) == 0xA1,
                        reg: ((bytes & 0x0F00) >> 8) as u8,
                    })
                },
                _ => Err(UnrecognizedOpcode(bytes)),
            },
            0xF000 => {
                let reg = ((bytes & 0x0F00) >> 8) as u8;

                match bytes & 0x00FF {
                    0x07 => Ok(SetRegToDelayTimer(reg)),
                    0x0A => Ok(WaitForKeyInReg(reg)),
                    0x15 => Ok(SetDelayTimerToReg(reg)),
                    0x18 => Ok(SetSoundTimerToReg(reg)),
                    0x1E => Ok(AddRegToAddressReg(reg)),
                    0x29 => Ok(SetAddressRegToCharInReg(reg)),
                    0x33 => Ok(RegToBCD(reg)),
                    0x55 => Ok(DumpRegsToAddr(reg)),
                    0x65 => Ok(LoadRegsFromAddr(reg)),
                    _ => Err(UnrecognizedOpcode(bytes)),
                }
            },
            _ => Err(UnrecognizedOpcode(bytes)),
        }
    }
}
<|endoftext|>