<commit_before>use messages::outgoing;
use mio;
use node::Node;
use node;
use routing_table::{InsertOutcome, RoutingTable};
use routing_table;
use std::collections::HashMap;
use std::io::Cursor;
use std::thread;
use transaction::{TransactionId, TransactionIdGenerator};

pub enum ScheduledTask {
    ContinueBootstrap,
    ContinueHealthCheck
}

pub enum OneshotTask {
    Incoming(Vec<u8>),
    StartBootstrap
}

enum TableAction {
    Bootstrap(mio::Timeout),
    HealthCheck(mio::Timeout)
}

enum Status {
    Bootstrapping,
    Idle
}

pub struct Handler {
    port: u16,
    routing_table: RoutingTable,
    self_node: Box<node::Node + 'static>,
    transaction_ids: TransactionIdGenerator,
    status: Status,
    pending_actions: HashMap<TransactionId, TableAction>
}

impl Handler {
    pub fn new<N: node::Node + 'static>(self_node: N, port: u16, routers: Vec<Box<Node>>) -> Handler {
        let self_address = self_node.get_address();
        let routing_table = routing_table::RoutingTable::new(8, self_address, routers);

        Handler {
            port: port,
            routing_table: routing_table,
            self_node: Box::new(self_node),
            transaction_ids: TransactionIdGenerator::new(),
            status: Status::Idle,
            pending_actions: HashMap::new()
        }
    }

    pub fn run(mut self) {
        let mut event_loop = mio::EventLoop::new().unwrap();
        let loop_channel = event_loop.channel();

        create_incoming_udp_channel(self.port, loop_channel.clone());
        loop_channel.send(OneshotTask::StartBootstrap).unwrap();
        event_loop.run(&mut self).unwrap();
    }

    fn handle_incoming(&mut self, data: Vec<u8>, event_loop: &mut mio::EventLoop<Handler>) {
        use messages::incoming;
        use messages::incoming::*;
        let mut data = Cursor::new(data);
        let message = incoming::parse_from_reader(&mut data).unwrap();

        match message {
            Message::Query(transaction_id, origin, query) => {
                let origin_address = origin.get_address();
                match query {
                    Query::FindNode(target) => {
                        let response: Vec<u8> = outgoing::create_find_node_response(
                                transaction_id,
                                &self.self_node,
                                self.routing_table.nearest_to(&target, false));
                        origin.send(response);

                        self.routing_table.insert(origin).unwrap();
                        {
                            if let Some(origin) = self.routing_table.find_node(&origin_address) {
                                origin.received_query(transaction_id);
                            }
                        }
                    },
                    Query::Ping => {
                        let response = outgoing::create_ping_response(
                            transaction_id,
                            &self.self_node);
                        origin.send(response);

                        self.routing_table.insert(origin).unwrap();
                        if let Some(origin) = self.routing_table.find_node(&origin_address) {
                            origin.received_query(transaction_id);
                        }
                    }
                }
            }
            Message::Response(transaction_id, origin, response) => {
                let origin_address = origin.get_address();
                match response {
                    Response::FindNode(mut nodes) => {
                        let mut encounted_new_node = false;
                        let mut tail = vec![origin];
                        for node in nodes.drain(..).chain(tail.drain(..)) {
                            match self.routing_table.insert(node) {
                                Ok(InsertOutcome::Inserted) => {
                                    encounted_new_node = true;
                                }
                                _ => { }
                            }
                        }

                        {
                            let mut origin = self.routing_table
                                .find_node(&origin_address)
                                .expect("Got find node response from unknown node");
                            origin.received_response(transaction_id);
                        }

                        match self.pending_actions.remove(&transaction_id) {
                            Some(TableAction::Bootstrap(timeout)) => {
                                event_loop.clear_timeout(timeout);
                            }
                            Some(TableAction::HealthCheck(timeout)) => {
                                event_loop.clear_timeout(timeout);
                            }
                            None => { }
                        }

                        match self.status {
                            Status::Bootstrapping => {
                                if encounted_new_node {
                                    // Continue botstrapping
                                    self.continue_bootstrap(event_loop);
                                } else {
                                    self.status = Status::Idle;
                                    self.continue_health_check(event_loop);
                                }
                            }
                            _ => { }
                        }
                    }
                    Response::Ping => {
                        let mut origin = self.routing_table.find_node(&origin_address).expect("Got ping response from unknown node");
                        origin.received_response(transaction_id);
                    }
                }
            }
        }
    }

    fn start_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Self>) {
        self.status = Status::Bootstrapping;
        self.continue_bootstrap(event_loop);
    }

    fn continue_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Self>) {
        let transaction_id = self.find_self();
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueBootstrap, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::Bootstrap(timeout));
    }

    fn continue_health_check(&mut self, event_loop: &mut mio::EventLoop<Self>) {
        let transaction_id = self.health_check();
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueHealthCheck, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::HealthCheck(timeout));
    }

    fn find_self(&mut self) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();
        let query = outgoing::create_find_node_query(
            transaction_id,
            &self.self_node,
            self.self_node.get_address());
        if let Some(node) = self.routing_table.nearest().get_mut(0) {
            {
                node.send(query);
            }
            node.sent_query(transaction_id);
        }
        transaction_id
    }

    fn health_check(&mut self) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();
        if let Some(node) = self.routing_table.questionable_nodes().get_mut(0) {
            let query = outgoing::create_ping_query(
                transaction_id, &self.self_node);
            node.send(query);
            node.sent_query(transaction_id);
        }
        transaction_id
    }
}

impl mio::Handler for Handler {
    type Timeout = ScheduledTask;
    type Message = OneshotTask;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Handler>, task: OneshotTask) {
        match task {
            OneshotTask::Incoming(data) => self.handle_incoming(data, event_loop),
            OneshotTask::StartBootstrap => self.start_bootstrap(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Handler>, timeout: ScheduledTask) {
        match timeout {
            ScheduledTask::ContinueBootstrap => self.continue_bootstrap(event_loop),
            ScheduledTask::ContinueHealthCheck => self.continue_health_check(event_loop)
        }
    }
}

fn create_incoming_udp_channel(port: u16, sender: mio::Sender<OneshotTask>) {
    use std::net::UdpSocket;
    thread::spawn(move || {
        let address = ("0.0.0.0", port);
        let socket = UdpSocket::bind(address).unwrap();
        loop {
            let mut buf = [0; 4096];
            match socket.recv_from(&mut buf) {
                Ok((size, _src)) => {
                    sender.send(OneshotTask::Incoming(buf[..size].iter().cloned().collect())).unwrap();
                }
                Err(e) => panic!("Error receiving from server: {}", e)
            }
        }
    });
}
<commit_msg>Separate network logic from Handler<commit_after>use messages::outgoing;
use mio;
use node::Node;
use node;
use routing_table::{InsertOutcome, RoutingTable};
use routing_table;
use std::collections::HashMap;
use std::io::Cursor;
use std::thread;
use transaction::{TransactionId, TransactionIdGenerator};

pub enum ScheduledTask {
    ContinueBootstrap,
    ContinueHealthCheck
}

pub enum OneshotTask {
    Incoming(Vec<u8>),
    StartBootstrap
}

enum TableAction {
    Bootstrap(mio::Timeout),
    HealthCheck(mio::Timeout)
}

enum Status {
    Bootstrapping,
    Idle
}

pub struct Network {
    port: u16,
    routing_table: RoutingTable,
    self_node: Box<node::Node + 'static>,
    transaction_ids: TransactionIdGenerator,
    status: Status,
    pending_actions: HashMap<TransactionId, TableAction>
}

impl Network {
    pub fn new<N: node::Node + 'static>(self_node: N, port: u16, routers: Vec<Box<Node>>) -> Network {
        let self_address = self_node.get_address().clone();
        let routing_table = routing_table::RoutingTable::new(8, self_address, routers);

        Network {
            port: port,
            routing_table: routing_table,
            self_node: Box::new(self_node),
            transaction_ids: TransactionIdGenerator::new(),
            status: Status::Idle,
            pending_actions: HashMap::new()
        }
    }

        let mut event_loop = mio::EventLoop::new().unwrap();
    pub fn run(self) {
        let loop_channel = event_loop.channel();

        create_incoming_udp_channel(self.port, loop_channel.clone());
        loop_channel.send(OneshotTask::StartBootstrap).unwrap();
        let mut handler = Handler::new(self);
        event_loop.run(&mut handler).unwrap();
    }

    fn handle_incoming(&mut self, data: Vec<u8>, event_loop: &mut mio::EventLoop<Handler>) {
        use messages::incoming;
        use messages::incoming::*;
        let mut data = Cursor::new(data);
        let message = incoming::parse_from_reader(&mut data).unwrap();

        match message {
            Message::Query(transaction_id, origin, query) => {
                let origin_address = origin.get_address();
                match query {
                    Query::FindNode(target) => {
                        let response: Vec<u8> = outgoing::create_find_node_response(
                                transaction_id,
                                &self.self_node,
                                self.routing_table.nearest_to(&target, false));
                        origin.send(response);

                        self.routing_table.insert(origin).unwrap();
                        {
                            if let Some(origin) = self.routing_table.find_node(&origin_address) {
                                origin.received_query(transaction_id);
                            }
                        }
                    },
                    Query::Ping => {
                        let response = outgoing::create_ping_response(
                            transaction_id,
                            &self.self_node);
                        origin.send(response);

                        self.routing_table.insert(origin).unwrap();
                        if let Some(origin) = self.routing_table.find_node(&origin_address) {
                            origin.received_query(transaction_id);
                        }
                    }
                }
            }
            Message::Response(transaction_id, origin, response) => {
                let origin_address = origin.get_address();
                match response {
                    Response::FindNode(mut nodes) => {
                        let mut encounted_new_node = false;
                        let mut tail = vec![origin];
                        for node in nodes.drain(..).chain(tail.drain(..)) {
                            match self.routing_table.insert(node) {
                                Ok(InsertOutcome::Inserted) => {
                                    encounted_new_node = true;
                                }
                                _ => { }
                            }
                        }

                        {
                            let mut origin = self.routing_table
                                .find_node(&origin_address)
                                .expect("Got find node response from unknown node");
                            origin.received_response(transaction_id);
                        }

                        match self.pending_actions.remove(&transaction_id) {
                            Some(TableAction::Bootstrap(timeout)) => {
                                event_loop.clear_timeout(timeout);
                            }
                            Some(TableAction::HealthCheck(timeout)) => {
                                event_loop.clear_timeout(timeout);
                            }
                            None => { }
                        }

                        match self.status {
                            Status::Bootstrapping => {
                                if encounted_new_node {
                                    // Continue botstrapping
                                    self.continue_bootstrap(event_loop);
                                } else {
                                    self.status = Status::Idle;
                                    self.continue_health_check(event_loop);
                                }
                            }
                            _ => { }
                        }
                    }
                    Response::Ping => {
                        let mut origin = self.routing_table.find_node(&origin_address).expect("Got ping response from unknown node");
                        origin.received_response(transaction_id);
                    }
                }
            }
        }
    }

    fn start_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        self.status = Status::Bootstrapping;
        self.continue_bootstrap(event_loop);
    }

        let transaction_id = self.find_self();
    fn continue_bootstrap(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueBootstrap, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::Bootstrap(timeout));
    }

    fn continue_health_check(&mut self, event_loop: &mut mio::EventLoop<Handler>) {
        let transaction_id = self.health_check();
        let timeout = event_loop.timeout_ms(ScheduledTask::ContinueHealthCheck, 1000).unwrap();
        self.pending_actions.insert(transaction_id, TableAction::HealthCheck(timeout));
    }

    fn find_self(&mut self) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();
        let query = outgoing::create_find_node_query(
            transaction_id,
            &self.self_node,
            self.self_node.get_address());
        if let Some(node) = self.routing_table.nearest().get_mut(0) {
            {
                node.send(query);
            }
            node.sent_query(transaction_id);
        }
        transaction_id
    }

    fn health_check(&mut self) -> TransactionId {
        let transaction_id = self.transaction_ids.generate();
        if let Some(node) = self.routing_table.questionable_nodes().get_mut(0) {
            let query = outgoing::create_ping_query(
                transaction_id, &self.self_node);
            node.send(query);
            node.sent_query(transaction_id);
        }
        transaction_id
    }
