<commit_before>#![deny(warnings)]

extern crate chrono;
extern crate clap;
extern crate curl;
extern crate fern;
extern crate flate2;
extern crate rustc_version;
extern crate tar;
extern crate tempdir;
extern crate toml;

#[macro_use]
extern crate log;

use std::env;
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Command;

use chrono::NaiveDate;
use clap::{App, AppSettings, Arg, SubCommand};
use curl::http;
use rustc_version::Channel;

// TODO proper error reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|e| panic!("{} with {}", stringify!($e), e))
    }
}

enum Target<'a> {
    /// `path/to/some/target.json`
    Spec(&'a Path),
    /// `arm-unknown-linux-gnueabihf`
    Triple(&'a str),
}

struct Context<'a> {
    commit_date: NaiveDate,
    commit_hash: &'a str,
    host: &'a str,
    out_dir: &'a Path,
    profile: &'static str,
    target: Target<'a>,
    verbose: bool,
}

struct Config {
    table: Option<toml::Value>,
}

impl Config {
    /// Parses `sysroot.toml`
    fn parse() -> Config {
        let path = Path::new("sysroot.toml");

        let table = if path.exists() {
            let ref mut toml = String::new();

            try!(try!(File::open(path)).read_to_string(toml));

            toml::Parser::new(toml).parse().map(toml::Value::Table)
        } else {
            info!("no sysroot.toml found, using default configuration");

            None
        };

        Config { table: table }
    }

    /// Crates to build for this target, returns `["core"]` if not specified
    fn crates(&self, target: &str) -> Vec<String> {
        let ref key = format!("target.{}.crates", target);

        let mut crates = self.table
                             .as_ref()
                             .and_then(|t| t.lookup(key))
                             .and_then(|v| v.as_slice())
                             .and_then(|vs| {
                                 vs.iter().map(|v| v.as_str().map(|s| s.to_owned())).collect()
                             })
                             .unwrap_or_else(|| vec!["core".to_owned()]);

        crates.push("core".to_owned());
        crates.sort();
        crates.dedup();
        crates
    }
}

fn main() {
    let rustc_version::VersionMeta { ref host, ref commit_date, ref commit_hash, ref channel, .. } =
        rustc_version::version_meta();
    let commit_hash = commit_hash.as_ref().unwrap();

    match *channel {
        Channel::Nightly => {}
        _ => panic!("only the nightly channel is supported at this time (see issue #5)"),
    }

    let ref matches = App::new("cargo-sysroot")
                          .bin_name("cargo")
                          .settings(&[AppSettings::SubcommandRequired])
                          .subcommand(SubCommand::with_name("sysroot")
                                          .about("Builds a sysroot with cross compiled standard \
                                                  crates")
                                          .arg(Arg::with_name("triple")
                                                   .help("Target triple to compile for")
                                                   .long("target")
                                                   .required(true)
                                                   .takes_value(true))
                                          .arg(Arg::with_name("release")
                                                   .help("Build artifacts in release mode, \
                                                          with optimizations")
                                                   .long("release"))
                                          .arg(Arg::with_name("out_dir")
                                                   .help("Output directory")
                                                   .required(true))
                                          .arg(Arg::with_name("verbose")
                                                   .help("Verbose cargo builds")
                                                   .long("verbose"))
                                          .author("Jorge Aparicio <japaricious@gmail.com>"))
                          .version(env!("CARGO_PKG_VERSION"))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("sysroot") {
        if let (Some(target), Some(out_dir)) = (matches.value_of("triple"),
                                                matches.value_of("out_dir")) {
            if host == target {
                panic!("`cargo sysroot` for host has not been implement yet");
            }

            let target = if target.ends_with("json") {
                Target::Spec(Path::new(target))
            } else {
                Target::Triple(target)
            };

            let ref ctx = Context {
                commit_date: NaiveDate::parse_from_str(commit_date.as_ref().unwrap(), "%Y-%m-%d")
                                 .unwrap(),
                commit_hash: commit_hash,
                host: host,
                out_dir: Path::new(out_dir),
                profile: if matches.is_present("release") {
                    "release"
                } else {
                    "debug"
                },
                target: target,
                verbose: matches.is_present("verbose"),
            };

            init_logger();
            fetch_source(ctx);
            symlink_host_crates(ctx);
            build_target_crates(ctx);
        }
    }
}

fn init_logger() {
    let config = fern::DispatchConfig {
        format: Box::new(|msg, level, _| format!("{}: {}", level, msg)),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    try!(fern::init_global_logger(config, log::LogLevelFilter::Trace));
}

// TODO Ultimately, we want to use `multirust fetch-source` for 100% correctness
fn fetch_source(ctx: &Context) {
    // XXX There doesn't seem to be way to get the _nightly_ date from the output of `rustc -Vv`
    // So we _assume_ the nightly day is the day after the commit-date found in `rustc -Vv` which
    // seems to be the common case, but it could be wrong and we'll end up building unusable crates
    let date = ctx.commit_date.succ();
    let hash = ctx.commit_hash;
    let ref src_dir = ctx.out_dir.join("src");
    let ref hash_file = src_dir.join(".commit-hash");

    if hash_file.exists() {
        let ref mut old_hash = String::with_capacity(hash.len());
        try!(try!(File::open(hash_file)).read_to_string(old_hash));

        if old_hash == hash {
            info!("source up to date");
            return;
        }
    }

    if src_dir.exists() {
        info!("purging the src directory");
        try!(fs::remove_dir_all(src_dir));
    }

    try!(fs::create_dir_all(src_dir));

    info!("fetching source tarball");
    let handle = http::Handle::new();
    let url = format!("http://static.rust-lang.org/dist/{}/rustc-nightly-src.tar.gz",
                      date.format("%Y-%m-%d"));
    let resp = try!(handle.timeout(300_000).get(&url[..]).follow_redirects(true).exec());

    assert_eq!(resp.get_code(), 200);

    info!("unpacking source tarball");
    let decoder = try!(flate2::read::GzDecoder::new(resp.get_body()));
    let mut archive = tar::Archive::new(decoder);
    for entry in try!(archive.entries()) {
        let mut entry = try!(entry);
        let path = {
            let path = try!(entry.path());
            let mut components = path.components();
            components.next(); // skip rust-lang-rust-<...>
            let next = components.next().and_then(|s| s.as_os_str().to_str());
            if next != Some("src") {
                continue;
            }
            components.as_path().to_path_buf()
        };
        try!(entry.unpack(&src_dir.join(path)));
    }

    info!("creating .commit-hash file");
    try!(try!(File::create(hash_file)).write_all(hash.as_bytes()));
}

fn symlink_host_crates(ctx: &Context) {
    info!("symlinking host crates");

    let sys_root = try!(String::from_utf8(try!(Command::new("rustc")
                                                   .args(&["--print", "sysroot"])
                                                   .output())
                                              .stdout));
    let ref src = Path::new(sys_root.trim_right()).join(format!("lib/rustlib/{}", ctx.host));
    let ref rustlib_dir = ctx.out_dir.join(format!("{}/lib/rustlib", ctx.profile));
    let ref dst = rustlib_dir.join(ctx.host);

    try!(fs::create_dir_all(rustlib_dir));

    if dst.exists() {
        try!(fs::remove_dir_all(dst));
    }

    link_dirs(&src, &dst);
}

fn link_dirs(src: &Path, dst: &Path) {
    try!(fs::create_dir(&dst));

    for entry in try!(src.read_dir()) {
        let entry = try!(entry);
        let src = entry.path();
        let dst = dst.join(entry.file_name());
        if try!(entry.file_type()).is_dir() {
            link_dirs(&src, &dst);
        } else {
            try!(fs::hard_link(&src, &dst).or_else(|_| fs::copy(&src, &dst).map(|_| ())));
        }
    }
}

fn build_target_crates(ctx: &Context) {
    const LIB_RS: &'static [u8] = b"#![no_std]";

    let ref config = Config::parse();

    let temp_dir = try!(tempdir::TempDir::new("sysroot"));
    let temp_dir = temp_dir.path();

    // Create Cargo project
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(temp_dir);
    cargo.args(&["new", "--vcs", "none"]);
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.arg("sysroot").status()).success());

    let ref cargo_dir = temp_dir.join("sysroot");
    let ref src_dir = env::current_dir().unwrap().join(ctx.out_dir).join("src");

    let (ref triple, ref spec_file): (String, _) = match ctx.target {
        Target::Spec(path) => {
            let path = try!(fs::canonicalize(path));
            let triple = path.file_stem().unwrap().to_str().unwrap().into();

            (triple, path)
        }
        Target::Triple(triple) => (triple.into(), PathBuf::from(format!("{}.json", triple))),
    };

    // Add crates to build as dependencies
    let ref mut toml = try!(OpenOptions::new()
                                .write(true)
                                .append(true)
                                .open(cargo_dir.join("Cargo.toml")));
    let ref crates = config.crates(triple);
    info!("will build the following crates: {:?}", crates);
    for ref krate in crates {
        try!(writeln!(toml,
                      "{} = {{ path = \"{}\" }}",
                      krate,
                      src_dir.join(format!("lib{}", krate)).display()))
    }

    {
        let ref mut toml = String::new();
        try!(try!(File::open(cargo_dir.join("Cargo.toml"))).read_to_string(toml));
        debug!("sysroot's Cargo.toml: {}", toml);
    }

    // Rewrite lib.rs to only depend on libcore
    try!(try!(OpenOptions::new().write(true).truncate(true).open(cargo_dir.join("src/lib.rs")))
             .write_all(LIB_RS));

    if spec_file.exists() {
        info!("copy target specification file");
        try!(fs::copy(spec_file, cargo_dir.join(format!("{}.json", triple))));
    }

    info!("building the target crates");
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(cargo_dir);
    cargo.args(&["build", "--target", triple]);
    if ctx.profile == "release" {
        cargo.arg("--release");
    }
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.status()).success());

    info!("copy the target crates to the sysroot");
    let ref libdir = ctx.out_dir.join(format!("{}/lib/rustlib/{}/lib", ctx.profile, triple));
    let ref deps_dir = cargo_dir.join(format!("target/{}/{}/deps", triple, ctx.profile));

    try!(fs::create_dir_all(libdir));
    for entry in try!(fs::read_dir(deps_dir)) {
        let entry = try!(entry);

        try!(fs::copy(entry.path(), libdir.join(entry.file_name())));
    }
}
<commit_msg>use single quotes for Cargo.toml paths<commit_after>#![deny(warnings)]