struct Handler {
    network: Network
}

impl Handler {
    fn new(network: Network) -> Handler {
        Handler {
            network: network
        }
    }
}

impl mio::Handler for Handler {
    type Timeout = ScheduledTask;
    type Message = OneshotTask;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Handler>, task: OneshotTask) {
        match task {
            OneshotTask::Incoming(data) => self.network.handle_incoming(data, event_loop),
            OneshotTask::StartBootstrap => self.network.start_bootstrap(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Handler>, timeout: ScheduledTask) {
        match timeout {
            ScheduledTask::ContinueBootstrap => self.network.continue_bootstrap(event_loop),
            ScheduledTask::ContinueHealthCheck => self.network.continue_health_check(event_loop),
            ScheduledTask::ContinueRefresh => self.network.continue_refresh(event_loop)
        }
    }
}

fn create_incoming_udp_channel(port: u16, sender: mio::Sender<OneshotTask>) {
    use std::net::UdpSocket;
    thread::spawn(move || {
        let address = ("0.0.0.0", port);
        let socket = UdpSocket::bind(address).unwrap();
        loop {
            let mut buf = [0; 4096];
            match socket.recv_from(&mut buf) {
                Ok((size, _src)) => {
                    sender.send(OneshotTask::Incoming(buf[..size].iter().cloned().collect())).unwrap();
                }
                Err(e) => panic!("Error receiving from server: {}", e)
            }
        }
    });
}
<|endoftext|>


<commit_before>extern crate crypto;
extern crate lzma;
extern crate rustc_serialize;
extern crate std;
extern crate tar;

use crypto::digest::Digest;
use std::io::Read;

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Lzma(lzma::LzmaError),
    ParseInt(std::num::ParseIntError),
    Custom(std::borrow::Cow<'static, str>),
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        Error::Io(e)
    }
}

impl From<lzma::LzmaError> for Error {
    fn from(e: lzma::LzmaError) -> Self {
        Error::Lzma(e)
    }
}

impl From<std::num::ParseIntError> for Error {
    fn from(e: std::num::ParseIntError) -> Self {
        Error::ParseInt(e)
    }
}

impl From<&'static str> for Error {
    fn from(e: &'static str) -> Self {
        Error::Custom(std::borrow::Cow::from(e))
    }
}

impl From<String> for Error {
    fn from(e: String) -> Self {
        Error::Custom(std::borrow::Cow::from(e))
    }
}

#[derive(Debug, Clone)]
pub struct Package {
    pkginfo: PkgInfo,
    size: u64,
    filename: String,
    pgpsig: String,
    md5sum: String,
    sha256sum: String,
    files: Vec<String>,
}

impl Package {
    pub fn load<P: AsRef<std::path::Path>>(path: &P) -> Result<Package, Error> {
        let (pkginfo, files) = try!(PkgInfo::load(path));
        let filename = path.as_ref().file_name().unwrap().to_string_lossy().into_owned();
        let sig_path = path.as_ref().parent().unwrap().join(format!("{}.sig", filename));
        let pgpsig = if let Ok(mut f) = std::fs::File::open(sig_path) {
            use rustc_serialize::base64::ToBase64;

            let mut buf = vec![];
            try!(f.read_to_end(&mut buf));
            buf.to_base64(rustc_serialize::base64::STANDARD)
        } else {
            "".to_owned()
        };
        let mut md5 = crypto::md5::Md5::new();
        let mut sha256 = crypto::sha2::Sha256::new();
        let mut f = try!(std::fs::File::open(path));
        loop {
            let mut buf = [0; 1024];
            match f.read(&mut buf) {
                Ok(0) => {
                    break;
                }
                Ok(len) => {
                    md5.input(&buf[..len]);
                    sha256.input(&buf[..len]);
                }
                Err(e) => {
                    return Err(Error::from(e));
                }
            }
        }

        Ok(Package {
            pkginfo: pkginfo,
            size: try!(std::fs::metadata(path)).len(),
            filename: filename,
            pgpsig: pgpsig,
            md5sum: md5.result_str(),
            sha256sum: sha256.result_str(),
            files: files,
        })
    }

    pub fn groups(&self) -> &Vec<String> {
        &self.pkginfo.groups
    }
    pub fn license(&self) -> &Vec<String> {
        &self.pkginfo.license
    }
    pub fn replaces(&self) -> &Vec<String> {
        &self.pkginfo.replaces
    }
    pub fn filename(&self) -> &str {
        &self.filename
    }
    pub fn pkgname(&self) -> &str {
        &self.pkginfo.pkgname
    }
    pub fn pkgbase(&self) -> &str {
        &self.pkginfo.pkgbase
    }
    pub fn pkgver(&self) -> &str {
        &self.pkginfo.pkgver
    }
    pub fn pkgdesc(&self) -> &str {
        &self.pkginfo.pkgdesc
    }
    pub fn csize(&self) -> u64 {
        self.size
    }
    pub fn isize(&self) -> u64 {
        self.pkginfo.size
    }
    pub fn md5sum(&self) -> &str {
        &self.md5sum
    }
    pub fn sha256sum(&self) -> &str {
        &self.sha256sum
    }
    pub fn pgpsig(&self) -> &str {
        &self.pgpsig
    }
    pub fn url(&self) -> &str {
        &self.pkginfo.url
    }
    pub fn arch(&self) -> &str {
        &self.pkginfo.arch
    }
    pub fn builddate(&self) -> u64 {
        self.pkginfo.builddate
    }
    pub fn packager(&self) -> &str {
        &self.pkginfo.packager
    }

    pub fn conflicts(&self) -> &Vec<String> {
        &self.pkginfo.conflicts
    }
    pub fn provides(&self) -> &Vec<String> {
        &self.pkginfo.provides
    }
    pub fn depends(&self) -> &Vec<String> {
        &self.pkginfo.depends
    }
    pub fn makedepends(&self) -> &Vec<String> {
        &self.pkginfo.makedepends
    }
    pub fn checkdepends(&self) -> &Vec<String> {
        &self.pkginfo.checkdepends
    }
    pub fn optdepends(&self) -> &Vec<String> {
        &self.pkginfo.optdepends
    }

    pub fn files(&self) -> &Vec<String> {
        &self.files
    }
}

#[derive(Debug, Default, Clone)]
pub struct PkgInfo {
    pub pkgname: String,
    pub pkgbase: String,
    pub pkgver: String,
    pub pkgdesc: String,
    pub url: String,
    pub builddate: u64,
    pub packager: String,
    pub size: u64,
    pub arch: String,
    pub license: Vec<String>,
    pub groups: Vec<String>,
    pub depends: Vec<String>,
    pub makedepends: Vec<String>,
    pub checkdepends: Vec<String>,
    pub optdepends: Vec<String>,
    pub conflicts: Vec<String>,
    pub provides: Vec<String>,
    pub replaces: Vec<String>,
}

impl PkgInfo {
    fn load<P: AsRef<std::path::Path>>(path: &P) -> Result<(Self, Vec<String>), Error> {
        let file = try!(std::fs::File::open(path));
        let xz_reader = try!(lzma::LzmaReader::new_decompressor(file));
        let mut tar_reader = tar::Archive::new(xz_reader);
        let mut pkginfo = None;
        let mut files = vec![];
        for entry_result in try!(tar_reader.entries()) {
            let mut entry = try!(entry_result);
            let path = try!(entry.path()).to_mut().to_string_lossy().into_owned();
            if path == ".PKGINFO" && entry.header().entry_type() == tar::EntryType::Regular {
                let mut body = String::new();
                try!(entry.read_to_string(&mut body));
                pkginfo = Some(try!(parse_pkginfo(&body)));
            }
            if !path.starts_with(".") {
                files.push(path);
            }
        }
        if let Some(pkginfo) = pkginfo {
            Ok((pkginfo, files))
        } else {
            Err(Error::from(".PKGINFO not found"))
        }
    }
}

fn parse_pkginfo(body: &str) -> Result<PkgInfo, Error> {
    let mut info = PkgInfo::default();
    for line in body.lines() {
        if line.starts_with('#') {
            continue;
        }
        let mut splitn = line.splitn(2, '=');
        let key = splitn.next();
        let val = splitn.next();
        let rest = splitn.next();
        if let (Some(key), Some(val), None) = (key, val, rest) {
            let key = key.trim();
            let val = val.trim();
            match key {
                "pkgname" => info.pkgname = val.to_owned(),
                "pkgbase" => info.pkgbase = val.to_owned(),
                "pkgver" => info.pkgver = val.to_owned(),
                "pkgdesc" => info.pkgdesc = val.to_owned(),
                "url" => info.url = val.to_owned(),
                "builddate" => info.builddate = try!(val.parse()),
                "packager" => info.packager = val.to_owned(),
                "size" => info.size = try!(val.parse()),
                "arch" => info.arch = val.to_owned(),
                "license" => info.license.push(val.to_owned()),
                "group" => info.groups.push(val.to_owned()),
                "depend" => info.depends.push(val.to_owned()),
                "makedepend" => info.makedepends.push(val.to_owned()),
                "checkdepend" => info.checkdepends.push(val.to_owned()),
                "optdepend" => info.optdepends.push(val.to_owned()),
                "conflict" => info.conflicts.push(val.to_owned()),
                "provides" => info.provides.push(val.to_owned()),
                "replaces" => info.replaces.push(val.to_owned()),
                _ => return Err(Error::from(format!("Unknown PKGINFO entry '{}': {}", key, line))),
            }
        } else {
            return Err(Error::from(format!("Invalid line: {}", line)));
        }
    }
    Ok(info)
}
<commit_msg>as_ref<commit_after>extern crate crypto;
extern crate lzma;
extern crate rustc_serialize;
extern crate std;
extern crate tar;

use crypto::digest::Digest;
use std::io::Read;

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Lzma(lzma::LzmaError),
    ParseInt(std::num::ParseIntError),
    Custom(std::borrow::Cow<'static, str>),
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        Error::Io(e)
    }
}

impl From<lzma::LzmaError> for Error {
    fn from(e: lzma::LzmaError) -> Self {
        Error::Lzma(e)
    }
}

impl From<std::num::ParseIntError> for Error {
    fn from(e: std::num::ParseIntError) -> Self {
        Error::ParseInt(e)
    }
}

impl From<&'static str> for Error {
    fn from(e: &'static str) -> Self {
        Error::Custom(std::borrow::Cow::from(e))
    }
}

impl From<String> for Error {
    fn from(e: String) -> Self {
        Error::Custom(std::borrow::Cow::from(e))
    }
}

#[derive(Debug, Clone)]
pub struct Package {
    pkginfo: PkgInfo,
    size: u64,
    filename: String,
    pgpsig: String,
    md5sum: String,
    sha256sum: String,
    files: Vec<String>,
}

impl Package {
    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<Package, Error> {
        let (pkginfo, files) = try!(PkgInfo::load(path.as_ref()));
        let filename = path.as_ref().file_name().unwrap().to_string_lossy().into_owned();
        let sig_path = path.as_ref().parent().unwrap().join(format!("{}.sig", filename));
        let pgpsig = if let Ok(mut f) = std::fs::File::open(sig_path) {
            use rustc_serialize::base64::ToBase64;

            let mut buf = vec![];
            try!(f.read_to_end(&mut buf));
            buf.to_base64(rustc_serialize::base64::STANDARD)
        } else {
            "".to_owned()
        };
        let mut md5 = crypto::md5::Md5::new();
        let mut sha256 = crypto::sha2::Sha256::new();
        let mut f = try!(std::fs::File::open(path.as_ref()));
        loop {
            let mut buf = [0; 1024];
            match f.read(&mut buf) {
                Ok(0) => {
                    break;
                }
                Ok(len) => {
                    md5.input(&buf[..len]);
                    sha256.input(&buf[..len]);
                }
                Err(e) => {
                    return Err(Error::from(e));
                }
            }
        }

        Ok(Package {
            pkginfo: pkginfo,
            size: try!(std::fs::metadata(path)).len(),
            filename: filename,
            pgpsig: pgpsig,
            md5sum: md5.result_str(),
            sha256sum: sha256.result_str(),
            files: files,
        })
    }

    pub fn groups(&self) -> &Vec<String> {
        &self.pkginfo.groups
    }
    pub fn license(&self) -> &Vec<String> {
        &self.pkginfo.license
    }
    pub fn replaces(&self) -> &Vec<String> {
        &self.pkginfo.replaces
    }
    pub fn filename(&self) -> &str {
        &self.filename
    }
    pub fn pkgname(&self) -> &str {
        &self.pkginfo.pkgname
    }
    pub fn pkgbase(&self) -> &str {
        &self.pkginfo.pkgbase
    }
    pub fn pkgver(&self) -> &str {
        &self.pkginfo.pkgver
    }
    pub fn pkgdesc(&self) -> &str {
        &self.pkginfo.pkgdesc
    }
    pub fn csize(&self) -> u64 {
        self.size
    }
    pub fn isize(&self) -> u64 {
        self.pkginfo.size
    }
    pub fn md5sum(&self) -> &str {
        &self.md5sum
    }
    pub fn sha256sum(&self) -> &str {
        &self.sha256sum
    }
    pub fn pgpsig(&self) -> &str {
        &self.pgpsig
    }
    pub fn url(&self) -> &str {
        &self.pkginfo.url
    }
    pub fn arch(&self) -> &str {
        &self.pkginfo.arch
    }
    pub fn builddate(&self) -> u64 {
        self.pkginfo.builddate
    }
    pub fn packager(&self) -> &str {
        &self.pkginfo.packager
    }

    pub fn conflicts(&self) -> &Vec<String> {
        &self.pkginfo.conflicts
    }
    pub fn provides(&self) -> &Vec<String> {
        &self.pkginfo.provides
    }
    pub fn depends(&self) -> &Vec<String> {
        &self.pkginfo.depends
    }
    pub fn makedepends(&self) -> &Vec<String> {
        &self.pkginfo.makedepends
    }
    pub fn checkdepends(&self) -> &Vec<String> {
        &self.pkginfo.checkdepends
    }
    pub fn optdepends(&self) -> &Vec<String> {
        &self.pkginfo.optdepends
    }

    pub fn files(&self) -> &Vec<String> {
        &self.files
    }
}

#[derive(Debug, Default, Clone)]
pub struct PkgInfo {
    pub pkgname: String,
    pub pkgbase: String,
    pub pkgver: String,
    pub pkgdesc: String,
    pub url: String,
    pub builddate: u64,
    pub packager: String,
    pub size: u64,
    pub arch: String,
    pub license: Vec<String>,
    pub groups: Vec<String>,
    pub depends: Vec<String>,
    pub makedepends: Vec<String>,
    pub checkdepends: Vec<String>,
    pub optdepends: Vec<String>,
    pub conflicts: Vec<String>,
    pub provides: Vec<String>,
    pub replaces: Vec<String>,
}

impl PkgInfo {
    fn load<P: AsRef<std::path::Path>>(path: P) -> Result<(Self, Vec<String>), Error> {
        let file = try!(std::fs::File::open(path));
        let xz_reader = try!(lzma::LzmaReader::new_decompressor(file));
        let mut tar_reader = tar::Archive::new(xz_reader);
        let mut pkginfo = None;
        let mut files = vec![];
        for entry_result in try!(tar_reader.entries()) {
            let mut entry = try!(entry_result);
            let path = try!(entry.path()).to_mut().to_string_lossy().into_owned();
            if path == ".PKGINFO" && entry.header().entry_type() == tar::EntryType::Regular {
                let mut body = String::new();
                try!(entry.read_to_string(&mut body));
                pkginfo = Some(try!(parse_pkginfo(&body)));
            }
            if !path.starts_with(".") {
                files.push(path);
            }
        }
        if let Some(pkginfo) = pkginfo {
            Ok((pkginfo, files))
        } else {
            Err(Error::from(".PKGINFO not found"))
        }
    }
}

fn parse_pkginfo(body: &str) -> Result<PkgInfo, Error> {
    let mut info = PkgInfo::default();
    for line in body.lines() {
        if line.starts_with('#') {
            continue;
        }
        let mut splitn = line.splitn(2, '=');
        let key = splitn.next();
        let val = splitn.next();
        let rest = splitn.next();
        if let (Some(key), Some(val), None) = (key, val, rest) {
            let key = key.trim();
            let val = val.trim();
            match key {
                "pkgname" => info.pkgname = val.to_owned(),
                "pkgbase" => info.pkgbase = val.to_owned(),
                "pkgver" => info.pkgver = val.to_owned(),
                "pkgdesc" => info.pkgdesc = val.to_owned(),
                "url" => info.url = val.to_owned(),
                "builddate" => info.builddate = try!(val.parse()),
                "packager" => info.packager = val.to_owned(),
                "size" => info.size = try!(val.parse()),
                "arch" => info.arch = val.to_owned(),
                "license" => info.license.push(val.to_owned()),
                "group" => info.groups.push(val.to_owned()),
                "depend" => info.depends.push(val.to_owned()),
                "makedepend" => info.makedepends.push(val.to_owned()),
                "checkdepend" => info.checkdepends.push(val.to_owned()),
                "optdepend" => info.optdepends.push(val.to_owned()),
                "conflict" => info.conflicts.push(val.to_owned()),
                "provides" => info.provides.push(val.to_owned()),
                "replaces" => info.replaces.push(val.to_owned()),
                _ => return Err(Error::from(format!("Unknown PKGINFO entry '{}': {}", key, line))),
            }
        } else {
            return Err(Error::from(format!("Invalid line: {}", line)));
        }
    }
    Ok(info)
}
<|endoftext|>


<commit_before>use memory::MemSegment;
use screen::Screen;
use cart::Cart;
use std::rc::Rc;
use std::cell::RefCell;
use std::default::Default;

mod ppu_reg;
use ppu::ppu_reg::*;

mod ppu_memory;
use ppu::ppu_memory::*;

mod sprite_rendering;
use ppu::sprite_rendering::*;

mod background_rendering;

pub const SCREEN_WIDTH: usize = 256;
pub const SCREEN_HEIGHT: usize = 240;
pub const SCREEN_BUFFER_SIZE: usize = SCREEN_WIDTH * SCREEN_HEIGHT;