extern crate chrono;
extern crate clap;
extern crate curl;
extern crate fern;
extern crate flate2;
extern crate rustc_version;
extern crate tar;
extern crate tempdir;
extern crate toml;

#[macro_use]
extern crate log;

use std::env;
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Command;

use chrono::NaiveDate;
use clap::{App, AppSettings, Arg, SubCommand};
use curl::http;
use rustc_version::Channel;

// TODO proper error reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|e| panic!("{} with {}", stringify!($e), e))
    }
}

enum Target<'a> {
    /// `path/to/some/target.json`
    Spec(&'a Path),
    /// `arm-unknown-linux-gnueabihf`
    Triple(&'a str),
}

struct Context<'a> {
    commit_date: NaiveDate,
    commit_hash: &'a str,
    host: &'a str,
    out_dir: &'a Path,
    profile: &'static str,
    target: Target<'a>,
    verbose: bool,
}

struct Config {
    table: Option<toml::Value>,
}

impl Config {
    /// Parses `sysroot.toml`
    fn parse() -> Config {
        let path = Path::new("sysroot.toml");

        let table = if path.exists() {
            let ref mut toml = String::new();

            try!(try!(File::open(path)).read_to_string(toml));

            toml::Parser::new(toml).parse().map(toml::Value::Table)
        } else {
            info!("no sysroot.toml found, using default configuration");

            None
        };

        Config { table: table }
    }

    /// Crates to build for this target, returns `["core"]` if not specified
    fn crates(&self, target: &str) -> Vec<String> {
        let ref key = format!("target.{}.crates", target);

        let mut crates = self.table
                             .as_ref()
                             .and_then(|t| t.lookup(key))
                             .and_then(|v| v.as_slice())
                             .and_then(|vs| {
                                 vs.iter().map(|v| v.as_str().map(|s| s.to_owned())).collect()
                             })
                             .unwrap_or_else(|| vec!["core".to_owned()]);

        crates.push("core".to_owned());
        crates.sort();
        crates.dedup();
        crates
    }
}

fn main() {
    let rustc_version::VersionMeta { ref host, ref commit_date, ref commit_hash, ref channel, .. } =
        rustc_version::version_meta();
    let commit_hash = commit_hash.as_ref().unwrap();

    match *channel {
        Channel::Nightly => {}
        _ => panic!("only the nightly channel is supported at this time (see issue #5)"),
    }

    let ref matches = App::new("cargo-sysroot")
                          .bin_name("cargo")
                          .settings(&[AppSettings::SubcommandRequired])
                          .subcommand(SubCommand::with_name("sysroot")
                                          .about("Builds a sysroot with cross compiled standard \
                                                  crates")
                                          .arg(Arg::with_name("triple")
                                                   .help("Target triple to compile for")
                                                   .long("target")
                                                   .required(true)
                                                   .takes_value(true))
                                          .arg(Arg::with_name("release")
                                                   .help("Build artifacts in release mode, \
                                                          with optimizations")
                                                   .long("release"))
                                          .arg(Arg::with_name("out_dir")
                                                   .help("Output directory")
                                                   .required(true))
                                          .arg(Arg::with_name("verbose")
                                                   .help("Verbose cargo builds")
                                                   .long("verbose"))
                                          .author("Jorge Aparicio <japaricious@gmail.com>"))
                          .version(env!("CARGO_PKG_VERSION"))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("sysroot") {
        if let (Some(target), Some(out_dir)) = (matches.value_of("triple"),
                                                matches.value_of("out_dir")) {
            if host == target {
                panic!("`cargo sysroot` for host has not been implement yet");
            }

            let target = if target.ends_with("json") {
                Target::Spec(Path::new(target))
            } else {
                Target::Triple(target)
            };

            let ref ctx = Context {
                commit_date: NaiveDate::parse_from_str(commit_date.as_ref().unwrap(), "%Y-%m-%d")
                                 .unwrap(),
                commit_hash: commit_hash,
                host: host,
                out_dir: Path::new(out_dir),
                profile: if matches.is_present("release") {
                    "release"
                } else {
                    "debug"
                },
                target: target,
                verbose: matches.is_present("verbose"),
            };

            init_logger();
            fetch_source(ctx);
            symlink_host_crates(ctx);
            build_target_crates(ctx);
        }
    }
}

fn init_logger() {
    let config = fern::DispatchConfig {
        format: Box::new(|msg, level, _| format!("{}: {}", level, msg)),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    try!(fern::init_global_logger(config, log::LogLevelFilter::Trace));
}

// TODO Ultimately, we want to use `multirust fetch-source` for 100% correctness
fn fetch_source(ctx: &Context) {
    // XXX There doesn't seem to be way to get the _nightly_ date from the output of `rustc -Vv`
    // So we _assume_ the nightly day is the day after the commit-date found in `rustc -Vv` which
    // seems to be the common case, but it could be wrong and we'll end up building unusable crates
    let date = ctx.commit_date.succ();
    let hash = ctx.commit_hash;
    let ref src_dir = ctx.out_dir.join("src");
    let ref hash_file = src_dir.join(".commit-hash");

    if hash_file.exists() {
        let ref mut old_hash = String::with_capacity(hash.len());
        try!(try!(File::open(hash_file)).read_to_string(old_hash));

        if old_hash == hash {
            info!("source up to date");
            return;
        }
    }

    if src_dir.exists() {
        info!("purging the src directory");
        try!(fs::remove_dir_all(src_dir));
    }

    try!(fs::create_dir_all(src_dir));

    info!("fetching source tarball");
    let handle = http::Handle::new();
    let url = format!("http://static.rust-lang.org/dist/{}/rustc-nightly-src.tar.gz",
                      date.format("%Y-%m-%d"));
    let resp = try!(handle.timeout(300_000).get(&url[..]).follow_redirects(true).exec());

    assert_eq!(resp.get_code(), 200);

    info!("unpacking source tarball");
    let decoder = try!(flate2::read::GzDecoder::new(resp.get_body()));
    let mut archive = tar::Archive::new(decoder);
    for entry in try!(archive.entries()) {
        let mut entry = try!(entry);
        let path = {
            let path = try!(entry.path());
            let mut components = path.components();
            components.next(); // skip rust-lang-rust-<...>
            let next = components.next().and_then(|s| s.as_os_str().to_str());
            if next != Some("src") {
                continue;
            }
            components.as_path().to_path_buf()
        };
        try!(entry.unpack(&src_dir.join(path)));
    }

    info!("creating .commit-hash file");
    try!(try!(File::create(hash_file)).write_all(hash.as_bytes()));
}

fn symlink_host_crates(ctx: &Context) {
    info!("symlinking host crates");

    let sys_root = try!(String::from_utf8(try!(Command::new("rustc")
                                                   .args(&["--print", "sysroot"])
                                                   .output())
                                              .stdout));
    let ref src = Path::new(sys_root.trim_right()).join(format!("lib/rustlib/{}", ctx.host));
    let ref rustlib_dir = ctx.out_dir.join(format!("{}/lib/rustlib", ctx.profile));
    let ref dst = rustlib_dir.join(ctx.host);

    try!(fs::create_dir_all(rustlib_dir));

    if dst.exists() {
        try!(fs::remove_dir_all(dst));
    }

    link_dirs(&src, &dst);
}

fn link_dirs(src: &Path, dst: &Path) {
    try!(fs::create_dir(&dst));

    for entry in try!(src.read_dir()) {
        let entry = try!(entry);
        let src = entry.path();
        let dst = dst.join(entry.file_name());
        if try!(entry.file_type()).is_dir() {
            link_dirs(&src, &dst);
        } else {
            try!(fs::hard_link(&src, &dst).or_else(|_| fs::copy(&src, &dst).map(|_| ())));
        }
    }
}

fn build_target_crates(ctx: &Context) {
    const LIB_RS: &'static [u8] = b"#![no_std]";

    let ref config = Config::parse();

    let temp_dir = try!(tempdir::TempDir::new("sysroot"));
    let temp_dir = temp_dir.path();

    // Create Cargo project
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(temp_dir);
    cargo.args(&["new", "--vcs", "none"]);
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.arg("sysroot").status()).success());

    let ref cargo_dir = temp_dir.join("sysroot");
    let ref src_dir = env::current_dir().unwrap().join(ctx.out_dir).join("src");

    let (ref triple, ref spec_file): (String, _) = match ctx.target {
        Target::Spec(path) => {
            let path = try!(fs::canonicalize(path));
            let triple = path.file_stem().unwrap().to_str().unwrap().into();

            (triple, path)
        }
        Target::Triple(triple) => (triple.into(), PathBuf::from(format!("{}.json", triple))),
    };

    // Add crates to build as dependencies
    let ref mut toml = try!(OpenOptions::new()
                                .write(true)
                                .append(true)
                                .open(cargo_dir.join("Cargo.toml")));
    let ref crates = config.crates(triple);
    info!("will build the following crates: {:?}", crates);
    for ref krate in crates {
        try!(writeln!(toml,
                      "{} = {{ path = '{}' }}",
                      krate,
                      src_dir.join(format!("lib{}", krate)).display()))
    }

    {
        let ref mut toml = String::new();
        try!(try!(File::open(cargo_dir.join("Cargo.toml"))).read_to_string(toml));
        debug!("sysroot's Cargo.toml: {}", toml);
    }

    // Rewrite lib.rs to only depend on libcore
    try!(try!(OpenOptions::new().write(true).truncate(true).open(cargo_dir.join("src/lib.rs")))
             .write_all(LIB_RS));

    if spec_file.exists() {
        info!("copy target specification file");
        try!(fs::copy(spec_file, cargo_dir.join(format!("{}.json", triple))));
    }

    info!("building the target crates");
    let ref mut cargo = Command::new("cargo");
    cargo.current_dir(cargo_dir);
    cargo.args(&["build", "--target", triple]);
    if ctx.profile == "release" {
        cargo.arg("--release");
    }
    if ctx.verbose {
        cargo.arg("--verbose");
    }
    assert!(try!(cargo.status()).success());

    info!("copy the target crates to the sysroot");
    let ref libdir = ctx.out_dir.join(format!("{}/lib/rustlib/{}/lib", ctx.profile, triple));
    let ref deps_dir = cargo_dir.join(format!("target/{}/{}/deps", triple, ctx.profile));

    try!(fs::create_dir_all(libdir));
    for entry in try!(fs::read_dir(deps_dir)) {
        let entry = try!(entry);

        try!(fs::copy(entry.path(), libdir.join(entry.file_name())));
    }
}
<|endoftext|>


<commit_before>use std::net::{TcpListener, TcpStream};
use std::io::prelude::*;
use std::io::BufReader;

fn main() {

    let listener = TcpListener::bind("127.0.0.1:8000").expect("failed to start server");

    #[derive(Debug)]
    enum HTTPMethod {
        POST,
        GET,
        PUT,
        HEAD,
        OPTIONS,
        DELETE,
        NONE,
        Other(String),
    }

    #[derive(Debug)]
    struct Request {
        method: HTTPMethod,
        resource: String,
        http_version: String,
        content_length: Option<u64>,
        content: Option<String>,
    }

    fn read_request_headers<R: Read>(reader: &mut BufReader<R>, request: &mut Request) {
        let mut l: String;

        let mut i:i32 = 0;

        loop {
            l = String::new();
            reader.by_ref().read_line(&mut l).unwrap();
            l = l.trim_right().to_string();

            if i == 0 {
                let mut iter = l.split_whitespace();
                request.method = match iter.next().unwrap() {
                    "GET" => HTTPMethod::GET,
                    "POST" => HTTPMethod::POST,
                    "PUT" => HTTPMethod::PUT,
                    "HEAD" => HTTPMethod::HEAD,
                    "OPTIONS" => HTTPMethod::OPTIONS,
                    "DELETE" => HTTPMethod::DELETE,
                    m => HTTPMethod::Other(m.to_string()),
                };
                request.resource = iter.next().unwrap().to_string();
                request.http_version = iter.next().unwrap().to_string();
            }
            match l.as_str() {
                "" => {
                    break;
                },
                a => {
                    let mut iter = a.split_whitespace();
                    match iter.next().unwrap() {
                        "Content-Length:" => {
                            let cl = iter.next().unwrap();
                            request.content_length = Some(cl.parse().unwrap());
                        },
                        _ => {
                        },
                    }
                },
            }

            i += 1;
        }
    }

    fn read_request_body<R: Read>(reader: &mut BufReader<R>, request: &mut Request) {
        match request.content_length {
            Some(l) => {
                let mut buf = String::new();
                let mut handle = reader.by_ref().take(l);
                let read = handle.read_to_string(&mut buf).unwrap();
                assert_eq!(l, read as u64);
                request.content = Some(buf);
            },
            None => {
            },
        }
    }

    fn read_request(stream: &TcpStream) -> Request {
        let mut reader = BufReader::new(stream);

        let mut request = Request {
            method: HTTPMethod::NONE,
            resource: String::new(),
            http_version: String::new(),
            content_length: None,
            content: None,
        };

        read_request_headers(&mut reader, &mut request);

        read_request_body(&mut reader, &mut request);

        let mut stream = stream;
        stream.flush().expect("stream couldn't be flushed");
        println!("finished reading");

        request
    }

    fn handle_client(mut stream: TcpStream) {
        println!("Hello, world!");

        let request;

        request = read_request(&stream);
        println!("{:?}", request);

        stream.write_all(b"HTTP/1.0 200 OK\nContent-Type: text/html\n\nHello there!").expect("write failed");

        stream.flush().expect("stream couldn't be flushed");
    }

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                handle_client(stream);
            }
            Err(e) => println!("{:?}", e)
        }
    }
}
<commit_msg>1. Add Response with a as_bytes() method 2. Change Request.ressource to Request.request_target, to follow the RFC 3. Add remaining "CONNECT" and "TRACE" HTTP methods 4. Add form_response() method<commit_after>use std::net::{TcpListener, TcpStream};
use std::io::prelude::*;
use std::io::BufReader;

fn main() {

    let listener = TcpListener::bind("127.0.0.1:8000").expect("failed to start server");

    #[derive(Debug)]
    enum HTTPMethod {
        POST,
        GET,
        PUT,
        HEAD,
        OPTIONS,
        DELETE,
        CONNECT,
        TRACE,
        NONE,
        Other(String),
    }

    #[derive(Debug)]
    struct Request {
        method: HTTPMethod,
        request_target: String,
        http_version: String,
        content_length: Option<u64>,
        content: Option<String>,
    }

    #[derive(Debug)]
    struct Response {
        http_version: String,
        status_code: String,
        reason_phrase: String,
        content_type: String,
        content: String,
    }

    impl Response {
        // couldn't figure how to make &[u8] + as_bytes() work,
        // instead of Vec<u8> + into_bytes()
        fn to_bytes(&self) -> Vec<u8> {
            let bytes = format!("{} {} {}\nContent-type: {}\n\n{}",
                self.http_version,
                self.status_code,
                self.reason_phrase,
                self.content_type,
                self.content).into_bytes();
            bytes
        }
    }

    fn read_request_headers<R: Read>(reader: &mut BufReader<R>, request: &mut Request) {
        let mut l: String;

        let mut i:i32 = 0;

        loop {
            l = String::new();
            reader.by_ref().read_line(&mut l).unwrap();
            l = l.trim_right().to_string();

            if i == 0 {
                let mut iter = l.split_whitespace();
                request.method = match iter.next().unwrap() {
                    "GET" => HTTPMethod::GET,
                    "POST" => HTTPMethod::POST,
                    "PUT" => HTTPMethod::PUT,
                    "HEAD" => HTTPMethod::HEAD,
                    "OPTIONS" => HTTPMethod::OPTIONS,
                    "DELETE" => HTTPMethod::DELETE,
                    "CONNECT" => HTTPMethod::CONNECT,
                    "TRACE" => HTTPMethod::TRACE,
                    m => HTTPMethod::Other(m.to_string()),
                };
                request.request_target = iter.next().unwrap().to_string();
                request.http_version = iter.next().unwrap().to_string();
            }
            match l.as_str() {
                "" => {
                    break;
                },
                a => {
                    let mut iter = a.split_whitespace();
                    match iter.next().unwrap() {
                        "Content-Length:" => {
                            let cl = iter.next().unwrap();
                            request.content_length = Some(cl.parse().unwrap());
                        },
                        _ => {
                        },
                    }
                },
            }

            i += 1;
        }
    }

    fn read_request_body<R: Read>(reader: &mut BufReader<R>, request: &mut Request) {
        match request.content_length {
            Some(l) => {
                let mut buf = String::new();
                let mut handle = reader.by_ref().take(l);
                let read = handle.read_to_string(&mut buf).unwrap();
                assert_eq!(l, read as u64);
                request.content = Some(buf);
            },
            None => {
            },
        }
    }

    fn read_request(stream: &TcpStream) -> Request {
        let mut reader = BufReader::new(stream);

        let mut request = Request {
            method: HTTPMethod::NONE,
            request_target: String::new(),
            http_version: String::new(),
            content_length: None,
            content: None,
        };

        read_request_headers(&mut reader, &mut request);

        read_request_body(&mut reader, &mut request);

        let mut stream = stream;
        stream.flush().expect("stream couldn't be flushed");
        println!("finished reading");

        request
    }

    fn form_response(request: &Request) -> Response {
        Response {
            http_version: "HTTP/1.0".to_string(),
            status_code: "200".to_string(),
            reason_phrase: "OK".to_string(),
            content_type: "text/html".to_string(),
            content: "Howdy!".to_string(),
        }
    }

    fn handle_client(mut stream: TcpStream) {
        println!("Hello, world!");

        let request = read_request(&stream);
        println!("{:?}", request);

        let response = form_response(&request);
        println!("{:?}", response.to_bytes());

        stream.write_all(response.to_bytes().as_slice()).expect("write failed");

        stream.flush().expect("stream couldn't be flushed");
    }

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                handle_client(stream);
            }
            Err(e) => println!("{:?}", e)
        }
    }
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;

use graphics::*;

use std::path::Path;
use std::fs::OpenOptions;
use board::Board;

mod block;
mod board;

pub const BLOCK_SIZE: i64 = 30;
pub const BOARD_WIDTH: i64 = 10; // 10 cells across
pub const BOARD_HEIGHT: i64 = 22; // 22 cells up n down

pub const NUM_BOARDS: i64 = 2; // number of boards

pub const FONT_SIZE: u32 = 24;
const SCORE_LEFT_MARGIN: f64 = 15.0;
const SCORE_TOP_MARGIN: f64 = 35.0;

const GAMEOVER_LEFT_MARGIN: f64 = 70.0;
const GAMEOVER_TOP_MARGIN: f64 = 250.0;

const BOARD_BKD_COLOR: [f32; 4] = [0.18, 0.18, 0.18, 1.0]; // dark gray
// const RED: [f32; 4] = [0.96, 0.12, 0.12, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// const BRIGHT_GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    boards: Vec<board::Board>, // the game board
    cache: GlyphCache<'static> // for drawing text
}