#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)]
pub struct Color(u8);
impl Color {
    fn from_bits_truncate(val: u8) -> Color {
        Color(val & 0b0011_1111)
    }

    pub fn bits(&self) -> u8 {
        self.0
    }
}

pub struct PPU {
    reg: PPUReg,
    ppudata_read_buffer: u8,
    oam: [OAMEntry; 64],
    ppu_mem: PPUMemory,

    screen: Box<Screen>,
    screen_buffer: [Color; SCREEN_BUFFER_SIZE],

    global_cyc: u64,
    cyc: u16,
    sl: i16,
    frame: u32,
}

#[derive(Copy, Debug, PartialEq, Clone)]
pub enum StepResult {
    NMI,
    Continue,
}

impl PPU {
    pub fn new(cart: Rc<RefCell<Cart>>, screen: Box<Screen>) -> PPU {
        PPU {
            reg: Default::default(),
            ppudata_read_buffer: 0,
            oam: [OAMEntry::zero(); 64],
            ppu_mem: PPUMemory::new(cart),
            screen_buffer: [Color::from_bits_truncate(0x00); SCREEN_BUFFER_SIZE],
            screen: screen,

            global_cyc: 0,
            cyc: 0,
            sl: 241,
            frame: 0,
        }
    }

    pub fn run_to(&mut self, cpu_cycle: u64) -> StepResult {
        let mut hit_nmi = false;
        while self.global_cyc < (cpu_cycle * 3) {
            self.tick_cycle();
            hit_nmi |= self.run_cycle();
        }

        if hit_nmi {
            StepResult::NMI
        } else {
            StepResult::Continue
        }
    }

    fn tick_cycle(&mut self) {
        self.global_cyc += 1;
        self.cyc += 1;
        if self.cyc == 341 {
            self.cyc = 0;
            self.sl += 1;
            if self.sl == 261 {
                self.sl = -1;
                self.frame += 1;
            }
        }
    }

    fn run_cycle(&mut self) -> bool {
        match (self.cyc, self.sl) {
            (c, -1) => self.prerender_scanline(c),
            (c, sl @ 0...239) => self.visible_scanline(c, sl),
            (_, 240) => (), //Post-render idle scanline
            (1, 241) => return self.start_vblank(),
            (_, 241...260) => (), //VBlank lines
            _ => (),
        }
        false
    }

    fn prerender_scanline(&mut self, _: u16) {
        // Nothing here yet
    }

    fn visible_scanline(&mut self, pixel: u16, scanline: i16) {
        // Nothing here yet
        if pixel >= 256 {
            return;
        }
        let x = pixel as usize;
        let y = scanline as usize;
        self.screen_buffer[y * SCREEN_WIDTH + x] = self.get_pixel(x as u16, y as u16);
    }

    fn get_pixel(&mut self, x: u16, y: u16) -> Color {
        self.get_background_pixel(x, y)
    }

    fn start_vblank(&mut self) -> bool {
        let buf = &self.screen_buffer;
        self.screen.draw(buf);
        if self.frame > 0 {
            self.reg.ppustat.insert(VBLANK);
            self.reg.ppuctrl.generate_vblank_nmi()
        } else {
            false
        }
    }

    #[cfg(feature="cputrace")]
    pub fn cycle(&self) -> u16 {
        self.cyc
    }

    #[cfg(feature="cputrace")]
    pub fn scanline(&self) -> i16 {
        self.sl
    }

    #[cfg(feature="cputrace")]
    pub fn vram_addr(&self) -> u16 {
        self.reg.ppuaddr
    }
}

impl MemSegment for PPU {
    fn read(&mut self, idx: u16) -> u8 {
        match idx % 8 {
            0x0004 => {
                let res = self.oam[self.reg.oamaddr as usize / 4].read(self.reg.oamaddr as u16);
                self.reg.incr_oamaddr();
                res
            }
            0x0007 => {
                let addr = self.reg.ppuaddr;
                match addr {
                    0x0000...0x3EFF => {
                        let old_buffer = self.ppudata_read_buffer;
                        self.ppudata_read_buffer = self.ppu_mem.read(addr);
                        self.reg.incr_ppuaddr();
                        old_buffer
                    },
                    0x3F00...0x3FFF => {
                        let read_result = self.ppu_mem.read(addr);
                        self.reg.incr_ppuaddr();
                        self.ppudata_read_buffer = self.ppu_mem.read_bypass_palette(addr);
                        read_result
                    },
                    x => invalid_address!(x),
                }
            }
            _ => self.reg.read( idx )
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        match idx % 8 {
            0x0004 => {
                self.oam[self.reg.oamaddr as usize / 4].write(self.reg.oamaddr as u16, val);
                self.reg.incr_oamaddr();
            }
            0x0007 => {
                self.ppu_mem.write(self.reg.ppuaddr, val);
                self.reg.incr_ppuaddr();
            }
            _ => self.reg.write( idx, val )
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use mappers::{Mapper, MapperParams};
    use std::rc::Rc;
    use std::cell::RefCell;
    use cart::Cart;
    use screen::DummyScreen;
    use ppu::ppu_reg::PPUCtrl;
    use memory::MemSegment;

    pub fn create_test_ppu() -> PPU {
        create_test_ppu_with_rom(vec![0u8; 0x1000])
    }

    pub fn create_test_ppu_with_rom(chr_rom: Vec<u8>) -> PPU {
        let mapper = Mapper::new(0, MapperParams::simple(vec![0u8; 0x1000], chr_rom));
        let cart = Cart::new(mapper);
        PPU::new(Rc::new(RefCell::new(cart)), Box::new(DummyScreen::new()))
    }

    #[test]
    fn reading_oamdata_increments_oamaddr() {
        let mut ppu = create_test_ppu();
        ppu.reg.oamaddr = 0;
        ppu.read(0x2004);
        assert_eq!(ppu.reg.oamaddr, 1);
        ppu.reg.oamaddr = 255;
        ppu.read(0x2004);
        assert_eq!(ppu.reg.oamaddr, 0);
    }

    #[test]
    fn writing_oamdata_increments_oamaddr() {
        let mut ppu = create_test_ppu();
        ppu.reg.oamaddr = 0;
        ppu.write(0x2004, 12);
        assert_eq!(ppu.reg.oamaddr, 1);
        ppu.reg.oamaddr = 255;
        ppu.write(0x2004, 12);
        assert_eq!(ppu.reg.oamaddr, 0);
    }

    #[test]
    fn ppu_can_read_chr_rom() {
        let mut chr_rom = vec![0u8; 0x2000];
        chr_rom[0x0ABC] = 12;
        chr_rom[0x0DBA] = 212;
        let mut ppu = create_test_ppu_with_rom(chr_rom);

        ppu.reg.ppuaddr = 0x0ABC;
        assert_eq!(ppu.read(0x2007), 12);

        ppu.reg.ppuaddr = 0x0DBA;
        assert_eq!(ppu.read(0x2007), 212);
    }

    #[test]
    fn ppu_can_read_write_vram() {
        let mut ppu = create_test_ppu();

        ppu.reg.ppuaddr = 0x2ABC;
        ppu.write(0x2007, 12);
        ppu.reg.ppuaddr = 0x2ABC;
        assert_eq!(ppu.read(0x2007), 12);

        ppu.reg.ppuaddr = 0x2DBA;
        ppu.write(0x2007, 212);
        ppu.reg.ppuaddr = 0x2DBA;
        assert_eq!(ppu.read(0x2007), 212);

        // Mirroring
        ppu.reg.ppuaddr = 0x2EFC;
        ppu.write(0x2007, 128);
        ppu.reg.ppuaddr = 0x3EFC;
        assert_eq!(ppu.read(0x2007), 128);
    }

    #[test]
    fn accessing_ppudata_increments_ppuaddr() {
        let mut ppu = create_test_ppu();
        ppu.reg.ppuaddr = 0x2000;
        ppu.read(0x2007);
        assert_eq!(ppu.reg.ppuaddr, 0x2001);
        ppu.write(0x2007, 0);
        assert_eq!(ppu.reg.ppuaddr, 0x2002);
    }

    #[test]
    fn accessing_ppudata_increments_ppuaddr_by_32_when_ctrl_flag_is_set() {
        let mut ppu = create_test_ppu();
        ppu.reg.ppuctrl = PPUCtrl::new(0b0000_0100);
        ppu.reg.ppuaddr = 0x2000;
        ppu.read(0x2007);
        assert_eq!(ppu.reg.ppuaddr, 0x2020);
        ppu.write(0x2007, 0);
        assert_eq!(ppu.reg.ppuaddr, 0x2040);
    }
}<commit_msg>Don't increment OAMADDR on read from OAMDATA.<commit_after>use memory::MemSegment;
use screen::Screen;
use cart::Cart;
use std::rc::Rc;
use std::cell::RefCell;
use std::default::Default;

mod ppu_reg;
use ppu::ppu_reg::*;

mod ppu_memory;
use ppu::ppu_memory::*;

mod sprite_rendering;
use ppu::sprite_rendering::*;

mod background_rendering;

pub const SCREEN_WIDTH: usize = 256;
pub const SCREEN_HEIGHT: usize = 240;
pub const SCREEN_BUFFER_SIZE: usize = SCREEN_WIDTH * SCREEN_HEIGHT;

#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)]
pub struct Color(u8);
impl Color {
    fn from_bits_truncate(val: u8) -> Color {
        Color(val & 0b0011_1111)
    }

    pub fn bits(&self) -> u8 {
        self.0
    }
}

pub struct PPU {
    reg: PPUReg,
    ppudata_read_buffer: u8,
    oam: [OAMEntry; 64],
    ppu_mem: PPUMemory,

    screen: Box<Screen>,
    screen_buffer: [Color; SCREEN_BUFFER_SIZE],

    global_cyc: u64,
    cyc: u16,
    sl: i16,
    frame: u32,
}

#[derive(Copy, Debug, PartialEq, Clone)]
pub enum StepResult {
    NMI,
    Continue,
}

impl PPU {
    pub fn new(cart: Rc<RefCell<Cart>>, screen: Box<Screen>) -> PPU {
        PPU {
            reg: Default::default(),
            ppudata_read_buffer: 0,
            oam: [OAMEntry::zero(); 64],
            ppu_mem: PPUMemory::new(cart),
            screen_buffer: [Color::from_bits_truncate(0x00); SCREEN_BUFFER_SIZE],
            screen: screen,

            global_cyc: 0,
            cyc: 0,
            sl: 241,
            frame: 0,
        }
    }

    pub fn run_to(&mut self, cpu_cycle: u64) -> StepResult {
        let mut hit_nmi = false;
        while self.global_cyc < (cpu_cycle * 3) {
            self.tick_cycle();
            hit_nmi |= self.run_cycle();
        }

        if hit_nmi {
            StepResult::NMI
        } else {
            StepResult::Continue
        }
    }

    fn tick_cycle(&mut self) {
        self.global_cyc += 1;
        self.cyc += 1;
        if self.cyc == 341 {
            self.cyc = 0;
            self.sl += 1;
            if self.sl == 261 {
                self.sl = -1;
                self.frame += 1;
            }
        }
    }

    fn run_cycle(&mut self) -> bool {
        match (self.cyc, self.sl) {
            (c, -1) => self.prerender_scanline(c),
            (c, sl @ 0...239) => self.visible_scanline(c, sl),
            (_, 240) => (), //Post-render idle scanline
            (1, 241) => return self.start_vblank(),
            (_, 241...260) => (), //VBlank lines
            _ => (),
        }
        false
    }

    fn prerender_scanline(&mut self, _: u16) {
        // Nothing here yet
    }

    fn visible_scanline(&mut self, pixel: u16, scanline: i16) {
        // Nothing here yet
        if pixel >= 256 {
            return;
        }
        let x = pixel as usize;
        let y = scanline as usize;
        self.screen_buffer[y * SCREEN_WIDTH + x] = self.get_pixel(x as u16, y as u16);
    }

    fn get_pixel(&mut self, x: u16, y: u16) -> Color {
        self.get_background_pixel(x, y)
    }

    fn start_vblank(&mut self) -> bool {
        let buf = &self.screen_buffer;
        self.screen.draw(buf);
        if self.frame > 0 {
            self.reg.ppustat.insert(VBLANK);
            self.reg.ppuctrl.generate_vblank_nmi()
        } else {
            false
        }
    }

    #[cfg(feature="cputrace")]
    pub fn cycle(&self) -> u16 {
        self.cyc
    }

    #[cfg(feature="cputrace")]
    pub fn scanline(&self) -> i16 {
        self.sl
    }

    #[cfg(feature="cputrace")]
    pub fn vram_addr(&self) -> u16 {
        self.reg.ppuaddr
    }
}

impl MemSegment for PPU {
    fn read(&mut self, idx: u16) -> u8 {
        match idx % 8 {
            0x0004 => self.oam[self.reg.oamaddr as usize / 4].read(self.reg.oamaddr as u16),
            0x0007 => {
                let addr = self.reg.ppuaddr;
                match addr {
                    0x0000...0x3EFF => {
                        let old_buffer = self.ppudata_read_buffer;
                        self.ppudata_read_buffer = self.ppu_mem.read(addr);
                        self.reg.incr_ppuaddr();
                        old_buffer
                    },
                    0x3F00...0x3FFF => {
                        let read_result = self.ppu_mem.read(addr);
                        self.reg.incr_ppuaddr();
                        self.ppudata_read_buffer = self.ppu_mem.read_bypass_palette(addr);
                        read_result
                    },
                    x => invalid_address!(x),
                }
            }
            _ => self.reg.read( idx )
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        match idx % 8 {
            0x0004 => {
                self.oam[self.reg.oamaddr as usize / 4].write(self.reg.oamaddr as u16, val);
                self.reg.incr_oamaddr();
            }
            0x0007 => {
                self.ppu_mem.write(self.reg.ppuaddr, val);
                self.reg.incr_ppuaddr();
            }
            _ => self.reg.write( idx, val )
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use mappers::{Mapper, MapperParams};
    use std::rc::Rc;
    use std::cell::RefCell;
    use cart::Cart;
    use screen::DummyScreen;
    use ppu::ppu_reg::PPUCtrl;
    use memory::MemSegment;

    pub fn create_test_ppu() -> PPU {
        create_test_ppu_with_rom(vec![0u8; 0x1000])
    }

    pub fn create_test_ppu_with_rom(chr_rom: Vec<u8>) -> PPU {
        let mapper = Mapper::new(0, MapperParams::simple(vec![0u8; 0x1000], chr_rom));
        let cart = Cart::new(mapper);
        PPU::new(Rc::new(RefCell::new(cart)), Box::new(DummyScreen::new()))
    }

    #[test]
    fn reading_oamdata_increments_oamaddr() {
        let mut ppu = create_test_ppu();
        ppu.reg.oamaddr = 0;
        ppu.read(0x2004);
        assert_eq!(ppu.reg.oamaddr, 1);
        ppu.reg.oamaddr = 255;
        ppu.read(0x2004);
        assert_eq!(ppu.reg.oamaddr, 0);
    }

    #[test]
    fn writing_oamdata_increments_oamaddr() {
        let mut ppu = create_test_ppu();
        ppu.reg.oamaddr = 0;
        ppu.write(0x2004, 12);
        assert_eq!(ppu.reg.oamaddr, 1);
        ppu.reg.oamaddr = 255;
        ppu.write(0x2004, 12);
        assert_eq!(ppu.reg.oamaddr, 0);
    }

    #[test]
    fn ppu_can_read_chr_rom() {
        let mut chr_rom = vec![0u8; 0x2000];
        chr_rom[0x0ABC] = 12;
        chr_rom[0x0DBA] = 212;
        let mut ppu = create_test_ppu_with_rom(chr_rom);

        ppu.reg.ppuaddr = 0x0ABC;
        assert_eq!(ppu.read(0x2007), 12);

        ppu.reg.ppuaddr = 0x0DBA;
        assert_eq!(ppu.read(0x2007), 212);
    }

    #[test]
    fn ppu_can_read_write_vram() {
        let mut ppu = create_test_ppu();

        ppu.reg.ppuaddr = 0x2ABC;
        ppu.write(0x2007, 12);
        ppu.reg.ppuaddr = 0x2ABC;
        assert_eq!(ppu.read(0x2007), 12);

        ppu.reg.ppuaddr = 0x2DBA;
        ppu.write(0x2007, 212);
        ppu.reg.ppuaddr = 0x2DBA;
        assert_eq!(ppu.read(0x2007), 212);

        // Mirroring
        ppu.reg.ppuaddr = 0x2EFC;
        ppu.write(0x2007, 128);
        ppu.reg.ppuaddr = 0x3EFC;
        assert_eq!(ppu.read(0x2007), 128);
    }

    #[test]
    fn accessing_ppudata_increments_ppuaddr() {
        let mut ppu = create_test_ppu();
        ppu.reg.ppuaddr = 0x2000;
        ppu.read(0x2007);
        assert_eq!(ppu.reg.ppuaddr, 0x2001);
        ppu.write(0x2007, 0);
        assert_eq!(ppu.reg.ppuaddr, 0x2002);
    }

    #[test]
    fn accessing_ppudata_increments_ppuaddr_by_32_when_ctrl_flag_is_set() {
        let mut ppu = create_test_ppu();
        ppu.reg.ppuctrl = PPUCtrl::new(0b0000_0100);
        ppu.reg.ppuaddr = 0x2000;
        ppu.read(0x2007);
        assert_eq!(ppu.reg.ppuaddr, 0x2020);
        ppu.write(0x2007, 0);
        assert_eq!(ppu.reg.ppuaddr, 0x2040);
    }
}<|endoftext|>


<commit_before>use preferences::{Preferences, Pref};
use prefreader::{parse, serialize, PrefReaderError};
use std::collections::btree_map::Iter;
use std::fs::File;
use std::io::Result as IoResult;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use tempdir::TempDir;

pub struct Profile {
    pub path: PathBuf,
    pub temp_dir: Option<TempDir>,
    prefs: Option<PrefFile>,
    user_prefs: Option<PrefFile>
}

impl Profile {
    pub fn new(opt_path: Option<&Path>) -> IoResult<Profile> {
        let mut temp_dir = None;
        let path = match opt_path {
            Some(p) => p.to_path_buf(),
            None => {
                let dir = try!(TempDir::new("rust_mozprofile"));
                let temp_path = dir.path().to_path_buf();
                temp_dir = Some(dir);
                temp_path
            }
        };

        info!("Using profile path {}", path.to_str().unwrap());

        Ok(Profile {
            path: path,
            temp_dir: temp_dir,
            prefs: None,
            user_prefs: None
        })
    }

    pub fn prefs(&mut self) -> Result<&mut PrefFile, PrefReaderError> {
        if self.prefs.is_none() {
            let mut pref_path = PathBuf::from(&self.path);
            pref_path.push("prefs.js");
            self.prefs = Some(try!(PrefFile::new(pref_path)))
        };
        // This error handling doesn't make much sense
        Ok(self.prefs.as_mut().unwrap())
    }

    pub fn user_prefs(&mut self) -> Result<&mut PrefFile, PrefReaderError> {
        if self.user_prefs.is_none() {
            let mut pref_path = PathBuf::from(&self.path);
            pref_path.push("user.js");
            self.user_prefs = Some(try!(PrefFile::new(pref_path)))
        };
        // This error handling doesn't make much sense
        Ok(self.user_prefs.as_mut().unwrap())
    }
}

pub struct PrefFile {
    path: PathBuf,
    pub prefs: Preferences
}

impl PrefFile {
    pub fn new(path: PathBuf) -> Result<PrefFile, PrefReaderError> {
        let prefs = if !path.exists() {
            Preferences::new()
        } else {
            let mut f = try!(File::open(&path));
            let mut buf = String::with_capacity(4096);
            try!(f.read_to_string(&mut buf));
            try!(parse(buf.as_bytes()))
        };

        Ok(PrefFile {
            path: path,
            prefs: prefs
        })
    }

    pub fn write(&self) -> IoResult<()> {
        let mut f = try!(File::create(&self.path));
        serialize(&self.prefs, &mut f)
    }

    pub fn insert_slice<K>(&mut self, preferences: &[(K, Pref)])
        where K: Into<String> + Clone {
        for &(ref name, ref value) in preferences.iter() {
            self.insert((*name).clone(), (*value).clone());
        }
    }

    pub fn insert<K>(&mut self, key: K, value: Pref)
        where K: Into<String> {
        self.prefs.insert(key.into(), value);
    }

    pub fn remove(&mut self, key: &str) -> Option<Pref> {
        self.prefs.remove(key)
    }

    pub fn get(&mut self, key: &str) -> Option<&Pref> {
        self.prefs.get(key)
    }

    pub fn contains_key(&self, key: &str) -> bool {
        self.prefs.contains_key(key)
    }

    pub fn iter(&self) -> Iter<String, Pref> {
        self.prefs.iter()
    }
}
<commit_msg>profile: provide Debug trait for Profile and PrefFile<commit_after>use preferences::{Preferences, Pref};
use prefreader::{parse, serialize, PrefReaderError};
use std::collections::btree_map::Iter;
use std::fs::File;
use std::io::Result as IoResult;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use tempdir::TempDir;

#[derive(Debug)]
pub struct Profile {
    pub path: PathBuf,
    pub temp_dir: Option<TempDir>,
    prefs: Option<PrefFile>,
    user_prefs: Option<PrefFile>,
}

impl Profile {
    pub fn new(opt_path: Option<&Path>) -> IoResult<Profile> {
        let mut temp_dir = None;
        let path = match opt_path {
            Some(p) => p.to_path_buf(),
            None => {
                let dir = try!(TempDir::new("rust_mozprofile"));
                let temp_path = dir.path().to_path_buf();
                temp_dir = Some(dir);
                temp_path
            }
        };

        info!("Using profile path {}", path.to_str().unwrap());

        Ok(Profile {
            path: path,
            temp_dir: temp_dir,
            prefs: None,
            user_prefs: None
        })
    }

    pub fn prefs(&mut self) -> Result<&mut PrefFile, PrefReaderError> {
        if self.prefs.is_none() {
            let mut pref_path = PathBuf::from(&self.path);
            pref_path.push("prefs.js");
            self.prefs = Some(try!(PrefFile::new(pref_path)))
        };
        // This error handling doesn't make much sense
        Ok(self.prefs.as_mut().unwrap())
    }

    pub fn user_prefs(&mut self) -> Result<&mut PrefFile, PrefReaderError> {
        if self.user_prefs.is_none() {
            let mut pref_path = PathBuf::from(&self.path);
            pref_path.push("user.js");
            self.user_prefs = Some(try!(PrefFile::new(pref_path)))
        };
        // This error handling doesn't make much sense
        Ok(self.user_prefs.as_mut().unwrap())
    }
}

#[derive(Debug)]
pub struct PrefFile {
    path: PathBuf,
    pub prefs: Preferences,
}

impl PrefFile {
    pub fn new(path: PathBuf) -> Result<PrefFile, PrefReaderError> {
        let prefs = if !path.exists() {
            Preferences::new()
        } else {
            let mut f = try!(File::open(&path));
            let mut buf = String::with_capacity(4096);
            try!(f.read_to_string(&mut buf));
            try!(parse(buf.as_bytes()))
        };

        Ok(PrefFile {
            path: path,
            prefs: prefs
        })
    }

    pub fn write(&self) -> IoResult<()> {
        let mut f = try!(File::create(&self.path));
        serialize(&self.prefs, &mut f)
    }

    pub fn insert_slice<K>(&mut self, preferences: &[(K, Pref)])
        where K: Into<String> + Clone {
        for &(ref name, ref value) in preferences.iter() {
            self.insert((*name).clone(), (*value).clone());
        }
    }

    pub fn insert<K>(&mut self, key: K, value: Pref)
        where K: Into<String> {
        self.prefs.insert(key.into(), value);
    }

    pub fn remove(&mut self, key: &str) -> Option<Pref> {
        self.prefs.remove(key)
    }

    pub fn get(&mut self, key: &str) -> Option<&Pref> {
        self.prefs.get(key)
    }

    pub fn contains_key(&self, key: &str) -> bool {
        self.prefs.contains_key(key)
    }

    pub fn iter(&self) -> Iter<String, Pref> {
        self.prefs.iter()
    }
}
<|endoftext|>


<commit_before>use gl;
use std::{fmt, mem, ptr};
use std::collections::HashMap;
use std::sync::Arc;
use {Display, DisplayImpl};

struct Shader {
    display: Arc<DisplayImpl>,
    id: gl::types::GLuint,
}

impl Drop for Shader {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(proc(gl, _state) {
            gl.DeleteShader(id);
        });
    }
}

/// A combinaison of shaders linked together.
pub struct Program {
    display: Arc<DisplayImpl>,
    #[allow(dead_code)]
    shaders: Vec<Arc<Shader>>,
    id: gl::types::GLuint,
    uniforms: Arc<HashMap<String, (gl::types::GLint, gl::types::GLenum, gl::types::GLint)>>     // location, type and size of each uniform, ordered by name
}

/// Error that can be triggered when creating a `Program`.
#[deriving(Clone, Show)]
pub enum ProgramCreationError {
    /// Error while compiling one of the shaders.
    CompilationError(String),

    /// Error while linking the program.
    LinkingError(String),

    /// `glCreateProgram` failed.
    ProgramCreationFailure,

    /// One of the request shader type is not supported by the backend.
    ///
    /// Usually the case of geometry shaders.
    ShaderTypeNotSupported,
}

impl Program {
    /// Builds a new program.
    ///
    /// A program is a group of shaders linked together.
    ///
    /// # Parameters
    ///
    /// - `vertex_shader`: Source code of the vertex shader.
    /// - `fragment_shader`: Source code of the fragment shader.
    /// - `geometry_shader`: Source code of the geometry shader.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # let display: glium::Display = unsafe { std::mem::uninitialized() };
    /// # let vertex_source = ""; let fragment_source = ""; let geometry_source = "";
    /// let program = glium::Program::new(&display, vertex_source, fragment_source, Some(geometry_source));
    /// ```
    ///
    #[experimental = "The list of shaders and the result error will probably change"]
    pub fn new(display: &Display, vertex_shader: &str, fragment_shader: &str,
               geometry_shader: Option<&str>) -> Result<Program, ProgramCreationError>
    {
        let mut shaders_store = Vec::new();
        shaders_store.push(try!(build_shader(display, gl::VERTEX_SHADER, vertex_shader)));
        match geometry_shader {
            Some(gs) => shaders_store.push(try!(build_geometry_shader(display, gs))),
            None => ()
        }
        shaders_store.push(try!(build_shader(display, gl::FRAGMENT_SHADER, fragment_shader)));

        let mut shaders_ids = Vec::new();
        for sh in shaders_store.iter() {
            shaders_ids.push(sh.id);
        }

        let (tx, rx) = channel();
        display.context.context.exec(proc(gl, _state) {
            unsafe {
                let id = gl.CreateProgram();
                if id == 0 {
                    tx.send(Err(ProgramCreationFailure));
                    return;
                }

                // attaching shaders
                for sh in shaders_ids.iter() {
                    gl.AttachShader(id, sh.clone());
                }

                // linking and checking for errors
                gl.LinkProgram(id);
                {   let mut link_success: gl::types::GLint = mem::uninitialized();
                    gl.GetProgramiv(id, gl::LINK_STATUS, &mut link_success);
                    if link_success == 0 {
                        match gl.GetError() {
                            gl::NO_ERROR => (),
                            gl::INVALID_VALUE => {
                                tx.send(Err(LinkingError(format!("glLinkProgram triggered GL_INVALID_VALUE"))));
                                return;
                            },
                            gl::INVALID_OPERATION => {
                                tx.send(Err(LinkingError(format!("glLinkProgram triggered GL_INVALID_OPERATION"))));
                                return;
                            },
                            _ => {
                                tx.send(Err(LinkingError(format!("glLinkProgram triggered an unknown error"))));
                                return;
                            }
                        };

                        let mut error_log_size: gl::types::GLint = mem::uninitialized();
                        gl.GetProgramiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);

                        let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as uint);
                        gl.GetProgramInfoLog(id, error_log_size, &mut error_log_size, error_log.as_mut_slice().as_mut_ptr() as *mut gl::types::GLchar);
                        error_log.set_len(error_log_size as uint);

                        let msg = String::from_utf8(error_log).unwrap();
                        tx.send(Err(LinkingError(msg)));
                        return;
                    }
                }

                tx.send(Ok(id));
            }
        });

        let id = try!(rx.recv());

        let (tx, rx) = channel();
        display.context.context.exec(proc(gl, _state) {
            unsafe {
                // reflecting program uniforms
                let mut uniforms = HashMap::new();

                let mut active_uniforms: gl::types::GLint = mem::uninitialized();
                gl.GetProgramiv(id, gl::ACTIVE_UNIFORMS, &mut active_uniforms);

                for uniform_id in range(0, active_uniforms) {
                    let mut uniform_name_tmp: Vec<u8> = Vec::with_capacity(64);
                    let mut uniform_name_tmp_len = 63;

                    let mut data_type: gl::types::GLenum = mem::uninitialized();
                    let mut data_size: gl::types::GLint = mem::uninitialized();
                    gl.GetActiveUniform(id, uniform_id as gl::types::GLuint, uniform_name_tmp_len, &mut uniform_name_tmp_len, &mut data_size, &mut data_type, uniform_name_tmp.as_mut_slice().as_mut_ptr() as *mut gl::types::GLchar);
                    uniform_name_tmp.set_len(uniform_name_tmp_len as uint);

                    let uniform_name = String::from_utf8(uniform_name_tmp).unwrap();
                    let location = gl.GetUniformLocation(id, uniform_name.to_c_str().unwrap());

                    uniforms.insert(uniform_name, (location, data_type, data_size));
                }

                tx.send(Arc::new(uniforms));
            }
        });

        Ok(Program {
            display: display.context.clone(),
            shaders: shaders_store,
            id: id,
            uniforms: rx.recv(),
        })
    }
}

impl fmt::Show for Program {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::FormatError> {
        (format!("Program #{}", self.id)).fmt(formatter)
    }
}

pub fn get_program_id(program: &Program) -> gl::types::GLuint {
    program.id
}

pub fn get_uniforms_locations(program: &Program) -> Arc<HashMap<String, (gl::types::GLint, gl::types::GLenum, gl::types::GLint)>> {
    program.uniforms.clone()
}

impl Drop for Program {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(proc(gl, state) {
            if state.program == id {
                gl.UseProgram(0);
                state.program = 0;
            }

            gl.DeleteProgram(id);
        });
    }
}

/// Builds an individual shader.
fn build_shader<S: ToCStr>(display: &Display, shader_type: gl::types::GLenum, source_code: S)
    -> Result<Arc<Shader>, ProgramCreationError>
{
    let source_code = source_code.to_c_str();

    let (tx, rx) = channel();
    display.context.context.exec(proc(gl, _state) {
        unsafe {
            let id = gl.CreateShader(shader_type);

            if id == 0 {
                tx.send(Err(ShaderTypeNotSupported));
                return;
            }

            gl.ShaderSource(id, 1, [ source_code.as_ptr() ].as_ptr(), ptr::null());
            gl.CompileShader(id);

            // checking compilation success
            let compilation_success = {
                let mut compilation_success: gl::types::GLint = mem::uninitialized();
                gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut compilation_success);
                compilation_success
            };

            if compilation_success == 0 {
                // compilation error
                let mut error_log_size: gl::types::GLint = mem::uninitialized();
                gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);

                let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as uint);
                gl.GetShaderInfoLog(id, error_log_size, &mut error_log_size, error_log.as_mut_slice().as_mut_ptr() as *mut gl::types::GLchar);
                error_log.set_len(error_log_size as uint);

                let msg = String::from_utf8(error_log).unwrap();
                tx.send(Err(CompilationError(msg)));
                return;
            }

            tx.send(Ok(id));
        }
    });

    rx.recv().map(|id| {
        Arc::new(Shader {
            display: display.context.clone(),
            id: id
        })
    })
}