impl App {
    fn render(&mut self, args: &RenderArgs) {

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(BOARD_BKD_COLOR, gl);
        });

        for (i, board) in &mut self.boards.iter().enumerate() {
            // do nothing if paused
            if board.state == board::GameState::Paused {
                continue
            }

            let font_cache = &mut self.cache;

            // show game over screen if game is done
            if board.state == board::GameState::Over {
                self.gl.draw(args.viewport(), |c, gl| {
                    rectangle(BLACK,
                             [(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                0.0,
                                (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                             c.transform.trans(0.0, 0.0),
                             gl);

                    // draw game over message
                    let mut text = graphics::Text::new(FONT_SIZE);
                    text.color = WHITE;
                    let mut transform: graphics::context::Context =
                                c.trans(GAMEOVER_LEFT_MARGIN + (BLOCK_SIZE * BOARD_WIDTH * i as i64) as f64, GAMEOVER_TOP_MARGIN);
                    text.draw(&format!("GAME OVER"),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);
                    transform = transform.trans(0.0, FONT_SIZE as f64 + 40.0);
                    // not centered because i am a weenie
                    text.draw(&format!("   Score: {}", board.score),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);

                    // draw border
                    let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                    rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                     0.0,
                                     (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                     (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                        &c.draw_state,
                        c.transform,
                        gl);
                });

                continue
            }

            // show gameplay screen
            let cells = board.cells;

            let blocks = board.current_piece.blocks;
            let current_x = board.current_piece.x_offset;
            let current_y = board.current_piece.y_offset;
            let piece_color = board.current_piece.color;

            let ghost_blocks = board.ghost_piece.blocks;
            let ghost_x = board.ghost_piece.x_offset;
            let ghost_y = board.ghost_piece.y_offset;
            let ghost_piece_color = board.ghost_piece.color;

            self.gl.draw(args.viewport(), |c, gl| {
                // iterate thru board cells and draw in filled-in blocks
                for row in 0..cells.len() {
                    for col in 0..cells[0].len() {
                        if cells[row][col] == 1 {
                            let x: f64 = col as f64 + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = row as f64;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(WHITE, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // iterate thru current piece and draw its current location
                for row in 0..blocks.len() {
                    for col in 0..blocks[0].len() {
                        if blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + current_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + current_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // DRAW  G H O S T   P I E C E ?
                for row in 0..ghost_blocks.len() {
                    for col in 0..ghost_blocks[0].len() {
                        if ghost_blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + ghost_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + ghost_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(ghost_piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // draw score
                let mut text = graphics::Text::new(FONT_SIZE);
                text.color = WHITE;
                let transform: graphics::context::Context =
                            c.trans(SCORE_LEFT_MARGIN + (BLOCK_SIZE as f64) * (BOARD_WIDTH * i as i64) as f64, SCORE_TOP_MARGIN);
                text.draw(&format!("Score: {}", board.score),
                          font_cache,
                          &c.draw_state,
                          transform.transform,
                          gl);

                // draw border
                let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                 0.0,
                                 (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                 (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                    &c.draw_state,
                    c.transform,
                    gl);
            });

        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        for mut board in &mut self.boards {
            if board.state != board::GameState::Playing {
                continue
            }
            board.advance_board();
        }
    }
}

fn main() {
    // Change this to OpenGL::V2_1 if not working.
    let opengl = OpenGL::V3_2;

    // Create an Glutin window.
    let mut window: Window = WindowSettings::new(
            "t e t r i s",
            [(BLOCK_SIZE * BOARD_WIDTH * NUM_BOARDS) as u32, (BLOCK_SIZE * BOARD_HEIGHT) as u32]
        )
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let font_path = match OpenOptions::new().read(true).open("Lato-Light.ttf") {
        Ok(_) => Path::new("Lato-Light.ttf"),
        Err(_) => {
            match OpenOptions::new().read(true).open("src/Lato-Light.ttf") {
                Ok(_) => Path::new("src/Lato-Light.ttf"),
                Err(_) => panic!("No font file found")
            }
        }
    };

    // Create a new game and run it.
    let mut boards: Vec<board::Board> = Vec::new();
    for i in 0..NUM_BOARDS {
        boards.push(Board::init_board());
    }
    let mut app = App {
        gl: GlGraphics::new(opengl),
        boards: boards,
        cache: GlyphCache::new(font_path).unwrap()
    };

    let mut events = window.events();
    events = events.ups(5);

    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }

        if let Some(u) = e.update_args() {
            app.update(&u);
        }

        if let Event::Input(i) = e {
            for board in &mut app.boards {
                board.handle_key_press(&i);
            }
        }
    }
}

<commit_msg>add todo comments<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;

use graphics::*;

use std::path::Path;
use std::fs::OpenOptions;
use board::Board;

mod block;
mod board;

pub const BLOCK_SIZE: i64 = 30;
pub const BOARD_WIDTH: i64 = 10; // 10 cells across
pub const BOARD_HEIGHT: i64 = 22; // 22 cells up n down

pub const NUM_BOARDS: i64 = 2; // number of boards

pub const FONT_SIZE: u32 = 24;
const SCORE_LEFT_MARGIN: f64 = 15.0;
const SCORE_TOP_MARGIN: f64 = 35.0;

const GAMEOVER_LEFT_MARGIN: f64 = 70.0;
const GAMEOVER_TOP_MARGIN: f64 = 250.0;

const BOARD_BKD_COLOR: [f32; 4] = [0.18, 0.18, 0.18, 1.0]; // dark gray
// const RED: [f32; 4] = [0.96, 0.12, 0.12, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// const BRIGHT_GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    boards: Vec<board::Board>, // game boards
    cache: GlyphCache<'static> // for drawing text
}

impl App {
    fn render(&mut self, args: &RenderArgs) {

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(BOARD_BKD_COLOR, gl);
        });

        for (i, board) in &mut self.boards.iter().enumerate() {
            // do nothing if paused
            if board.state == board::GameState::Paused {
                continue
            }

            let font_cache = &mut self.cache;

            // show game over screen if game is done
            if board.state == board::GameState::Over {
                self.gl.draw(args.viewport(), |c, gl| {
                    rectangle(BLACK,
                             [(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                0.0,
                                (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                             c.transform.trans(0.0, 0.0),
                             gl);

                    // draw game over message
                    let mut text = graphics::Text::new(FONT_SIZE);
                    text.color = WHITE;
                    let mut transform: graphics::context::Context =
                                c.trans(GAMEOVER_LEFT_MARGIN + (BLOCK_SIZE * BOARD_WIDTH * i as i64) as f64, GAMEOVER_TOP_MARGIN);
                    text.draw(&format!("GAME OVER"),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);
                    transform = transform.trans(0.0, FONT_SIZE as f64 + 40.0);
                    // not centered because i am a weenie
                    text.draw(&format!("   Score: {}", board.score),
                              font_cache,
                              &c.draw_state,
                              transform.transform,
                              gl);

                    // draw border
                    let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                    rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                     0.0,
                                     (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                     (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                        &c.draw_state,
                        c.transform,
                        gl);
                });

                continue
            }

            // show gameplay screen
            let cells = board.cells;

            let blocks = board.current_piece.blocks;
            let current_x = board.current_piece.x_offset;
            let current_y = board.current_piece.y_offset;
            let piece_color = board.current_piece.color;

            let ghost_blocks = board.ghost_piece.blocks;
            let ghost_x = board.ghost_piece.x_offset;
            let ghost_y = board.ghost_piece.y_offset;
            let ghost_piece_color = board.ghost_piece.color;

            self.gl.draw(args.viewport(), |c, gl| {
                // iterate thru board cells and draw in filled-in blocks
                for row in 0..cells.len() {
                    for col in 0..cells[0].len() {
                        if cells[row][col] == 1 {
                            let x: f64 = col as f64 + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = row as f64;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(WHITE, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // iterate thru current piece and draw its current location
                for row in 0..blocks.len() {
                    for col in 0..blocks[0].len() {
                        if blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + current_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + current_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // DRAW  G H O S T   P I E C E ?
                for row in 0..ghost_blocks.len() {
                    for col in 0..ghost_blocks[0].len() {
                        if ghost_blocks[row][col] == 1 {
                            let x: f64 = (col as f64) + ghost_x + (BOARD_WIDTH * i as i64) as f64;
                            let y: f64 = (row as f64) + ghost_y;
                            let size: f64 = BLOCK_SIZE as f64;
                            let b = rectangle::square(x * size, y * size, size);
                            rectangle(ghost_piece_color, b, c.transform.trans(0.0, 0.0), gl);
                        }
                    }
                }

                // draw score
                let mut text = graphics::Text::new(FONT_SIZE);
                text.color = WHITE;
                let transform: graphics::context::Context =
                            c.trans(SCORE_LEFT_MARGIN + (BLOCK_SIZE as f64) * (BOARD_WIDTH * i as i64) as f64, SCORE_TOP_MARGIN);
                text.draw(&format!("Score: {}", board.score),
                          font_cache,
                          &c.draw_state,
                          transform.transform,
                          gl);

                // draw border
                let rect_border = graphics::Rectangle::new_border(WHITE, 0.3);
                rect_border.draw([(BOARD_WIDTH * BLOCK_SIZE) as f64 * i as f64,
                                 0.0,
                                 (BOARD_WIDTH * BLOCK_SIZE) as f64,
                                 (BOARD_HEIGHT * BLOCK_SIZE) as f64],
                    &c.draw_state,
                    c.transform,
                    gl);
            });

        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        for mut board in &mut self.boards {
            if board.state != board::GameState::Playing {
                continue
            }
            board.advance_board();
        }
    }
}

fn main() {
    // Change this to OpenGL::V2_1 if not working.
    let opengl = OpenGL::V3_2;

    // Create an Glutin window.
    let mut window: Window = WindowSettings::new(
            "t e t r i s",
            [(BLOCK_SIZE * BOARD_WIDTH * NUM_BOARDS) as u32, (BLOCK_SIZE * BOARD_HEIGHT) as u32]
        )
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let font_path = match OpenOptions::new().read(true).open("Lato-Light.ttf") {
        Ok(_) => Path::new("Lato-Light.ttf"),
        Err(_) => {
            match OpenOptions::new().read(true).open("src/Lato-Light.ttf") {
                Ok(_) => Path::new("src/Lato-Light.ttf"),
                Err(_) => panic!("No font file found")
            }
        }
    };

    // Create a new game and run it.
    let mut boards: Vec<board::Board> = Vec::new();
    for i in 0..NUM_BOARDS { // TODO: update num_boards from server
        boards.push(Board::init_board());
    }
    let mut app = App {
        gl: GlGraphics::new(opengl),
        boards: boards,
        cache: GlyphCache::new(font_path).unwrap()
    };

    let mut events = window.events();
    events = events.ups(5);

    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }

        if let Some(u) = e.update_args() {
            app.update(&u);
        }

        if let Event::Input(i) = e {
            for board in &mut app.boards {
                board.handle_key_press(&i);
            }
            // TODO: send new keystroke to server

            // TODO: send messages:
            // new block name
            // keystroke
            // new score
            // GameState
        }
    }
}

<|endoftext|>


<commit_before>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();
  let initial_search = args.initial_search.clone();
  if args.use_first {
    let matches = matching::compute_matches(&choices, initial_search.as_slice());
    println!("{}", matches[0]);
    return;
  } else {
    event_loop(choices, initial_search.as_slice());
  }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
  let mut search = String::from_str(initial_search);
  let mut screen = Screen::open_screen();
  let mut index = 0;

  let start_line = screen.height - screen.visible_choices - 1;
  let mut matches_stale = true;
  let mut matches = matching::compute_matches(&choices, search.as_slice());
  loop {
    if matches_stale {
      matches = matching::compute_matches(&choices, search.as_slice());
      matches_stale = false;
    }
    screen.hide_cursor();
    screen.blank_screen(start_line);
    screen.move_cursor(start_line, 0);
    screen.write(format!("> {} ({} choices)\n", search.as_slice(), choices.len()).as_slice());

    print_matches(&mut screen, &matches, index);

    screen.move_cursor(start_line, 2 + search.len() as u16);
    screen.show_cursor();

    let chars = screen.get_buffered_keys();
    for char in chars.iter() {
      match *char {
        Char(x) => {
          search.push(x);
          index = 0;
          matches_stale = true;
        }
        Backspace => { search.pop(); matches_stale = true; }
        Control('h') => { search.pop(); matches_stale = true; }
        Control('u') => { search.clear(); matches_stale = true; }
        Control('c') => { return; }
        Control('n') => { index = min(index + 1, min(screen.visible_choices as usize - 1, matches.len() - 1)); }
        Control('p') => { index = if index == 0 { 0 } else { index - 1 }; }
        Enter => {
          let end_line = start_line + screen.visible_choices;
          screen.move_cursor(end_line, 0);
          screen.write("\n");
          if matches_stale {
            matches = matching::compute_matches(&choices, search.as_slice());
          }
          println!("{}", matches[index]);
          return;
        }
        _ => panic!("Unexpected input"),
      }
    }
  }
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
  let mut i = 1;
  for choice in matches.iter() {
    if i == index + 1 {
      screen.write_inverted(choice.as_slice());
    } else {
      screen.write(choice.as_slice());
    }
    if i >= screen.visible_choices as usize {
      return;
    } else {
      screen.write("\n");
    }
    i += 1;
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  while let Ok(mut s) = stdin.read_line() {
    trim(&mut s);
    lines.push(s);
  }

  lines
}

fn trim(s: &mut String) {
  while let Some(x) = s.pop() {
    if x != '\n' {
      s.push(x);
      return;
    }
  }
}
<commit_msg>Extract draw_screen<commit_after>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();
  let initial_search = args.initial_search.clone();
  if args.use_first {
    let matches = matching::compute_matches(&choices, initial_search.as_slice());
    println!("{}", matches[0]);
    return;
  } else {
    event_loop(choices, initial_search.as_slice());
  }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
  let mut search = String::from_str(initial_search);
  let mut screen = Screen::open_screen();
  let mut index = 0;

  let start_line = screen.height - screen.visible_choices - 1;
  let mut matches_stale = true;
  let mut matches = matching::compute_matches(&choices, search.as_slice());
  loop {
    if matches_stale {
      matches = matching::compute_matches(&choices, search.as_slice());
      matches_stale = false;
    }

    draw_screen(&mut screen, &matches, search.as_slice(), choices.len(), start_line, index);

    let chars = screen.get_buffered_keys();
    for char in chars.iter() {
      match *char {
        Char(x) => {
          search.push(x);
          index = 0;
          matches_stale = true;
        }
        Backspace => { search.pop(); matches_stale = true; }
        Control('h') => { search.pop(); matches_stale = true; }
        Control('u') => { search.clear(); matches_stale = true; }
        Control('c') => { return; }
        Control('n') => { index = min(index + 1, min(screen.visible_choices as usize - 1, matches.len() - 1)); }
        Control('p') => { index = if index == 0 { 0 } else { index - 1 }; }
        Enter => {
          let end_line = start_line + screen.visible_choices;
          screen.move_cursor(end_line, 0);
          screen.write("\n");
          if matches_stale {
            matches = matching::compute_matches(&choices, search.as_slice());
          }
          println!("{}", matches[index]);
          return;
        }
        _ => panic!("Unexpected input"),
      }
    }
  }
}

fn draw_screen(screen: &mut Screen, matches: &Vec<&String>, search: &str, choices: usize, start_line: u16, index: usize) {
  screen.hide_cursor();
  screen.blank_screen(start_line);
  screen.move_cursor(start_line, 0);
  screen.write(format!("> {} ({} choices)\n", search, choices).as_slice());

  print_matches(screen, matches, index);

  screen.move_cursor(start_line, 2 + search.len() as u16);
  screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
  let mut i = 1;
  for choice in matches.iter() {
    if i == index + 1 {
      screen.write_inverted(choice.as_slice());
    } else {
      screen.write(choice.as_slice());
    }
    if i >= screen.visible_choices as usize {
      return;
    } else {
      screen.write("\n");
    }
    i += 1;
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  while let Ok(mut s) = stdin.read_line() {
    trim(&mut s);
    lines.push(s);
  }

  lines
}

fn trim(s: &mut String) {
  while let Some(x) = s.pop() {
    if x != '\n' {
      s.push(x);
      return;
    }
  }
}
<|endoftext|>


<commit_before>extern crate rucc;
use rucc::common;

extern crate ansi_term;
use self::ansi_term::Colour;

extern crate clap;
use clap::{App, Arg};

const VERSION_STR: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let mut app = App::new("rucc")
        .version(VERSION_STR)
        .author("uint256_t")
        .about("rucc is a small toy C compiler in Rust")
        .arg(
            Arg::with_name("version")
                .short("v")
                .long("version")
                .help("Show version info"),
        )
        .arg(Arg::with_name("FILE").help("Input file").index(1));
    let app_matches = app.clone().get_matches();

    if app_matches.is_present("version") {
        app.write_version(&mut ::std::io::stdout());
        println!();
    } else if let Some(filename) = app_matches.value_of("FILE") {
        common::run_file(filename);
        println!("{}", Colour::Green.paint("Compiling exited successfully."));
    } else {
        app.print_help();
        println!();
    }
}

#[test]
fn compare_with_clang_output() {
    use std::fs;
    use std::process::Command;

    let examples_paths = match fs::read_dir("example") {
        Ok(paths) => paths,
        Err(e) => panic!(format!("error: {:?}", e.kind())),
    };
    for path in examples_paths {
        let name = path.unwrap().path().to_str().unwrap().to_string();
        println!("testing {}...", name);

        Command::new("./rucc.sh")
            .arg(name.to_string())
            .spawn()
            .expect("failed to run")
            .wait()
            .expect("failed to run");
        let output1 = Command::new("./a.out").output().expect("failed to run");
        Command::new("clang")
            .arg(name)
            .arg("-lm")
            .arg("-w")
            .spawn()
            .expect("failed to run")
            .wait()
            .expect("failed to run");
        let output2 = Command::new("./a.out").output().expect("failed to run");
        assert!(output1 == output2);
    }
}

#[test]
fn compile_all_examples() {
    use std::fs;
    use rucc::{codegen, lexer, parser};
    use std::process::Command;

    let examples_paths = match fs::read_dir("example") {
        Ok(paths) => paths,
        Err(e) => panic!(format!("error: {:?}", e.kind())),
    };
    for path in examples_paths {
        let name = path.unwrap().path().to_str().unwrap().to_string();
        println!("testing {}...", name);

        // for coverage...
        unsafe {
            let mut nodes = Vec::new();
            let mut lexer = lexer::Lexer::new(name.to_string());
            let mut parser = parser::Parser::new(&mut lexer);
            let mut codegen = codegen::Codegen::new("test");
            loop {
                match parser.read_toplevel(&mut nodes) {
                    Err(parser::Error::EOF) => break,
                    Err(_) => continue,
                    _ => {}
                }
                for node in &nodes {
                    node.show();
                }
                match codegen.run(&nodes) {
                    Ok(_) => {}
                    Err(e) => panic!(format!("err in codegen: {:?}", e)),
                }
                nodes.clear();
            }
        }
    }
}
<commit_msg>update for circleci<commit_after>extern crate rucc;
use rucc::common;

extern crate ansi_term;
use self::ansi_term::Colour;

extern crate clap;
use clap::{App, Arg};

const VERSION_STR: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let mut app = App::new("rucc")
        .version(VERSION_STR)
        .author("uint256_t")
        .about("rucc is a small toy C compiler in Rust")
        .arg(
            Arg::with_name("version")
                .short("v")
                .long("version")
                .help("Show version info"),
        )
        .arg(Arg::with_name("FILE").help("Input file").index(1));
    let app_matches = app.clone().get_matches();

    if app_matches.is_present("version") {
        app.write_version(&mut ::std::io::stdout());
        println!();
    } else if let Some(filename) = app_matches.value_of("FILE") {
        common::run_file(filename);
        println!("{}", Colour::Green.paint("Compiling exited successfully."));
    } else {
        app.print_help();
        println!();
    }
}

#[test]
fn compare_with_clang_output() {
    use std::fs;
    use std::process::Command;

    let examples_paths = match fs::read_dir("example") {
        Ok(paths) => paths,
        Err(e) => panic!(format!("error: {:?}", e.kind())),
    };
    for path in examples_paths {
        let name = path.unwrap().path().to_str().unwrap().to_string();
        println!("testing {}...", name);

        Command::new("./rucc.sh")
            .arg(name.to_string())
            .spawn()
            .expect("failed to run")
            .wait()
            .expect("failed to run");
        let output1 = Command::new("./a.out").output().expect("failed to run");
        Command::new("clang")
            .arg(name)
            .arg("-lm")
            .arg("-w")
            .spawn()
            .expect("failed to run")
            .wait()
            .expect("failed to run");
        let output2 = Command::new("./a.out").output().expect("failed to run");
        assert!(output1 == output2);
    }
}

#[test]
fn compile_all_examples() {
    use std::fs;
    use rucc::{codegen, lexer, parser};
    use std::process::Command;
    use std::io::{stderr, Write};

    let examples_paths = match fs::read_dir("example") {
        Ok(paths) => paths,
        Err(e) => panic!(format!("error: {:?}", e.kind())),
    };
    for path in examples_paths {
        let name = path.unwrap().path().to_str().unwrap().to_string();
        writeln!(&mut stderr(), "testing {}...", name).unwrap();

        // for coverage...
        unsafe {
            let mut nodes = Vec::new();
            let mut lexer = lexer::Lexer::new(name.to_string());
            let mut parser = parser::Parser::new(&mut lexer);
            let mut codegen = codegen::Codegen::new("test");
            loop {
                match parser.read_toplevel(&mut nodes) {
                    Err(parser::Error::EOF) => break,
                    Err(_) => continue,
                    _ => {}
                }
                for node in &nodes {
                    node.show();
                }
                match codegen.run(&nodes) {
                    Ok(_) => {}
                    Err(e) => panic!(format!("err in codegen: {:?}", e)),
                }
                nodes.clear();
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate clap;

use serde_json::Value;
use serde_json::Deserializer;
use std::io::{self, Write};
use clap::{App, Arg};
use std::fs;
use std::result;
use std::error::Error;
use std::vec::Vec;
use std::string::String;

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

fn run() -> Result<()> {
    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("Extract JSON elements from a stream via JSON Pointers")
        .arg(Arg::with_name("INPUT")
            .short("i")
            .long("input")
            .takes_value(true)
            .help("input file to use if not receiving on stdin"))
        .arg(Arg::with_name("DELIMITER")
            .short("d")
            .long("delimiter")
            .takes_value(true)
            .help("delimiter between output values, default is tab"))
        .arg(Arg::with_name("POINTER")
            .required(true)
            .multiple(true)
            .help("JSON Pointer expressions to match on input"))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap_or("-");
    let rdr: Box<io::Read> = match input {
        "-" => Box::new(io::stdin()),
        _ => Box::new(try!(fs::File::open(input))),
    };

    let delim = matches.value_of("DELIMITER").unwrap_or("\t");

    let mut pointers = Vec::new();
    // unwrap is safe as POINTER is required
    for p in matches.values_of("POINTER").unwrap() {
        pointers.push(p);
    }

    let iter = Deserializer::from_reader(rdr).into_iter::<Value>();
    for rv in iter {
        let v = try!(rv);
        let mut line = String::new();
        for p in &pointers {
            line.push_str(&render(v.pointer(p).unwrap_or(&Value::Null)));
            line.push_str(delim);
        }
        line.pop(); // remove the final tab
        println!("{}", line);
    }
    Ok(())
}

fn main() {
    match run() {
        Ok(_) => {}
        Err(e) => {
            writeln!(&mut io::stderr(), "error: {}", e).unwrap();
            std::process::exit(1);
        }
    }
}

fn render(v: &Value) -> String {
    match v {
        &Value::Null => String::new(),
        &Value::Bool(ref b) => format!("{}", b),
        &Value::Number(ref b) => format!("{}", b),
        &Value::String(ref s) => format!("{}", s.replace("\n", "\\n")
                                                .replace("\t", "\\t")),
        &Value::Array(_) => format!("{}", v),
        &Value::Object(_) => format!("{}", v),
    }
}
<commit_msg>rearrange function ordering a smidge<commit_after>extern crate serde_json;
extern crate clap;

use serde_json::Value;
use serde_json::Deserializer;
use std::io::{self, Write};
use clap::{App, Arg};
use std::fs;
use std::result;
use std::error::Error;
use std::vec::Vec;
use std::string::String;

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

fn main() {
    match run() {
        Ok(_) => {}
        Err(e) => {
            writeln!(&mut io::stderr(), "error: {}", e).unwrap();
            std::process::exit(1);
        }
    }
}

fn run() -> Result<()> {
    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("Extract JSON elements from a stream via JSON Pointers")
        .arg(Arg::with_name("INPUT")
            .short("i")
            .long("input")
            .takes_value(true)
            .help("input file to use if not receiving on stdin"))
        .arg(Arg::with_name("DELIMITER")
            .short("d")
            .long("delimiter")
            .takes_value(true)
            .help("delimiter between output values, default is tab"))
        .arg(Arg::with_name("POINTER")
            .required(true)
            .multiple(true)
            .help("JSON Pointer expressions to match on input"))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap_or("-");
    let rdr: Box<io::Read> = match input {
        "-" => Box::new(io::stdin()),
        _ => Box::new(try!(fs::File::open(input))),
    };

    let delim = matches.value_of("DELIMITER").unwrap_or("\t");

    let mut pointers = Vec::new();
    // unwrap is safe as POINTER is required
    for p in matches.values_of("POINTER").unwrap() {
        pointers.push(p);
    }

    let iter = Deserializer::from_reader(rdr).into_iter::<Value>();
    for rv in iter {
        let v = try!(rv);
        let mut line = String::new();
        for p in &pointers {
            line.push_str(&render(v.pointer(p).unwrap_or(&Value::Null)));
            line.push_str(delim);
        }
        line.pop(); // remove the final tab
        println!("{}", line);
    }
    Ok(())
}

fn render(v: &Value) -> String {
    match v {
        &Value::Null => String::new(),
        &Value::Bool(ref b) => format!("{}", b),
        &Value::Number(ref b) => format!("{}", b),
        &Value::String(ref s) => format!("{}", s.replace("\n", "\\n")
                                                .replace("\t", "\\t")),
        &Value::Array(_) => format!("{}", v),
        &Value::Object(_) => format!("{}", v),
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(plugin, link)] extern crate log;

// masks for pulling nibbles out of bytes
const U4_LOW:  u8 = 0b00001111;
const U4_HIGH: u8 = 0b11110000;

/// If the CPU enters the `Halt` state then any additional ticks will result in
/// the program exhibiting undefined behavior.
///
#[deriving(PartialEq,Show)]
enum CpuState {
	Continue,
	Halt,
}

/// The P150 virtual machine
/// Represents all information the CPU needs to continue executing a
/// program stored in its main memory.
struct P150Cpu {
	ip:  u8,
	ir: u16,

	reg: [u8, ..16],
	mem: [u8, ..256],
}

impl P150Cpu {
	/// Initializes the P150 CPU
	///
	/// NOTE: This implementation 0s memory; but this is not guaranteed
	/// by the machine specification.
	///
	/// Programs MUST start at memory address 0x00
	fn new() -> P150Cpu {
		P150Cpu {
			ip:  0x00,
			ir:  0x0000,

			reg: [0u8, ..16],
			mem: [0u8, ..256],
		}
	}

	/// Prints the current machine state to the console window
	/// This includes the IP, IR, and all registers.
	/// (Registers will be formatted as 2s complement numbers.)
	fn dump(&self) {
		println!("IP: 0x{:02X}, IR: 0x{:04X}", self.ip, self.ir);

		println!("---\nRegisters\n---")
		for (addr, cell) in self.reg.iter().enumerate() {
			println!("{:01X}: {}", addr, cell)
		}

		println!("---")

		debug!("Memory")
		for (addr, cell) in self.mem.iter().enumerate() {
			debug!("{:02X}: {}", addr, cell)
		}
	}

	#[cfg(test)]
	fn get_reg(&self) -> &[u8] {
		self.reg
	}

	/// Read an array of instructions into main memory
	/// This reads two bytes at a time from the `memory` array
	/// and loads them into the P150s RAM bank, starting from address 0.
	fn init_mem(&mut self, memory: &[u16]) {
		let mut next_cell = 0x00;

		for op in memory.iter() {
			let byte_1 = (*op >> 8) as u8;
			let byte_2 = *op as u8;

			self.mem[next_cell]     = byte_1;
			self.mem[next_cell + 1] = byte_2;
			next_cell += 2
		}
	}

	/// Runs the entire fetch, execute, decode cycle against the current IP
	fn tick(&mut self) -> CpuState {
		self.fetch();

		// decode & execute
		let op = (self.ir >> 12) as u8;
		match op {
			0x0   => { // ADDB
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);   // first input: lower nibble of first byte
				let rloc_i1 = hi_nibble(self.ir as u8);          // second input: upper nibble of second byte
				let rloc_o0 = lo_nibble(self.ir as u8);          // output: lower nibble of second byte

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] + self.reg[rloc_i1 as uint];
				Continue
			},

			0x2   => { // ROT
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let swidth  = hi_nibble(self.ir as u8);
				let swidth = if swidth > 8 { swidth - 8 } else { swidth };
				let swidth = swidth as uint;

				// LHS shifts <width> bits off the (left) end of the bitstring
				// RHS shifts the bitstring to the right until only the bits which fell off remain.
				//   LHS is the remaining MSB bits; RHS is the remaining LSB bits
				//   ∴ LHS <OR> RHS provides a rotated bitstring
				let x = self.reg[rloc_i0 as uint];
				self.reg[rloc_i0 as uint] = (x << swidth) | (x >> (8 - swidth));

				Continue
			},

			0x3   => { // AND
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_i1 = hi_nibble(self.ir as u8);
				let rloc_o0 = lo_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] & self.reg[rloc_i1 as uint];
				Continue
			},

			0x4   => { // OR
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_i1 = hi_nibble(self.ir as u8);
				let rloc_o0 = lo_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] | self.reg[rloc_i1 as uint];
				Continue
			},

			0x5   => { // XOR
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_i1 = hi_nibble(self.ir as u8);
				let rloc_o0 = lo_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] ^ self.reg[rloc_i1 as uint];
				Continue
			},

			0x6   => { // MLOAD
				let rloc_o0 = lo_nibble((self.ir >> 8) as u8);
				let mloc_i0 = self.ir as u8;

				self.reg[rloc_o0 as uint] = self.mem[mloc_i0 as uint];
				Continue
			},

			0x7   => { // MSTOR
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let mloc_o0 = self.ir as u8;

				self.mem[mloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x8   => { // RMOV
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_o0 = hi_nibble(self.ir as u8);

				debug!("moving from {:02X} to {:02X}", rloc_i0, rloc_o0)
				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x9   => { // RSET
				let rloc = lo_nibble((self.ir >> 8) as u8);  // lower nibble of first byte
				let rval = self.ir as u8;                    // value is entire second byte

				self.reg[rloc as uint] = rval;               // store value in register
				Continue
			},

			0xA   => { // JMPEQ
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let next_ip = self.ir as u8;

				if self.reg[rloc_i0 as uint] == self.reg[0] { self.ip = next_ip }
				Continue
			},

			0xB   => { Halt },
			_     => { debug!("halt, cpu on fire: {}", op); Halt },
		}
	}

	/// Load the instruction at `IP` and advance the pointer by two bytes.
	/// The instruction is packed into a single `u16` and stored in the instruction register.
	fn fetch(&mut self) {
		// load PC -> IR
		let byte_1 = self.mem[(self.ip+0) as uint];
		let byte_2 = self.mem[(self.ip+1) as uint];

		self.ir  = (byte_1 as u16 << 8) | (byte_2 as u16);
		self.ip += 2;

		debug!("IR set to 0x{:04X} ({:02X},{:02X})", self.ir, byte_1, byte_2)
	}
}

/// Take the lower nibble of a byte
fn lo_nibble(byte: u8) -> u8 {
	(byte & U4_LOW)
}

/// Take the upper nibble of a byte and shift it
/// towards the least significant bits.
fn hi_nibble(byte: u8) -> u8 {
	(byte & U4_HIGH) >> 4
}

fn main() {
	let mut cpu = P150Cpu::new();
	let program = [0x911E, 0x920C, 0x0123, 0x7340, 0x6040, 0xA310, 0x9500, 0xB000, 0x9501, 0xB000];
	cpu.init_mem(program);

	loop {
		match cpu.tick() {
			Continue => { continue; },
			Halt => { println!("CPU halted."); cpu.dump(); break; },
		}
	}
}

#[test]
fn test_hammer_time() {
	// cpu should stop
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0xB000]);

	assert_eq!(cpu.tick(), Halt)
}

#[test]
fn test_registers() {
	// cpu should set, move registers accordingly
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9110, 0x8130, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], 16)
}

#[test]
fn test_memory() {
	// memory sets and memory stores should read back successfully
	// uninitialized registers should not match initialized registers
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x9330, 0x7140, 0x6240, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x1] == cpu.get_reg()[0x2]);
	assert!(cpu.get_reg()[0x1] != cpu.get_reg()[0x3]);
}

#[test]
fn test_bin() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9121, 0x9222, 0x3123, 0x4124, 0x5125, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x3] == (0x21 & 0x22));
	assert!(cpu.get_reg()[0x4] == (0x21 | 0x22));
	assert!(cpu.get_reg()[0x5] == (0x21 ^ 0x22));
}

#[test]
fn test_math() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x920A, 0x0123, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], cpu.get_reg()[0x1] + cpu.get_reg()[0x2])
}

#[test]
fn test_branch() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9021, 0x9121, 0xA108, 0xB000, 0x922A, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x2], 0x2A)
}

#[test]
fn test_shift() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x90B0, 0x2040, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x0], 0x0B)
}