#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
fn build_geometry_shader<S: ToCStr>(display: &Display, source_code: S)
    -> Result<Arc<Shader>, ProgramCreationError>
{
    build_shader(display, gl::GEOMETRY_SHADER, source_code)
}

#[cfg(target_os = "android")]
fn build_geometry_shader<S: ToCStr>(display: &Display, source_code: S)
    -> Result<Arc<Shader>, ProgramCreationError>
{
    Err(ShaderTypeNotSupported)
}
<commit_msg>No longer use Arcs for Shaders<commit_after>use gl;
use std::{fmt, mem, ptr};
use std::collections::HashMap;
use std::sync::Arc;
use {Display, DisplayImpl};

struct Shader {
    display: Arc<DisplayImpl>,
    id: gl::types::GLuint,
}

impl Drop for Shader {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(proc(gl, _state) {
            gl.DeleteShader(id);
        });
    }
}

/// A combinaison of shaders linked together.
pub struct Program {
    display: Arc<DisplayImpl>,
    #[allow(dead_code)]
    shaders: Vec<Shader>,
    id: gl::types::GLuint,
    uniforms: Arc<HashMap<String, (gl::types::GLint, gl::types::GLenum, gl::types::GLint)>>     // location, type and size of each uniform, ordered by name
}

/// Error that can be triggered when creating a `Program`.
#[deriving(Clone, Show)]
pub enum ProgramCreationError {
    /// Error while compiling one of the shaders.
    CompilationError(String),

    /// Error while linking the program.
    LinkingError(String),

    /// `glCreateProgram` failed.
    ProgramCreationFailure,

    /// One of the request shader type is not supported by the backend.
    ///
    /// Usually the case of geometry shaders.
    ShaderTypeNotSupported,
}

impl Program {
    /// Builds a new program.
    ///
    /// A program is a group of shaders linked together.
    ///
    /// # Parameters
    ///
    /// - `vertex_shader`: Source code of the vertex shader.
    /// - `fragment_shader`: Source code of the fragment shader.
    /// - `geometry_shader`: Source code of the geometry shader.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # let display: glium::Display = unsafe { std::mem::uninitialized() };
    /// # let vertex_source = ""; let fragment_source = ""; let geometry_source = "";
    /// let program = glium::Program::new(&display, vertex_source, fragment_source, Some(geometry_source));
    /// ```
    ///
    #[experimental = "The list of shaders and the result error will probably change"]
    pub fn new(display: &Display, vertex_shader: &str, fragment_shader: &str,
               geometry_shader: Option<&str>) -> Result<Program, ProgramCreationError>
    {
        let mut shaders_store = Vec::new();
        shaders_store.push(try!(build_shader(display, gl::VERTEX_SHADER, vertex_shader)));
        match geometry_shader {
            Some(gs) => shaders_store.push(try!(build_geometry_shader(display, gs))),
            None => ()
        }
        shaders_store.push(try!(build_shader(display, gl::FRAGMENT_SHADER, fragment_shader)));

        let mut shaders_ids = Vec::new();
        for sh in shaders_store.iter() {
            shaders_ids.push(sh.id);
        }

        let (tx, rx) = channel();
        display.context.context.exec(proc(gl, _state) {
            unsafe {
                let id = gl.CreateProgram();
                if id == 0 {
                    tx.send(Err(ProgramCreationFailure));
                    return;
                }

                // attaching shaders
                for sh in shaders_ids.iter() {
                    gl.AttachShader(id, sh.clone());
                }

                // linking and checking for errors
                gl.LinkProgram(id);
                {   let mut link_success: gl::types::GLint = mem::uninitialized();
                    gl.GetProgramiv(id, gl::LINK_STATUS, &mut link_success);
                    if link_success == 0 {
                        match gl.GetError() {
                            gl::NO_ERROR => (),
                            gl::INVALID_VALUE => {
                                tx.send(Err(LinkingError(format!("glLinkProgram triggered GL_INVALID_VALUE"))));
                                return;
                            },
                            gl::INVALID_OPERATION => {
                                tx.send(Err(LinkingError(format!("glLinkProgram triggered GL_INVALID_OPERATION"))));
                                return;
                            },
                            _ => {
                                tx.send(Err(LinkingError(format!("glLinkProgram triggered an unknown error"))));
                                return;
                            }
                        };

                        let mut error_log_size: gl::types::GLint = mem::uninitialized();
                        gl.GetProgramiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);

                        let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as uint);
                        gl.GetProgramInfoLog(id, error_log_size, &mut error_log_size, error_log.as_mut_slice().as_mut_ptr() as *mut gl::types::GLchar);
                        error_log.set_len(error_log_size as uint);

                        let msg = String::from_utf8(error_log).unwrap();
                        tx.send(Err(LinkingError(msg)));
                        return;
                    }
                }

                tx.send(Ok(id));
            }
        });

        let id = try!(rx.recv());

        let (tx, rx) = channel();
        display.context.context.exec(proc(gl, _state) {
            unsafe {
                // reflecting program uniforms
                let mut uniforms = HashMap::new();

                let mut active_uniforms: gl::types::GLint = mem::uninitialized();
                gl.GetProgramiv(id, gl::ACTIVE_UNIFORMS, &mut active_uniforms);

                for uniform_id in range(0, active_uniforms) {
                    let mut uniform_name_tmp: Vec<u8> = Vec::with_capacity(64);
                    let mut uniform_name_tmp_len = 63;

                    let mut data_type: gl::types::GLenum = mem::uninitialized();
                    let mut data_size: gl::types::GLint = mem::uninitialized();
                    gl.GetActiveUniform(id, uniform_id as gl::types::GLuint, uniform_name_tmp_len, &mut uniform_name_tmp_len, &mut data_size, &mut data_type, uniform_name_tmp.as_mut_slice().as_mut_ptr() as *mut gl::types::GLchar);
                    uniform_name_tmp.set_len(uniform_name_tmp_len as uint);

                    let uniform_name = String::from_utf8(uniform_name_tmp).unwrap();
                    let location = gl.GetUniformLocation(id, uniform_name.to_c_str().unwrap());

                    uniforms.insert(uniform_name, (location, data_type, data_size));
                }

                tx.send(Arc::new(uniforms));
            }
        });

        Ok(Program {
            display: display.context.clone(),
            shaders: shaders_store,
            id: id,
            uniforms: rx.recv(),
        })
    }
}

impl fmt::Show for Program {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::FormatError> {
        (format!("Program #{}", self.id)).fmt(formatter)
    }
}

pub fn get_program_id(program: &Program) -> gl::types::GLuint {
    program.id
}

pub fn get_uniforms_locations(program: &Program) -> Arc<HashMap<String, (gl::types::GLint, gl::types::GLenum, gl::types::GLint)>> {
    program.uniforms.clone()
}

impl Drop for Program {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(proc(gl, state) {
            if state.program == id {
                gl.UseProgram(0);
                state.program = 0;
            }

            gl.DeleteProgram(id);
        });
    }
}

/// Builds an individual shader.
fn build_shader<S: ToCStr>(display: &Display, shader_type: gl::types::GLenum, source_code: S)
    -> Result<Shader, ProgramCreationError>
{
    let source_code = source_code.to_c_str();

    let (tx, rx) = channel();
    display.context.context.exec(proc(gl, _state) {
        unsafe {
            let id = gl.CreateShader(shader_type);

            if id == 0 {
                tx.send(Err(ShaderTypeNotSupported));
                return;
            }

            gl.ShaderSource(id, 1, [ source_code.as_ptr() ].as_ptr(), ptr::null());
            gl.CompileShader(id);

            // checking compilation success
            let compilation_success = {
                let mut compilation_success: gl::types::GLint = mem::uninitialized();
                gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut compilation_success);
                compilation_success
            };

            if compilation_success == 0 {
                // compilation error
                let mut error_log_size: gl::types::GLint = mem::uninitialized();
                gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);

                let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as uint);
                gl.GetShaderInfoLog(id, error_log_size, &mut error_log_size, error_log.as_mut_slice().as_mut_ptr() as *mut gl::types::GLchar);
                error_log.set_len(error_log_size as uint);

                let msg = String::from_utf8(error_log).unwrap();
                tx.send(Err(CompilationError(msg)));
                return;
            }

            tx.send(Ok(id));
        }
    });

    rx.recv().map(|id| {
        Shader {
            display: display.context.clone(),
            id: id
        }
    })
}