<commit_msg>(Move architecture byte width into a constant.)<commit_after>#![feature(phase)]
#[phase(plugin, link)] extern crate log;

// masks for pulling nibbles out of bytes
const BYTE_WIDTH: uint = 8;
const U4_LOW:     u8   = 0b00001111;
const U4_HIGH:    u8   = 0b11110000;

/// If the CPU enters the `Halt` state then any additional ticks will result in
/// the program exhibiting undefined behavior.
///
#[deriving(PartialEq,Show)]
enum CpuState {
	Continue,
	Halt,
}

/// The P150 virtual machine
/// Represents all information the CPU needs to continue executing a
/// program stored in its main memory.
struct P150Cpu {
	ip:  u8,
	ir: u16,

	reg: [u8, ..16],
	mem: [u8, ..256],
}

impl P150Cpu {
	/// Initializes the P150 CPU
	///
	/// NOTE: This implementation 0s memory; but this is not guaranteed
	/// by the machine specification.
	///
	/// Programs MUST start at memory address 0x00
	fn new() -> P150Cpu {
		P150Cpu {
			ip:  0x00,
			ir:  0x0000,

			reg: [0u8, ..16],
			mem: [0u8, ..256],
		}
	}

	/// Prints the current machine state to the console window
	/// This includes the IP, IR, and all registers.
	/// (Registers will be formatted as 2s complement numbers.)
	fn dump(&self) {
		println!("IP: 0x{:02X}, IR: 0x{:04X}", self.ip, self.ir);

		println!("---\nRegisters\n---")
		for (addr, cell) in self.reg.iter().enumerate() {
			println!("{:01X}: {}", addr, cell)
		}

		println!("---")

		debug!("Memory")
		for (addr, cell) in self.mem.iter().enumerate() {
			debug!("{:02X}: {}", addr, cell)
		}
	}

	#[cfg(test)]
	fn get_reg(&self) -> &[u8] {
		self.reg
	}

	/// Read an array of instructions into main memory
	/// This reads two bytes at a time from the `memory` array
	/// and loads them into the P150s RAM bank, starting from address 0.
	fn init_mem(&mut self, memory: &[u16]) {
		let mut next_cell = 0x00;

		for op in memory.iter() {
			let byte_1 = (*op >> 8) as u8;
			let byte_2 = *op as u8;

			self.mem[next_cell]     = byte_1;
			self.mem[next_cell + 1] = byte_2;
			next_cell += 2
		}
	}

	/// Runs the entire fetch, execute, decode cycle against the current IP
	fn tick(&mut self) -> CpuState {
		self.fetch();

		// decode & execute
		let op = (self.ir >> 12) as u8;
		match op {
			0x0   => { // ADDB
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);   // first input: lower nibble of first byte
				let rloc_i1 = hi_nibble(self.ir as u8);          // second input: upper nibble of second byte
				let rloc_o0 = lo_nibble(self.ir as u8);          // output: lower nibble of second byte

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] + self.reg[rloc_i1 as uint];
				Continue
			},

			0x2   => { // ROT
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let swidth  = hi_nibble(self.ir as u8);
				let swidth = if swidth > 8 { swidth - 8 } else { swidth };
				let swidth = swidth as uint;

				// LHS shifts <width> bits off the (left) end of the bitstring
				// RHS shifts the bitstring to the right until only the bits which fell off remain.
				//   LHS is the remaining MSB bits; RHS is the remaining LSB bits
				//   ∴ LHS <OR> RHS provides a rotated bitstring
				let x = self.reg[rloc_i0 as uint];
				self.reg[rloc_i0 as uint] = (x << swidth) | (x >> (BYTE_WIDTH - swidth));

				Continue
			},