#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
fn build_geometry_shader<S: ToCStr>(display: &Display, source_code: S)
    -> Result<Shader, ProgramCreationError>
{
    build_shader(display, gl::GEOMETRY_SHADER, source_code)
}

#[cfg(target_os = "android")]
fn build_geometry_shader<S: ToCStr>(display: &Display, source_code: S)
    -> Result<Shader, ProgramCreationError>
{
    Err(ShaderTypeNotSupported)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016
// Jeff Nettleton
//
// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
// file may not be copied, modified, or distributed except according to those
// terms

use std::collections::HashMap;
use rustc_serialize::{json, Decodable};

/// This enum represents the various types of HTTP requests.
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

/// A trait that allows for extracting variables from URIs.
pub trait FromUri {
    /// A function to parse a string into the correct type.
    fn from_uri(data: &str) -> Self;
}

impl FromUri for String {
    fn from_uri(data: &str) -> String {
        String::from(data)
    }
}

impl FromUri for i32 {
    fn from_uri(data: &str) -> i32 {
        match data.parse::<i32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for u32 {
    fn from_uri(data: &str) -> u32 {
        match data.parse::<u32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for f32 {
    fn from_uri(data: &str) -> f32 {
        match data.parse::<f32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched float can't be parsed: {:?}", e),
        }
    }
}

/// This struct represents a request from an HTTP client.
#[derive(Debug)]
pub struct Request {
    pub method:  Method,
    pub path:    String,
    pub payload: Vec<u8>,
    pub params:  HashMap<String, String>,
    headers:     HashMap<String, String>,
}

impl Request {
    /// Create a new, empty Request.
    pub fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            params:  HashMap::new(),
            payload: Vec::with_capacity(2048),
        }
    }

    /// Create a Request from an HTTP request string.
    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    /// Get a variable from the URI.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the route "/hello/<str:name>"
    /// fn handler(req: &Request) -> Response {
    ///     let name: String = req.get("name");
    ///     utils::make_response(format!("<b>Hello, {}!</b>", name), "text/html", 200)
    /// }
    /// ```
    pub fn get<T: FromUri>(&self, name: &str) -> T {
        if !self.params.contains_key(name) {
            panic!("invalid route parameter {:?}", name);
        }

        FromUri::from_uri(&self.params[name])
    }

    /// Get a raw JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data = req.get_json();
    ///
    ///     match data {
    ///         Some(val) => utils::make_response(format!("We got: {}", val), "text/plain", 200),
    ///         None      => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json(&self) -> Option<json::Json> {
        match String::from_utf8(self.payload.clone()) {
            Err(_)      => None,
            Ok(payload) => {
                match json::Json::from_str(&payload) {
                    Ok(data)    => Some(data),
                    Err(_)      => None,
                }
            }
        }
    }

    /// Get a composed JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// use canteen::{Request, Response};
    ///
    /// #[derive(RustcDecodable)]
    /// struct Foo {
    ///     item: i32,
    /// }
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data: Foo = req.get_json_obj();
    ///
    ///     match data {
    ///         Ok(foo) => utils::make_response(format!("We got: {}!", data.item), "text/plain", 200),
    ///         Err(_)  => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json_obj<T: Decodable>(&self) -> Result<T, json::DecoderError> {
        let data = String::from_utf8(self.payload.clone()).unwrap();
        json::decode(&data)
    }

    fn parse(&mut self, rqstr: &str) {
        let mut buf: Vec<&str> = rqstr.splitn(2, "\r\n").collect();
        let ask: Vec<&str> = buf[0].splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);

        loop {
            buf = buf[1].splitn(2, "\r\n").collect();

            if buf[0] == "" {
                if buf.len() == 1 || buf[1] == "" {
                    // no payload
                    break;
                }

                self.payload.extend(buf[1].as_bytes());
                break;
            }

            let hdr: Vec<&str> = buf[0].splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

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

    #[derive(RustcDecodable)]
    struct Foo {
        item: i32,
    }

    #[test]
    fn test_fromuri_trait_i32() {
        let pos = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&pos));

        let neg = String::from("-4321");
        assert_eq!(-4321, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_fromuri_trait_u32() {
        let orig = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_string() {
        let orig = String::from("foobar");
        assert_eq!("foobar", <String as FromUri>::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_float() {
        let pos = String::from("123.45");
        assert_eq!(123.45f32, FromUri::from_uri(&pos));

        let neg = String::from("-54.321");
        assert_eq!(-54.321f32, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_get_fromuri_i32() {
        let mut req = Request::new();
        req.params.insert(String::from("test"), String::from("1234"));

        assert_eq!(1234, req.get("test"));
    }

    #[test]
    fn test_get_json() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data = req.get_json().unwrap();

        assert_eq!(true, data.is_object());

        let obj = data.as_object().unwrap();
        let val = obj.get("item").unwrap();

        assert_eq!(true, val.is_u64());
        assert_eq!(123u64, val.as_u64().unwrap());
    }

    #[test]
    fn test_get_json_obj() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data: Foo = req.get_json_obj().unwrap();

        assert_eq!(123, data.item);
    }
}
<commit_msg>add a function I forgot I'd missed to get HTTP headers from the request<commit_after>// Copyright (c) 2016
// Jeff Nettleton
//
// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
// file may not be copied, modified, or distributed except according to those
// terms

use std::collections::HashMap;
use rustc_serialize::{json, Decodable};

/// This enum represents the various types of HTTP requests.
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

/// A trait that allows for extracting variables from URIs.
pub trait FromUri {
    /// A function to parse a string into the correct type.
    fn from_uri(data: &str) -> Self;
}

impl FromUri for String {
    fn from_uri(data: &str) -> String {
        String::from(data)
    }
}

impl FromUri for i32 {
    fn from_uri(data: &str) -> i32 {
        match data.parse::<i32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for u32 {
    fn from_uri(data: &str) -> u32 {
        match data.parse::<u32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for f32 {
    fn from_uri(data: &str) -> f32 {
        match data.parse::<f32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched float can't be parsed: {:?}", e),
        }
    }
}

/// This struct represents a request from an HTTP client.
#[derive(Debug)]
pub struct Request {
    pub method:  Method,
    pub path:    String,
    pub payload: Vec<u8>,
    pub params:  HashMap<String, String>,
    headers:     HashMap<String, String>,
}

impl Request {
    /// Create a new, empty Request.
    pub fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            params:  HashMap::new(),
            payload: Vec::with_capacity(2048),
        }
    }

    /// Create a Request from an HTTP request string.
    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    /// Get an HTTP header contained in the Request.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let browser = req.get_header("User-Agent");
    ///
    ///     match browser {
    ///         Some(ua) => utils::make_response(format!("You're using {}!", ua), "text/plain", 200),
    ///         None     => utils::make_response("Bad browser, no user agent!", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_header(&self, name: &str) -> Option<String> {
        let key = String::from(name);

        match self.headers.get(&key) {
            Some(val)   => Some(val.clone()),
            None        => None,
        }
    }

    /// Get a variable from the URI.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the route "/hello/<str:name>"
    /// fn handler(req: &Request) -> Response {
    ///     let name: String = req.get("name");
    ///     utils::make_response(format!("<b>Hello, {}!</b>", name), "text/html", 200)
    /// }
    /// ```
    pub fn get<T: FromUri>(&self, name: &str) -> T {
        if !self.params.contains_key(name) {
            panic!("invalid route parameter {:?}", name);
        }

        FromUri::from_uri(&self.params[name])
    }

    /// Get a raw JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data = req.get_json();
    ///
    ///     match data {
    ///         Some(val) => utils::make_response(format!("We got: {}", val), "text/plain", 200),
    ///         None      => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json(&self) -> Option<json::Json> {
        match String::from_utf8(self.payload.clone()) {
            Err(_)      => None,
            Ok(payload) => {
                match json::Json::from_str(&payload) {
                    Ok(data)    => Some(data),
                    Err(_)      => None,
                }
            }
        }
    }

    /// Get a composed JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// use canteen::{Request, Response};
    ///
    /// #[derive(RustcDecodable)]
    /// struct Foo {
    ///     item: i32,
    /// }
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data: Foo = req.get_json_obj();
    ///
    ///     match data {
    ///         Ok(foo) => utils::make_response(format!("We got: {}!", data.item), "text/plain", 200),
    ///         Err(_)  => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json_obj<T: Decodable>(&self) -> Result<T, json::DecoderError> {
        let data = String::from_utf8(self.payload.clone()).unwrap();
        json::decode(&data)
    }

    fn parse(&mut self, rqstr: &str) {
        let mut buf: Vec<&str> = rqstr.splitn(2, "\r\n").collect();
        let ask: Vec<&str> = buf[0].splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);

        loop {
            buf = buf[1].splitn(2, "\r\n").collect();

            if buf[0] == "" {
                if buf.len() == 1 || buf[1] == "" {
                    // no payload
                    break;
                }

                self.payload.extend(buf[1].as_bytes());
                break;
            }

            let hdr: Vec<&str> = buf[0].splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

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

    #[derive(RustcDecodable)]
    struct Foo {
        item: i32,
    }

    #[test]
    fn test_fromuri_trait_i32() {
        let pos = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&pos));

        let neg = String::from("-4321");
        assert_eq!(-4321, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_fromuri_trait_u32() {
        let orig = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_string() {
        let orig = String::from("foobar");
        assert_eq!("foobar", <String as FromUri>::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_float() {
        let pos = String::from("123.45");
        assert_eq!(123.45f32, FromUri::from_uri(&pos));

        let neg = String::from("-54.321");
        assert_eq!(-54.321f32, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_get_fromuri_i32() {
        let mut req = Request::new();
        req.params.insert(String::from("test"), String::from("1234"));

        assert_eq!(1234, req.get("test"));
    }

    #[test]
    fn test_get_json() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data = req.get_json().unwrap();

        assert_eq!(true, data.is_object());

        let obj = data.as_object().unwrap();
        let val = obj.get("item").unwrap();

        assert_eq!(true, val.is_u64());
        assert_eq!(123u64, val.as_u64().unwrap());
    }

    #[test]
    fn test_get_json_obj() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data: Foo = req.get_json_obj().unwrap();

        assert_eq!(123, data.item);
    }
}
<|endoftext|>