			0x3   => { // AND
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_i1 = hi_nibble(self.ir as u8);
				let rloc_o0 = lo_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] & self.reg[rloc_i1 as uint];
				Continue
			},

			0x4   => { // OR
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_i1 = hi_nibble(self.ir as u8);
				let rloc_o0 = lo_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] | self.reg[rloc_i1 as uint];
				Continue
			},

			0x5   => { // XOR
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_i1 = hi_nibble(self.ir as u8);
				let rloc_o0 = lo_nibble(self.ir as u8);

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] ^ self.reg[rloc_i1 as uint];
				Continue
			},

			0x6   => { // MLOAD
				let rloc_o0 = lo_nibble((self.ir >> 8) as u8);
				let mloc_i0 = self.ir as u8;

				self.reg[rloc_o0 as uint] = self.mem[mloc_i0 as uint];
				Continue
			},

			0x7   => { // MSTOR
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let mloc_o0 = self.ir as u8;

				self.mem[mloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x8   => { // RMOV
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let rloc_o0 = hi_nibble(self.ir as u8);

				debug!("moving from {:02X} to {:02X}", rloc_i0, rloc_o0)
				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint];
				Continue
			},

			0x9   => { // RSET
				let rloc = lo_nibble((self.ir >> 8) as u8);  // lower nibble of first byte
				let rval = self.ir as u8;                    // value is entire second byte

				self.reg[rloc as uint] = rval;               // store value in register
				Continue
			},

			0xA   => { // JMPEQ
				let rloc_i0 = lo_nibble((self.ir >> 8) as u8);
				let next_ip = self.ir as u8;

				if self.reg[rloc_i0 as uint] == self.reg[0] { self.ip = next_ip }
				Continue
			},

			0xB   => { Halt },
			_     => { debug!("halt, cpu on fire: {}", op); Halt },
		}
	}

	/// Load the instruction at `IP` and advance the pointer by two bytes.
	/// The instruction is packed into a single `u16` and stored in the instruction register.
	fn fetch(&mut self) {
		// load PC -> IR
		let byte_1 = self.mem[(self.ip+0) as uint];
		let byte_2 = self.mem[(self.ip+1) as uint];

		self.ir  = (byte_1 as u16 << 8) | (byte_2 as u16);
		self.ip += 2;

		debug!("IR set to 0x{:04X} ({:02X},{:02X})", self.ir, byte_1, byte_2)
	}
}

/// Take the lower nibble of a byte
fn lo_nibble(byte: u8) -> u8 {
	(byte & U4_LOW)
}

/// Take the upper nibble of a byte and shift it
/// towards the least significant bits.
fn hi_nibble(byte: u8) -> u8 {
	(byte & U4_HIGH) >> 4
}

fn main() {
	let mut cpu = P150Cpu::new();
	let program = [0x911E, 0x920C, 0x0123, 0x7340, 0x6040, 0xA310, 0x9500, 0xB000, 0x9501, 0xB000];
	cpu.init_mem(program);

	loop {
		match cpu.tick() {
			Continue => { continue; },
			Halt => { println!("CPU halted."); cpu.dump(); break; },
		}
	}
}

#[test]
fn test_hammer_time() {
	// cpu should stop
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0xB000]);

	assert_eq!(cpu.tick(), Halt)
}

#[test]
fn test_registers() {
	// cpu should set, move registers accordingly
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9110, 0x8130, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], 16)
}

#[test]
fn test_memory() {
	// memory sets and memory stores should read back successfully
	// uninitialized registers should not match initialized registers
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x9330, 0x7140, 0x6240, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x1] == cpu.get_reg()[0x2]);
	assert!(cpu.get_reg()[0x1] != cpu.get_reg()[0x3]);
}

#[test]
fn test_bin() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9121, 0x9222, 0x3123, 0x4124, 0x5125, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert!(cpu.get_reg()[0x3] == (0x21 & 0x22));
	assert!(cpu.get_reg()[0x4] == (0x21 | 0x22));
	assert!(cpu.get_reg()[0x5] == (0x21 ^ 0x22));
}

#[test]
fn test_math() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9120, 0x920A, 0x0123, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x3], cpu.get_reg()[0x1] + cpu.get_reg()[0x2])
}

#[test]
fn test_branch() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x9021, 0x9121, 0xA108, 0xB000, 0x922A, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x2], 0x2A)
}

#[test]
fn test_shift() {
	let mut cpu = P150Cpu::new();
	cpu.init_mem([0x90B0, 0x2040, 0xB000]);

	loop {
		if cpu.tick() == Halt { break; }
	}

	assert_eq!(cpu.get_reg()[0x0], 0x0B)
}

<|endoftext|>


<commit_before>extern crate liner;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use liner::Context;
use liner::KeyBindings;
use std::collections::HashMap;
use std::collections::hash_map::Keys;
use std::fs::File;
use std::io::{ErrorKind, Read};
use std::mem;
use std::path::Path;

type Data = HashMap<String, String>;

#[derive(Deserialize)]
struct CacheConfig {
    ttl: usize,
}

#[derive(Deserialize)]
struct Config {
    cache: CacheConfig,
}

struct Cache {
    data: Box<Data>,
    config: Config,
    bytes: usize
}

impl Cache {
    fn clear(&mut self) {
        self.data.clear();
        self.bytes = 0;
    }

    fn get(&mut self, key: &String) -> Option<&String> {
        self.data.get(key)
    }

    fn set(&mut self, key: String, value: String) {
        self.bytes += mem::size_of_val(&key) + mem::size_of_val(&value);
        self.data.insert(key, value);
    }

    fn delete(&mut self, key: &String) {
        let value = self.data.remove(key);
        self.bytes -= mem::size_of_val(key) + mem::size_of_val(&value);
    }

    fn keys(&self) -> Keys<String, String> {
        self.data.keys()
    }

    fn size(&self) -> usize {
        self.bytes
    }
}

fn main() {
    // Open and read configuration
    let config_path = Path::new("etc/carpool.toml");
    let mut config_file = match File::open(&config_path) {
        Err(_) => panic!("couldn't open {:?}", config_path),
        Ok(file) => file,
    };

    let mut config_content = String::new();
    let _ = config_file.read_to_string(&mut config_content);

    let config: Config = toml::from_str(config_content.as_str()).unwrap();

    // Set up cache
    let mut cache = Cache {
        config: config,
        data: Box::new(HashMap::new()),
        bytes: 0,
    };

    let empty_value = String::from("");

    // Start REPL
    let mut con = Context::new();

    loop {
        let res = con.read_line("> ", &mut |_| {});

        match res {
            Ok(res) => {
                match res.as_str() {
                    s if s.trim() == "" =>  {}
                    s if s.starts_with("get ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        println!("{}", cache.get(&key_trimmed).unwrap_or(&empty_value));
                    }
                    s if s.starts_with("del ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        let value = cache.delete(&key_trimmed);
                    }
                    s if s.starts_with("set ") =>  {
                        let (_, key_value) = s.split_at(4);
                        match key_value.find(' ') {
                            Some(i) => {
                                let (key, value) = key_value.split_at(i);
                                let key_trimmed = String::from(key.trim());
                                let value_trimmed = String::from(value.trim());
                                cache.set(key_trimmed, value_trimmed);
                            }
                            None => println!("no value specified")
                        }
                    }
                    "size" =>  {
                        println!("{} bytes", cache.size());
                    }
                    "keys" =>  {
                        for key in cache.keys() {
                            println!("{}", key);
                        }
                    }
                    "emacs" => {
                        con.key_bindings = KeyBindings::Emacs;
                        println!("emacs mode");
                    }
                    "vi" | "vim" =>  {
                        con.key_bindings = KeyBindings::Vi;
                        println!("vi mode");
                    }
                    "exit" =>  {
                        break;
                    }
                    _ => {
                        println!("operation not defined")
                    }
                }

                con.history.push(res.into()).unwrap();
            }
            Err(e) => {
                match e.kind() {
                    // ctrl-c pressed
                    ErrorKind::Interrupted => {}
                    // ctrl-d pressed
                    ErrorKind::UnexpectedEof => {
                        break;
                    }
                    _ => panic!("error: {:?}", e),
                }
            }
        }
    }
}
<commit_msg>Fix size operation<commit_after>extern crate liner;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use liner::Context;
use liner::KeyBindings;
use std::collections::HashMap;
use std::collections::hash_map::Keys;
use std::fs::File;
use std::io::{ErrorKind, Read};
use std::mem;
use std::path::Path;

type Data = HashMap<String, String>;

#[derive(Deserialize)]
struct CacheConfig {
    ttl: usize,
}

#[derive(Deserialize)]
struct Config {
    cache: CacheConfig,
}

struct Cache {
    data: Box<Data>,
    config: Config,
    bytes: usize
}

fn get_size_of_string(s: &String) -> usize {
    return s.len() * std::mem::size_of::<u8>();
}

impl Cache {
    fn clear(&mut self) {
        self.data.clear();
        self.bytes = 0;
    }

    fn get(&mut self, key: &String) -> Option<&String> {
        self.data.get(key)
    }

    fn set(&mut self, key: String, value: String) {
        if let Some(previous_value) = self.data.get(&key) {
            self.bytes -= get_size_of_string(previous_value);
        }
        else {
            self.bytes += get_size_of_string(&key);
        }
        self.bytes += get_size_of_string(&value);

        self.data.insert(key, value);
    }

    fn delete(&mut self, key: &String) {
        if let Some(value) = self.data.remove(key) {
            self.bytes -= get_size_of_string(&value) + get_size_of_string(key);
        }
    }

    fn keys(&self) -> Keys<String, String> {
        self.data.keys()
    }

    fn size(&self) -> usize {
        self.bytes
    }
}

fn main() {
    // Open and read configuration
    let config_path = Path::new("etc/carpool.toml");
    let mut config_file = match File::open(&config_path) {
        Err(_) => panic!("couldn't open {:?}", config_path),
        Ok(file) => file,
    };

    let mut config_content = String::new();
    let _ = config_file.read_to_string(&mut config_content);

    let config: Config = toml::from_str(config_content.as_str()).unwrap();

    // Set up cache
    let mut cache = Cache {
        config: config,
        data: Box::new(HashMap::new()),
        bytes: 0,
    };

    let empty_value = String::from("");

    // Start REPL
    let mut con = Context::new();

    loop {
        let res = con.read_line("> ", &mut |_| {});

        match res {
            Ok(res) => {
                match res.as_str() {
                    s if s.trim() == "" =>  {}
                    s if s.starts_with("get ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        println!("{}", cache.get(&key_trimmed).unwrap_or(&empty_value));
                    }
                    s if s.starts_with("del ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        let value = cache.delete(&key_trimmed);
                    }
                    s if s.starts_with("set ") =>  {
                        let (_, key_value) = s.split_at(4);
                        match key_value.find(' ') {
                            Some(i) => {
                                let (key, value) = key_value.split_at(i);
                                let key_trimmed = String::from(key.trim());
                                let value_trimmed = String::from(value.trim());
                                cache.set(key_trimmed, value_trimmed);
                            }
                            None => println!("no value specified")
                        }
                    }
                    "size" =>  {
                        println!("{} bytes", cache.size());
                    }
                    "keys" =>  {
                        for key in cache.keys() {
                            println!("{}", key);
                        }
                    }
                    "emacs" => {
                        con.key_bindings = KeyBindings::Emacs;
                        println!("emacs mode");
                    }
                    "vi" | "vim" =>  {
                        con.key_bindings = KeyBindings::Vi;
                        println!("vi mode");
                    }
                    "exit" =>  {
                        break;
                    }
                    _ => {
                        println!("operation not defined")
                    }
                }

                con.history.push(res.into()).unwrap();
            }
            Err(e) => {
                match e.kind() {
                    // ctrl-c pressed
                    ErrorKind::Interrupted => {}
                    // ctrl-d pressed
                    ErrorKind::UnexpectedEof => {
                        break;
                    }
                    _ => panic!("error: {:?}", e),
                }
            }
        }
    }
}
<|endoftext|>