<commit_before>use std::{ net, marker, io, collections,  };
use std::str::FromStr;
use std::io::{ Read, Write };
use std::borrow::Borrow;

use mio::*;

use crypto::sha1;
use crypto::digest::Digest;

use rustc_serialize::base64;
use rustc_serialize::base64::ToBase64;

use utils::*;
use socket_message;
use user_message;

pub use user_message::Message;

const SERVER: Token = Token(0);
const WEBSOCKET_GUID: &'static str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

struct TokenFactory(usize);
impl TokenFactory {
    fn next_token(&mut self) -> Token { self.0 += 1; Token(self.0) }
}

pub struct WebSocketServer<T, C> where T: WebSocketHandler<C> {
    handler: T,                                         // User handler which implements callbacks
    phantom: marker::PhantomData<C>,                    // I need to use this to calm down the compiler
    listener: tcp::TcpListener,                         // Listens all incoming connections

    // List af all clients:
    // C - User object returned for a new connection
    // TcpStream - Stream we need to delete in case of disconnect
    // bool - If client was handshaked
    clients: collections::HashMap<Token, (C, tcp::TcpStream, bool)>,
    tokens: TokenFactory,                               // Counter to assign tokens to clients
    message_fac: socket_message::MessageFactory                // Factory, which produces messages from a raw data
}

impl<T, C> Handler for WebSocketServer<T, C> where T: WebSocketHandler<C> {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, main_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
        match token {
            // Got new connection. Lets add it to the list of clients and notify a user
            SERVER => {
                match self.listener.accept() {
                    Ok(client) => {
                        if let Err(e) = self.register_client(client, main_loop) {
                            println!("Error registering a client: {}", e)
                        }
                    },
                    Err(e) => { println!("Error accepting a client: {}", e) }
                }
            },
            ref token => {
                // Client has been disconnected
                // TODO: Handle error
                if events.is_error() || events.is_hup() {
                    if let Err(e) = self.disconnect_client(*token, main_loop) {
                        println!("Error disconnecting a client {}", e)
                    }
                } else if events.is_readable() {
                    self.read_client_data(token)
                }
            }
        }
    }
}

impl<T, C> WebSocketServer<T, C> where T: WebSocketHandler<C> + Sync {
    fn new(handler: T, addr: &'static str) -> io::Result<()> {
        let formated_addr = net::SocketAddr::from_str(addr).unwrap();
        let listener = try!(tcp::TcpListener::bind(&formated_addr));

        let mut result = WebSocketServer {
            handler: handler,
            phantom: marker::PhantomData,
            listener:listener,
            clients: collections::HashMap::new(),
            tokens: TokenFactory(1),
            message_fac: socket_message::MessageFactory::new()
        };

        let mut main_loop = try!(EventLoop::<Self>::new());
        try!(main_loop.register(&result.listener, SERVER));
        try!(main_loop.run(&mut result));

        return Ok(())
    }

    fn register_client(&mut self, client: Option<tcp::TcpStream>, main_loop: &mut EventLoop<Self>) -> Result<(), String> {
        if let Some(client) = client {
            let token = self.tokens.next_token();

            match main_loop.register(&client, token) {
                Err(e) => { return Err(format!("Error registering a client: {}", e)) },
                _ => {
                    let addr = format!("{}", client.peer_addr().unwrap());

                    let _ = self.clients.insert(token, (self.handler.on_connect(addr), client, false));
                }
            }
        }
        return Ok(())
    }

    fn read_client_data(&mut self, token: & Token) {
        // Got a message from the client. Lets get our client data
        match self.clients.get_mut(token) {
            Some(&mut (ref mut client,
                       ref mut stream,
                       ref mut handshaked)) => {
                let ref mut buffer = [0; 1024];

                // Read a message from the client socket
                match stream.read(buffer) {
                    Ok(size) if *handshaked => {
                        if let Some(message) =  self.message_fac.parse(&buffer[..size], token) {
                            match message.opcode {
                                socket_message::OPCODE_PING => {
                                    if let Err(e) =
                                        stream.write_all(self.message_fac.create_pong_message().into_vector().borrow())
                                    {
                                        println!("Can't send pong to the client: {}", e);
                                    }
                                }
                                _ => {
                                    let incoming_message = user_message::Message::new(message.payload,
                                                                                stream,
                                                                                message.opcode);
                                    self.handler.on_message(incoming_message, client)
                                }
                            }
                        }
                    }
                    Ok(size) => {
                        match Self::create_handshake_response(slice_to_string(&buffer[..size])) {
                            Ok(response) => {
                                match stream.write_all(response.as_bytes()) {
                                    Ok(_) => { *handshaked = true }
                                    Err(e) => { println!("Can't send a handshake response to the client: {}", e) }
                                }
                            },
                            Err(e) => { println!("Failed to handshake a client: {}", e) }
                        }
                    },
                    Err(e) => { println!("An error occured while reading client socket {}", e) }
                }
            },
            None => { println!("Failed to find a client, which sends a message {:?}", token); }
        }
    }

    pub fn create_handshake_response(message: String) -> Result<String, String> {
        // Check if we got valid handshake message from a client
        match message.find("Upgrade: websocket") {
            Some(_) => {
                // Check if message contains a security key
                let key = message.lines().
                    find(|s| s.starts_with("Sec-WebSocket-Key")).
                    and_then(|s| s.split(":").last());

                match key {
                    // Calculate response security key
                    Some(key) => {
                        let ref mut sha_object = sha1::Sha1::new();
                        sha_object.input_str(format!("{}{}", key.trim(), WEBSOCKET_GUID).borrow());

                        // Seriously `crypto`? Buffer as a parameter?
                        let ref mut buffer = [0; 20]; sha_object.result(buffer);

                        // Response
                        return Ok(format!("{}\r\n{}\r\n{}\r\n{}\r\n\r\n",
                                          "HTTP/1.1 101 Switching Protocols",
                                          "Upgrade: websocket",
                                          "Connection: Upgrade",
                                          format!("Sec-WebSocket-Accept: {}", buffer.to_base64(base64::MIME))));
                    },
                    None => { Err(format!("Can't find a key in a handshake message: \n{}", message)) }
                }
            },
            None => { Err(format!("Client sent invalid handshake: \n{}", message)) }
        }
    }

    fn disconnect_client(&mut self, token: Token, main_loop: &mut EventLoop<Self>) -> Result<(), String> {
        match self.clients.remove(&token) {
            Some((client, stream, _)) => {
                match main_loop.deregister(&stream) {
                    Ok(_) => { self.handler.on_disconnect(client) },
                    Err(e) => { return Err(format!("Failed do unregister a client {}", e)) }
                }
            },
            None => { return Err(format!("Failed to disconnect a client")) }
        }
        return Ok(())
    }
}

pub trait WebSocketHandler<C>: Sized + marker::Sync {
    fn listen(self, addr: &'static str) -> io::Result<()> {
        return WebSocketServer::new(self, addr)
    }

    fn on_connect(&self, addr: String) -> C;
    fn on_message(&self, mut message: user_message::Message, client: &mut C);
    fn on_disconnect(&self, client: C);
}
<commit_msg>Add websocket disconnection<commit_after>use std::{ net, marker, io, collections,  };
use std::str::FromStr;
use std::io::{ Read, Write };
use std::borrow::Borrow;

use mio::*;

use crypto::sha1;
use crypto::digest::Digest;

use rustc_serialize::base64;
use rustc_serialize::base64::ToBase64;

use utils::*;
use socket_message;
use user_message;

pub use user_message::Message;

const SERVER: Token = Token(0);
const WEBSOCKET_GUID: &'static str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

struct TokenFactory(usize);
impl TokenFactory {
    fn next_token(&mut self) -> Token { self.0 += 1; Token(self.0) }
}

pub struct WebSocketServer<T, C> where T: WebSocketHandler<C> {
    handler: T,                                         // User handler which implements callbacks
    phantom: marker::PhantomData<C>,                    // I need to use this to calm down the compiler
    listener: tcp::TcpListener,                         // Listens all incoming connections

    // List af all clients:
    // C - User object returned for a new connection
    // TcpStream - Stream we need to delete in case of disconnect
    // bool - If client was handshaked
    clients: collections::HashMap<Token, (C, tcp::TcpStream, bool)>,
    tokens: TokenFactory,                               // Counter to assign tokens to clients
    message_fac: socket_message::MessageFactory                // Factory, which produces messages from a raw data
}

impl<T, C> Handler for WebSocketServer<T, C> where T: WebSocketHandler<C> {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, main_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
        match token {
            // Got new connection. Lets add it to the list of clients and notify a user
            SERVER => {
                match self.listener.accept() {
                    Ok(client) => {
                        if let Err(e) = self.register_client(client, main_loop) {
                            println!("Error registering a client: {}", e)
                        }
                    },
                    Err(e) => { println!("Error accepting a client: {}", e) }
                }
            },
            ref token => {
                // Client has been disconnected
                // TODO: Handle error
                if events.is_error() || events.is_hup() {
                    if let Err(e) = self.disconnect_client(token, main_loop) {
                        println!("Error disconnecting a client {}", e)
                    }
                } else if events.is_readable() {
                    self.read_client_data(token, main_loop)
                }
            }
        }
    }
}

impl<T, C> WebSocketServer<T, C> where T: WebSocketHandler<C> + Sync {
    fn new(handler: T, addr: &'static str) -> io::Result<()> {
        let formated_addr = net::SocketAddr::from_str(addr).unwrap();
        let listener = try!(tcp::TcpListener::bind(&formated_addr));

        let mut result = WebSocketServer {
            handler: handler,
            phantom: marker::PhantomData,
            listener:listener,
            clients: collections::HashMap::new(),
            tokens: TokenFactory(1),
            message_fac: socket_message::MessageFactory::new()
        };

        let mut main_loop = try!(EventLoop::<Self>::new());
        try!(main_loop.register(&result.listener, SERVER));
        try!(main_loop.run(&mut result));

        return Ok(())
    }

    fn register_client(&mut self, client: Option<tcp::TcpStream>, main_loop: &mut EventLoop<Self>) -> Result<(), String> {
        if let Some(client) = client {
            let token = self.tokens.next_token();

            match main_loop.register(&client, token) {
                Err(e) => { return Err(format!("Error registering a client: {}", e)) },
                _ => {
                    let addr = format!("{}", client.peer_addr().unwrap());

                    let _ = self.clients.insert(token, (self.handler.on_connect(addr), client, false));
                }
            }
        }
        return Ok(())
    }

    fn read_client_data(&mut self, token: & Token, main_loop: &mut EventLoop<Self>) {
        // If got disconnection, will be set to true
        let mut disconnect = false;

        // Got a message from the client. Lets get our client data
        match self.clients.get_mut(token) {
            Some(&mut (ref mut client,
                       ref mut stream,
                       ref mut handshaked)) => {
                let ref mut buffer = [0; 1024];

                // Read a message from the client socket
                match stream.read(buffer) {
                    Ok(size) if *handshaked => {
                        if let Some(message) =  self.message_fac.parse(&buffer[..size], token) {
                            match message.opcode {
                                socket_message::OPCODE_PING => {
                                    if let Err(e) =
                                        stream.write_all(self.message_fac.create_pong_message().into_vector().borrow())
                                    {
                                        println!("Can't send pong to the client: {}", e);
                                    }
                                }
                                socket_message::OPCODE_CONNECTION_CLOSE => {
                                    disconnect = true;
                                }
                                _ => {
                                    let incoming_message = user_message::Message::new(message.payload,
                                                                                stream,
                                                                                message.opcode);
                                    self.handler.on_message(incoming_message, client)
                                }
                            }
                        }
                    }
                    Ok(size) => {
                        match Self::create_handshake_response(slice_to_string(&buffer[..size])) {
                            Ok(response) => {
                                match stream.write_all(response.as_bytes()) {
                                    Ok(_) => { *handshaked = true }
                                    Err(e) => { println!("Can't send a handshake response to the client: {}", e) }
                                }
                            },
                            Err(e) => { println!("Failed to handshake a client: {}", e) }
                        }
                    },
                    Err(e) => { println!("An error occured while reading client socket {}", e) }
                }
            },
            None => { println!("Failed to find a client, which sends a message {:?}", token); }
        }

        if disconnect {
            if let Err(e) =
                self.disconnect_client(token, main_loop)
            {
                println!("Failed to disconnect client properly: {}", e);
            }
        }
    }

    pub fn create_handshake_response(message: String) -> Result<String, String> {
        // Check if we got valid handshake message from a client
        match message.find("Upgrade: websocket") {
            Some(_) => {
                // Check if message contains a security key
                let key = message.lines().
                    find(|s| s.starts_with("Sec-WebSocket-Key")).
                    and_then(|s| s.split(":").last());

                match key {
                    // Calculate response security key
                    Some(key) => {
                        let ref mut sha_object = sha1::Sha1::new();
                        sha_object.input_str(format!("{}{}", key.trim(), WEBSOCKET_GUID).borrow());

                        // Seriously `crypto`? Buffer as a parameter?
                        let ref mut buffer = [0; 20]; sha_object.result(buffer);

                        // Response
                        return Ok(format!("{}\r\n{}\r\n{}\r\n{}\r\n\r\n",
                                          "HTTP/1.1 101 Switching Protocols",
                                          "Upgrade: websocket",
                                          "Connection: Upgrade",
                                          format!("Sec-WebSocket-Accept: {}", buffer.to_base64(base64::MIME))));
                    },
                    None => { Err(format!("Can't find a key in a handshake message: \n{}", message)) }
                }
            },
            None => { Err(format!("Client sent invalid handshake: \n{}", message)) }
        }
    }

    fn disconnect_client(&mut self, token: & Token, main_loop: &mut EventLoop<Self>) -> Result<(), String> {
        match self.clients.remove(token) {
            Some((client, stream, _)) => {
                match main_loop.deregister(&stream) {
                    Ok(_) => { self.handler.on_disconnect(client) },
                    Err(e) => { return Err(format!("Failed do unregister a client {}", e)) }
                }
            },
            None => { return Err(format!("Failed to disconnect a client")) }
        }
        return Ok(())
    }
}

pub trait WebSocketHandler<C>: Sized + marker::Sync {
    fn listen(self, addr: &'static str) -> io::Result<()> {
        return WebSocketServer::new(self, addr)
    }

    fn on_connect(&self, addr: String) -> C;
    fn on_message(&self, mut message: user_message::Message, client: &mut C);
    fn on_disconnect(&self, client: C);
}
<|endoftext|>


<commit_before>use std::io::net::udp::UdpSocket;
use std::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use std::io::{IoResult, IoError, OtherIoError, TimedOut};
use std::io::Timer;
use std::comm::{Disconnected, Empty, Select};
use std::time::duration::Duration;
use std::collections::TreeMap;
use packet::{Packet, PacketType, PacketConnect, PacketDisconnect, PacketMessage, PacketAccept, PacketReject, Command, Disconnect};
use time;

//FIXME: Ew ew ew
fn hash_sender(address: &SocketAddr) -> String {
    match address.ip {
        Ipv4Addr(a, b, c, d) => {
            format!("{}-{}-{}-{}:{}", a, b, c, d, address.port)
        },
        Ipv6Addr(a, b, c, d, e, f, g, h) => {
            format!("{}-{}-{}-{}-{}-{}-{}-{}:{}", a, b, c, d, e, f, g, h, address.port)
        }
    }
}

#[deriving(Clone)]
struct ClientInstance {
    addr: SocketAddr,
    timeout: i64
}

impl ClientInstance {
    pub fn new(addr: SocketAddr, timeout: i64) -> ClientInstance {
        ClientInstance {
            addr: addr,
            timeout: timeout
        }
    }
}

/**
 * What we want:
 * Server sits and loops about. Has a send/receive buffer
 * Has a broadcast method?
 * Has a send method
 * Has a read method, to pump items out of the receive buffer
 * Has a read iterator?
 */
fn reader_process(mut reader: UdpSocket, reader_sub_out: Sender<(Packet, SocketAddr)>, reader_sub_in: Receiver<Command>, protocol_id: u32) {
    let mut buf = [0, ..255];
    reader.set_timeout(Some(1000));
    loop {
        match reader.recv_from(buf) {
            Ok((amt, src)) => {
                match Packet::deserialize(buf.slice_to(amt)) {
                    Ok(packet) => {
                        if packet.protocol_id == protocol_id {
                            reader_sub_out.send((packet, src));
                        }
                    },
                    Err(_) => ()
                }
            },
            Err(e) => {
                match e.kind {
                    TimedOut => {
                        match reader_sub_in.try_recv() {
                            Ok(Disconnect) => {
                                break;
                            },
                            Err(Disconnected) => {
                                break;
                            },
                            Err(Empty) => ()
                        }
                    },
                    _ => ()
                }
            }
        }
    }
}

fn writer_process(mut writer: UdpSocket, writer_sub_out: Sender<Command>, writer_sub_in: Receiver<(Packet, SocketAddr)>) {
    for (msg, target_addr) in writer_sub_in.iter() {
        match msg.serialize() {
            Ok(msg) => {
                match writer.send_to(msg.as_slice(), target_addr) {
                    Ok(()) => (),
                    Err(e) => println!("Error sending data - {}", e)
                }
            },
            Err(_) => ()
        }
    }
}

pub struct ServerManager {
    pub addr: SocketAddr,

    protocol_id: u32,
    reader_send: Sender<Command>,
    reader_receive: Receiver<(Packet, SocketAddr)>,
    writer_send: Sender<(Packet, SocketAddr)>,
    writer_receive: Receiver<Command>,

    connections: TreeMap<String, ClientInstance>
}

impl ServerManager {
    pub fn new(protocol_id: u32, addr: SocketAddr) -> IoResult<ServerManager> {
        match UdpSocket::bind(addr) {
            Ok(reader) => {
                let writer = reader.clone();
                let (reader_out, reader_sub_in) = channel();
                let (reader_sub_out, reader_in) = channel();

                spawn(proc() {
                    reader_process(reader, reader_sub_out, reader_sub_in, protocol_id);
                });

                let (writer_out, writer_sub_in) = channel();
                let (writer_sub_out, writer_in) = channel();

                spawn(proc() {
                    writer_process(writer, writer_sub_out, writer_sub_in);
                });

                Ok(ServerManager {
                    protocol_id: protocol_id,
                    addr: addr,
                    reader_send: reader_out,
                    reader_receive: reader_in,
                    writer_send: writer_out,
                    writer_receive: writer_in,
                    connections: TreeMap::new()
                })
            }
            Err(e) => Err(e)
        }
    }

    pub fn poll(&mut self) -> Option<(Packet, SocketAddr)> {
        let mut out = None;
        loop {
            match self.reader_receive.try_recv() {
                Ok((packet, src)) => {
                    //Handle any new connections
                    match packet.packet_type {
                        PacketConnect => {
                            self.connections.insert(hash_sender(&src), ClientInstance::new(src, time::now().to_timespec().sec + 10)); //FIXME: Shouldn't be done here
                            self.writer_send.send((Packet::accept(self.protocol_id), src));
                            out = Some((packet, src));
                            break
                        },
                        PacketDisconnect => {
                            let hash = hash_sender(&src);
                            if self.connections.contains_key(&hash) {
                                out = Some((packet, src));
                                self.connections.remove(&hash);
                                break
                            }
                        },
                        PacketMessage => {
                            let hash = hash_sender(&src);
                            match self.connections.find_mut(&hash) {
                                Some(ref mut comms) => {
                                    out = Some((packet, src));
                                    //Update our timeout
                                    comms.timeout = time::now().to_timespec().sec + 10; //FIXME: Stop hardcoding
                                    break
                                },
                                None => ()
                            }
                        },
                        _ => ()
                    };
                },
                _ => {
                    break
                }
            };
        };
        out
    }

    pub fn cull(&mut self) {
        let mut keep_alive = TreeMap::new();
        let now = time::now().to_timespec().sec;

        for (hash, connection) in self.connections.iter() {
            if connection.timeout >= now {
                keep_alive.insert(hash.clone(), connection.clone());
            }
        };

        self.connections = keep_alive;
    }

    pub fn send_to(&mut self, packet: &Packet, addr: &SocketAddr) {
        let hashed = hash_sender(addr);
        match self.connections.find(&hashed) {
            Some(_) => self.writer_send.send((packet.clone(), addr.clone())),
            None => (),
        }
    }

    pub fn send_to_many(&mut self, packet: &Packet, addrs: &Vec<SocketAddr>) {
        for addr in addrs.iter() {
            self.send_to(packet, addr)
        }
    }

    pub fn send_to_all(&mut self, packet: &Packet) {
        for addr in self.connections.clone().values() { //FIXME: Urgh
            self.send_to(packet, &addr.addr)
        }
    }
}

impl Drop for ServerManager {

    fn drop(&mut self) {
        self.reader_send.send(Disconnect);
    }
}
<commit_msg>Connection culling now returns a list of connections<commit_after>use std::io::net::udp::UdpSocket;
use std::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use std::io::{IoResult, IoError, OtherIoError, TimedOut};
use std::io::Timer;
use std::comm::{Disconnected, Empty, Select};
use std::time::duration::Duration;
use std::collections::TreeMap;
use packet::{Packet, PacketType, PacketConnect, PacketDisconnect, PacketMessage, PacketAccept, PacketReject, Command, Disconnect};
use time;

//FIXME: Ew ew ew
fn hash_sender(address: &SocketAddr) -> String {
    match address.ip {
        Ipv4Addr(a, b, c, d) => {
            format!("{}-{}-{}-{}:{}", a, b, c, d, address.port)
        },
        Ipv6Addr(a, b, c, d, e, f, g, h) => {
            format!("{}-{}-{}-{}-{}-{}-{}-{}:{}", a, b, c, d, e, f, g, h, address.port)
        }
    }
}

#[deriving(Clone)]
struct ClientInstance {
    addr: SocketAddr,
    timeout: i64
}

impl ClientInstance {
    pub fn new(addr: SocketAddr, timeout: i64) -> ClientInstance {
        ClientInstance {
            addr: addr,
            timeout: timeout
        }
    }
}

/**
 * What we want:
 * Server sits and loops about. Has a send/receive buffer
 * Has a broadcast method?
 * Has a send method
 * Has a read method, to pump items out of the receive buffer
 * Has a read iterator?
 */
fn reader_process(mut reader: UdpSocket, reader_sub_out: Sender<(Packet, SocketAddr)>, reader_sub_in: Receiver<Command>, protocol_id: u32) {
    let mut buf = [0, ..255];
    reader.set_timeout(Some(1000));
    loop {
        match reader.recv_from(buf) {
            Ok((amt, src)) => {
                match Packet::deserialize(buf.slice_to(amt)) {
                    Ok(packet) => {
                        if packet.protocol_id == protocol_id {
                            reader_sub_out.send((packet, src));
                        }
                    },
                    Err(_) => ()
                }
            },
            Err(e) => {
                match e.kind {
                    TimedOut => {
                        match reader_sub_in.try_recv() {
                            Ok(Disconnect) => {
                                break;
                            },
                            Err(Disconnected) => {
                                break;
                            },
                            Err(Empty) => ()
                        }
                    },
                    _ => ()
                }
            }
        }
    }
}

fn writer_process(mut writer: UdpSocket, writer_sub_out: Sender<Command>, writer_sub_in: Receiver<(Packet, SocketAddr)>) {
    for (msg, target_addr) in writer_sub_in.iter() {
        match msg.serialize() {
            Ok(msg) => {
                match writer.send_to(msg.as_slice(), target_addr) {
                    Ok(()) => (),
                    Err(e) => println!("Error sending data - {}", e)
                }
            },
            Err(_) => ()
        }
    }
}

pub struct ServerManager {
    pub addr: SocketAddr,

    protocol_id: u32,
    reader_send: Sender<Command>,
    reader_receive: Receiver<(Packet, SocketAddr)>,
    writer_send: Sender<(Packet, SocketAddr)>,
    writer_receive: Receiver<Command>,

    connections: TreeMap<String, ClientInstance>
}

impl ServerManager {
    pub fn new(protocol_id: u32, addr: SocketAddr) -> IoResult<ServerManager> {
        match UdpSocket::bind(addr) {
            Ok(reader) => {
                let writer = reader.clone();
                let (reader_out, reader_sub_in) = channel();
                let (reader_sub_out, reader_in) = channel();

                spawn(proc() {
                    reader_process(reader, reader_sub_out, reader_sub_in, protocol_id);
                });

                let (writer_out, writer_sub_in) = channel();
                let (writer_sub_out, writer_in) = channel();

                spawn(proc() {
                    writer_process(writer, writer_sub_out, writer_sub_in);
                });

                Ok(ServerManager {
                    protocol_id: protocol_id,
                    addr: addr,
                    reader_send: reader_out,
                    reader_receive: reader_in,
                    writer_send: writer_out,
                    writer_receive: writer_in,
                    connections: TreeMap::new()
                })
            }
            Err(e) => Err(e)
        }
    }

    pub fn poll(&mut self) -> Option<(Packet, SocketAddr)> {
        let mut out = None;
        loop {
            match self.reader_receive.try_recv() {
                Ok((packet, src)) => {
                    //Handle any new connections
                    match packet.packet_type {
                        PacketConnect => {
                            self.connections.insert(hash_sender(&src), ClientInstance::new(src, time::now().to_timespec().sec + 10)); //FIXME: Shouldn't be done here
                            self.writer_send.send((Packet::accept(self.protocol_id), src));
                            out = Some((packet, src));
                            break
                        },
                        PacketDisconnect => {
                            let hash = hash_sender(&src);
                            if self.connections.contains_key(&hash) {
                                out = Some((packet, src));
                                self.connections.remove(&hash);
                                break
                            }
                        },
                        PacketMessage => {
                            let hash = hash_sender(&src);
                            match self.connections.find_mut(&hash) {
                                Some(ref mut comms) => {
                                    out = Some((packet, src));
                                    //Update our timeout
                                    comms.timeout = time::now().to_timespec().sec + 10; //FIXME: Stop hardcoding
                                    break
                                },
                                None => ()
                            }
                        },
                        _ => ()
                    };
                },
                _ => {
                    break
                }
            };
        };
        out
    }

    pub fn cull(&mut self) -> Vec<SocketAddr> {
        let mut keep_alive = TreeMap::new();
        let mut culled = vec![];

        let now = time::now().to_timespec().sec;

        for (hash, connection) in self.connections.iter() {
            if connection.timeout >= now {
                keep_alive.insert(hash.clone(), connection.clone());
            } else {
                culled.push(connection.addr);
            }
        };

        self.connections = keep_alive;
        culled
    }

    pub fn send_to(&mut self, packet: &Packet, addr: &SocketAddr) {
        let hashed = hash_sender(addr);
        match self.connections.find(&hashed) {
            Some(_) => self.writer_send.send((packet.clone(), addr.clone())),
            None => (),
        }
    }

    pub fn send_to_many(&mut self, packet: &Packet, addrs: &Vec<SocketAddr>) {
        for addr in addrs.iter() {
            self.send_to(packet, addr)
        }
    }

    pub fn send_to_all(&mut self, packet: &Packet) {
        for addr in self.connections.clone().values() { //FIXME: Urgh
            self.send_to(packet, &addr.addr)
        }
    }
}

impl Drop for ServerManager {

    fn drop(&mut self) {
        self.reader_send.send(Disconnect);
    }
}
<|endoftext|>


<commit_before>use handlebars::Handlebars;
use rocket;

pub fn serve() {
    // in debug builds this will force an init, good enough for testing
    let _hbars = &*TEMPLATES;

    rocket::ignite()
        .mount("/api",
               routes![api::all_fcps, api::member_fcps, api::github_webhook])
        .mount("/", routes![html::all_fcps, html::member_fcps])
        .launch();
}

mod html {
    use std::collections::BTreeMap;
    use rocket::response::content;
    use super::TEMPLATES;
    use error::DashResult;
    use nag;

    #[get("/")]
    fn all_fcps() -> DashResult<content::Html<String>> {
        let mut teams = BTreeMap::new();
        for fcp in nag::all_fcps()? {
            let nag::FcpWithInfo {
                fcp,
                reviews,
                issue,
                status_comment,
            } = fcp;

            let mut pending_reviewers = reviews
                .into_iter()
                .filter(|&(_, reviewed)| !reviewed)
                .map(|(user, _)| user.login)
                .collect::<Vec<String>>();

            pending_reviewers.sort();

            let record = json!({
                "disposition": fcp.disposition,
                "issue": issue,
                "statusComment": status_comment,
                "pendingReviewers": pending_reviewers,
            });

            for label in issue.labels.iter().filter(|l| l.starts_with("T-")).cloned() {
                teams
                    .entry(label)
                    .or_insert_with(Vec::new)
                    .push(record.clone());
            }
        }

        let context = teams
            .into_iter()
            .map(|(team_label, fcps)| {
                     json!({
                "team": team_label,
                "fcps": fcps,
            })
                 })
            .collect::<Vec<_>>();

        let rendered = TEMPLATES.render("all", &json!({"model": context}))?;
        println!("rendered\n{}", &rendered);
        Ok(content::Html(rendered))
    }

    #[get("/fcp/<username>")]
    fn member_fcps(username: String) -> DashResult<content::Html<String>> {
        let (user, fcps) = nag::individual_nags(&username)?;

        let context = json!({
            "model": {
                "user": user,
                "fcps": fcps,
            }
        });

        let rendered = TEMPLATES.render("user", &context)?;
        Ok(content::Html(rendered))
    }
}

mod api {
    use rocket_contrib::Json;
    use DB_POOL;
    use domain::github::GitHubUser;
    use error::DashResult;
    use github::{handle_comment, handle_issue, handle_pr};
    use github::webhooks::{Event, Payload};
    use nag;

    #[get("/all")]
    pub fn all_fcps() -> DashResult<Json<Vec<nag::FcpWithInfo>>> { Ok(Json(nag::all_fcps()?)) }

    #[get("/<username>")]
    pub fn member_fcps(username: String)
                       -> DashResult<Json<(GitHubUser, Vec<nag::IndividualFcp>)>> {
        Ok(Json(nag::individual_nags(&username)?))
    }

    #[post("/github-webhook", data="<event>")]
    pub fn github_webhook(event: Event) -> DashResult<()> {
        let conn = &*DB_POOL.get()?;

        match event.payload {
            Payload::Issues(issue_event) => {
                handle_issue(conn, issue_event.issue, &issue_event.repository.full_name)?;
            }

            Payload::PullRequest(pr_event) => {
                handle_pr(conn, pr_event.pull_request, &pr_event.repository.full_name)?;
            }

            Payload::IssueComment(comment_event) => {
                // possible race conditions if we get a comment hook before the issue one (or we
                // missed the issue one), so make sure the issue exists first

                if comment_event.action != "deleted" {
                    // TODO handle deleted comments properly
                    handle_issue(conn,
                                 comment_event.issue,
                                 &comment_event.repository.full_name)?;
                    handle_comment(conn,
                                   comment_event.comment,
                                   &comment_event.repository.full_name)?;
                }
            }

            Payload::Unsupported => (),
        }

        Ok(())
    }
}

lazy_static! {
    static ref TEMPLATES: Handlebars = {
        let mut hbars = Handlebars::new();
        let root_template = include_str!("templates/index.html");

        let all_fcps_fragment = include_str!("templates/fcp.hbs");
        let all_fcps_template = root_template.replace("{{content}}", all_fcps_fragment);

        let user_fcps_fragment = include_str!("templates/fcp-user.hbs");
        let user_fcps_template = root_template.replace("{{content}}", user_fcps_fragment);

        hbars.register_template_string("all", &all_fcps_template)
            .expect("unable to register all-fcps template");
        hbars.register_template_string("user", &user_fcps_template)
            .expect("unable to register user fcps template");

        hbars
    };
}
<commit_msg>rustfmt update<commit_after>use handlebars::Handlebars;
use rocket;

pub fn serve() {
    // in debug builds this will force an init, good enough for testing
    let _hbars = &*TEMPLATES;

    rocket::ignite()
        .mount("/api",
               routes![api::all_fcps, api::member_fcps, api::github_webhook])
        .mount("/", routes![html::all_fcps, html::member_fcps])
        .launch();
}

mod html {
    use std::collections::BTreeMap;
    use rocket::response::content;
    use super::TEMPLATES;
    use error::DashResult;
    use nag;

    #[get("/")]
    fn all_fcps() -> DashResult<content::Html<String>> {
        let mut teams = BTreeMap::new();
        for fcp in nag::all_fcps()? {
            let nag::FcpWithInfo {
                fcp,
                reviews,
                issue,
                status_comment,
            } = fcp;

            let mut pending_reviewers = reviews
                .into_iter()
                .filter(|&(_, reviewed)| !reviewed)
                .map(|(user, _)| user.login)
                .collect::<Vec<String>>();

            pending_reviewers.sort();

            let record = json!({
                "disposition": fcp.disposition,
                "issue": issue,
                "statusComment": status_comment,
                "pendingReviewers": pending_reviewers,
            });

            for label in issue.labels.iter().filter(|l| l.starts_with("T-")).cloned() {
                teams
                    .entry(label)
                    .or_insert_with(Vec::new)
                    .push(record.clone());
            }
        }

        let context = teams
            .into_iter()
            .map(|(team_label, fcps)| {
                     json!({
                "team": team_label,
                "fcps": fcps,
            })
                 })
            .collect::<Vec<_>>();

        let rendered = TEMPLATES.render("all", &json!({ "model": context }))?;
        println!("rendered\n{}", &rendered);
        Ok(content::Html(rendered))
    }

    #[get("/fcp/<username>")]
    fn member_fcps(username: String) -> DashResult<content::Html<String>> {
        let (user, fcps) = nag::individual_nags(&username)?;

        let context = json!({
            "model": {
                "user": user,
                "fcps": fcps,
            }
        });

        let rendered = TEMPLATES.render("user", &context)?;
        Ok(content::Html(rendered))
    }
}

mod api {
    use rocket_contrib::Json;
    use DB_POOL;
    use domain::github::GitHubUser;
    use error::DashResult;
    use github::{handle_comment, handle_issue, handle_pr};
    use github::webhooks::{Event, Payload};
    use nag;

    #[get("/all")]
    pub fn all_fcps() -> DashResult<Json<Vec<nag::FcpWithInfo>>> { Ok(Json(nag::all_fcps()?)) }

    #[get("/<username>")]
    pub fn member_fcps(username: String)
                       -> DashResult<Json<(GitHubUser, Vec<nag::IndividualFcp>)>> {
        Ok(Json(nag::individual_nags(&username)?))
    }

    #[post("/github-webhook", data = "<event>")]
    pub fn github_webhook(event: Event) -> DashResult<()> {
        let conn = &*DB_POOL.get()?;

        match event.payload {
            Payload::Issues(issue_event) => {
                handle_issue(conn, issue_event.issue, &issue_event.repository.full_name)?;
            }

            Payload::PullRequest(pr_event) => {
                handle_pr(conn, pr_event.pull_request, &pr_event.repository.full_name)?;
            }

            Payload::IssueComment(comment_event) => {
                // possible race conditions if we get a comment hook before the issue one (or we
                // missed the issue one), so make sure the issue exists first

                if comment_event.action != "deleted" {
                    // TODO handle deleted comments properly
                    handle_issue(conn,
                                 comment_event.issue,
                                 &comment_event.repository.full_name)?;
                    handle_comment(conn,
                                   comment_event.comment,
                                   &comment_event.repository.full_name)?;
                }
            }

            Payload::Unsupported => (),
        }

        Ok(())
    }
}

lazy_static! {
    static ref TEMPLATES: Handlebars = {
        let mut hbars = Handlebars::new();
        let root_template = include_str!("templates/index.html");

        let all_fcps_fragment = include_str!("templates/fcp.hbs");
        let all_fcps_template = root_template.replace("{{content}}", all_fcps_fragment);

        let user_fcps_fragment = include_str!("templates/fcp-user.hbs");
        let user_fcps_template = root_template.replace("{{content}}", user_fcps_fragment);

        hbars.register_template_string("all", &all_fcps_template)
            .expect("unable to register all-fcps template");
        hbars.register_template_string("user", &user_fcps_template)
            .expect("unable to register user fcps template");

        hbars
    };
}
<|endoftext|>


<commit_before>use std;
import std::io;
import std::io::{writer_util, reader_util};
import std::map;
import result;

export rowreader, rowaccess, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inescapedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mutable offset : uint,
    mutable buffers : [@[char]],
    mutable state : state
};

type row = {
    fields : [ fieldtype ]
};

type bufferdescr = {
    escaped: bool,
    buffers: [@[char]],
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow() -> result::t<row, str>;
}

iface rowaccess {
    fn len() -> uint;
    fn getchars(uint) -> [char];
    fn getstr(uint) -> str;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        readlen: 1024u,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false)
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false)
    }
}

impl of rowaccess for row {
    fn len() -> uint {
        vec::len(self.fields)
    }
    fn getchars(field: uint) -> [char] {
        fn unescape(escaped: [char]) -> [char] {
            let r : [char] = [];
            vec::reserve(r, vec::len(escaped));
            let in_q = false;
            for c in escaped {
                if in_q {
                    assert(c == '"');
                    in_q = false;
                } else {
                    in_q = c == '"';
                    r += [c];
                }
            }
            ret r;
        }
        alt self.fields[field] {
            emptyfield() { ret []; }
            bufferfield(desc) {
                let buf = [];
                {
                    let i = 0u;
                    while i < vec::len(desc.buffers) {
                        let from = if (i == 0u)
                            { desc.start } else { 0u };
                        let to = if (i == vec::len(desc.buffers) - 1u)
                            { desc.end } else { vec::len(*desc.buffers[i]) };
                        buf += vec::slice(*desc.buffers[i], from, to);
                        i = i + 1u;
                    }
                }
                if field == self.len() - 1u {
                    // there may be a trailing \r on the last field; we should strip it
                    // if so. bodgy here but seems the most efficient place to deal with this
                    if vec::len(buf) > 0u {
                        if buf[vec::len(buf)-1u] == '\r' {
                            buf = vec::slice(buf, 0u, vec::len(buf)-1u);
                        }
                    }
                }
                if desc.escaped {
                    buf = unescape(buf);
                }
                ret buf;
            }
        };
    }
    fn getstr(field: uint) -> str {
        ret str::from_chars(self.getchars(field));
    }
    fn getall() -> [str] {
        let a = [];
        self.map() { |s|
            a += [s];
        }
        ret a;
    }
    fn map(f: fn(s: str)) {
        let i = 0u;
        let len = self.len();
        while i < len {
            f(self.getstr(i));
            i += 1u;
        }
    }
}

impl of rowiter for rowreader {
    fn readrow() -> result::t<row, str> {
        fn row_from_buf(self: rowreader, &fields: [fieldtype]) -> bool {
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let eb = vec::len(self.buffers);
                let sb = sb, so = so, eo = eo;
                if escaped {
                    so += 1u;
                    if so > vec::len(*self.buffers[sb]) {
                        sb += 1u;
                        so = vec::len(*self.buffers[sb]) - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = vec::len(*self.buffers[sb]) - 1u;
                    }
                }
                bufferfield({ escaped: escaped, buffers: vec::slice(self.buffers, sb, eb), start: so, end: eo })
            }
            fn statestr(state: state) -> str {
                alt state {
                    fieldstart(after_delim) {
                        #fmt("fieldstart : after_delim %b", after_delim)
                    }
                    infield(b,o) {
                        #fmt("field : %u %u", b, o)
                    }
                    inescapedfield(b, o) {
                        #fmt("inescapedfield : %u %u", b, o)
                    }
                    inquote(b, o) {
                        #fmt("inquote : %u %u", b, o)
                    }
                }
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf: @[char] = self.buffers[cbuffer];
            while self.offset < vec::len(*buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                //io::println(#fmt("-> %c | %s", c, statestr(self.state)));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        if c == self.quote {
                            self.state = inescapedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [emptyfield];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [emptyfield];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        if c == '\n' {
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                        }
                    }
                    inescapedfield(b, o) {
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                        else if c == self.delim {
                         self.state = fieldstart(true);
                         fields += [new_bufferfield(self, true, b, o, coffset)];
                        }
                    }
                    inquote(b, o) {
                        //io::println(#fmt("inquote : %u %u", b, o));
                        if c == '\n' {
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                            ret true;
                        } else if c == self.quote {
                            self.state = inescapedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                //io::println(#fmt(".... %s", statestr(self.state)));
            }
            ret false;
        }

        self.state = fieldstart(false);
        let do_read = vec::len(self.buffers) == 0u;
        let fields = [];
        while true {
            if do_read {
                let data: @[char] = @self.f.read_chars(self.readlen);
                if vec::len(*data) == 0u {
                    ret result::err("EOF");
                }
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, fields) {
                let r: row = { fields: fields };
                fields = [];
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                ret result::ok(r);
            }
            do_read = true;
        }
        ret result::err("unreachable");
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    while true {
        let res = r.readrow();
        if result::failure(res) {
            break;
        }
        let m : map::hashmap<str, str> = map::new_str_hash();
        let col = 0u;
        let row = result::get(res);
        if row.len() != vec::len(cols) {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        result::get(res).map() { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            hashmap_iter_cols(r, result::get(res).getall(), f);
        }
        result::err(_) { }
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            let cols : [str] = vec::map(result::get(res).getall(), hmap);
            if !hver(cols) {
                ret;
            }
            hashmap_iter_cols(r, cols, f);
        }
        result::err(_) { }
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(testdata);
            let r = mk(f);
            let i = 0u;
            while true {
                let res = r.readrow();
                if result::failure(res) {
                    break;
                }
                let row = result::get(res);
                let expect = expected[i];

                assert(row.len() == vec::len(expect));
                let j = 0u;
                while j < row.len() {
                    assert(row.getstr(j) == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(testdata: str) {
            // test default reader params
            chk() { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test continuations over read buffers
            let j = 1u;
            while j < str::len(testdata) {
                chk() { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
        };
        runchecks(testdata);
        runchecks(str::replace(testdata, "\n", "\r\n"));
    }

    #[test]
    fn test_simple() {
        rowmatch("a,b,c,d\n1,2,3,4\n",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn test_trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,\n",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn test_leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4\n",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn test_quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d\n",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d\n",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_with_comma() {
        rowmatch("\"1,2\"\n",
                 [["1,2"]])
    }

    #[test]
    fn test_quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn test_blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<commit_msg>fix comma in quote bug<commit_after>use std;
import std::io;
import std::io::{writer_util, reader_util};
import std::map;
import result;

export rowreader, rowaccess, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inquotedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mutable offset : uint,
    mutable buffers : [@[char]],
    mutable state : state
};

type row = {
    fields : [ fieldtype ]
};

type bufferdescr = {
    escaped: bool,
    buffers: [@[char]],
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow() -> result::t<row, str>;
}

iface rowaccess {
    fn len() -> uint;
    fn getchars(uint) -> [char];
    fn getstr(uint) -> str;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        readlen: 1024u,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false)
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false)
    }
}

impl of rowaccess for row {
    fn len() -> uint {
        vec::len(self.fields)
    }
    fn getchars(field: uint) -> [char] {
        fn unescape(escaped: [char]) -> [char] {
            let r : [char] = [];
            vec::reserve(r, vec::len(escaped));
            let in_q = false;
            for c in escaped {
                if in_q {
                    assert(c == '"');
                    in_q = false;
                } else {
                    in_q = c == '"';
                    r += [c];
                }
            }
            ret r;
        }
        alt self.fields[field] {
            emptyfield() { ret []; }
            bufferfield(desc) {
                let buf = [];
                {
                    let i = 0u;
                    while i < vec::len(desc.buffers) {
                        let from = if (i == 0u)
                            { desc.start } else { 0u };
                        let to = if (i == vec::len(desc.buffers) - 1u)
                            { desc.end } else { vec::len(*desc.buffers[i]) };
                        buf += vec::slice(*desc.buffers[i], from, to);
                        i = i + 1u;
                    }
                }
                if field == self.len() - 1u {
                    // there may be a trailing \r on the last field; we should strip it
                    // if so. bodgy here but seems the most efficient place to deal with this
                    if vec::len(buf) > 0u {
                        if buf[vec::len(buf)-1u] == '\r' {
                            buf = vec::slice(buf, 0u, vec::len(buf)-1u);
                        }
                    }
                }
                if desc.escaped {
                    buf = unescape(buf);
                }
                ret buf;
            }
        };
    }
    fn getstr(field: uint) -> str {
        ret str::from_chars(self.getchars(field));
    }
    fn getall() -> [str] {
        let a = [];
        self.map() { |s|
            a += [s];
        }
        ret a;
    }
    fn map(f: fn(s: str)) {
        let i = 0u;
        let len = self.len();
        while i < len {
            f(self.getstr(i));
            i += 1u;
        }
    }
}

impl of rowiter for rowreader {
    fn readrow() -> result::t<row, str> {
        fn statestr(state: state) -> str {
            alt state {
                fieldstart(after_delim) {
                    #fmt("fieldstart : after_delim %b", after_delim)
                }
                infield(b,o) {
                    #fmt("field : %u %u", b, o)
                }
                inquotedfield(b, o) {
                    #fmt("inquotedfield : %u %u", b, o)
                }
                inquote(b, o) {
                    #fmt("inquote : %u %u", b, o)
                }
            }
        }
        fn row_from_buf(self: rowreader, &fields: [fieldtype]) -> bool {
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let eb = vec::len(self.buffers);
                let sb = sb, so = so, eo = eo;
                if escaped {
                    so += 1u;
                    if so > vec::len(*self.buffers[sb]) {
                        sb += 1u;
                        so = vec::len(*self.buffers[sb]) - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = vec::len(*self.buffers[sb]) - 1u;
                    }
                }
                bufferfield({ escaped: escaped, buffers: vec::slice(self.buffers, sb, eb), start: so, end: eo })
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf: @[char] = self.buffers[cbuffer];
            while self.offset < vec::len(*buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                #debug("-> %c | %s", c, statestr(self.state));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        #debug("fieldstart : after_delim %b", after_delim);
                        if c == self.quote {
                            self.state = inquotedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [emptyfield];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [emptyfield];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        #debug("field : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                        }
                    }
                    inquotedfield(b, o) {
                        #debug("inquotedfield : %u %u", b, o);
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                    }
                    inquote(b, o) {
                        #debug("inquote : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                            ret true;
                        } else if c == self.quote {
                            self.state = inquotedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                #debug(".... %s", statestr(self.state));
            }
            ret false;
        }

        self.state = fieldstart(false);
        let do_read = vec::len(self.buffers) == 0u;
        let fields = [];
        while true {
            if do_read {
                let data: @[char] = @self.f.read_chars(self.readlen);
                if vec::len(*data) == 0u {
                    ret result::err("EOF");
                }
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, fields) {
                let r: row = { fields: fields };
                fields = [];
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                ret result::ok(r);
            }
            do_read = true;
        }
        ret result::err("unreachable");
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    while true {
        let res = r.readrow();
        if result::failure(res) {
            break;
        }
        let m : map::hashmap<str, str> = map::new_str_hash();
        let col = 0u;
        let row = result::get(res);
        if row.len() != vec::len(cols) {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        result::get(res).map() { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            hashmap_iter_cols(r, result::get(res).getall(), f);
        }
        result::err(_) { }
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            let cols : [str] = vec::map(result::get(res).getall(), hmap);
            if !hver(cols) {
                ret;
            }
            hashmap_iter_cols(r, cols, f);
        }
        result::err(_) { }
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(testdata);
            let r = mk(f);
            let i = 0u;
            while true {
                let res = r.readrow();
                if result::failure(res) {
                    break;
                }
                let row = result::get(res);
                let expect = expected[i];

                assert(row.len() == vec::len(expect));
                let j = 0u;
                while j < row.len() {
                    assert(row.getstr(j) == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(testdata: str) {
            // test default reader params
            chk() { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test continuations over read buffers
            let j = 1u;
            while j < str::len(testdata) {
                chk() { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
        };
        runchecks(testdata);
        runchecks(str::replace(testdata, "\n", "\r\n"));
    }

    #[test]
    fn test_simple() {
        rowmatch("a,b,c,d\n1,2,3,4\n",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn test_trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,\n",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn test_leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4\n",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn test_quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d\n",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d\n",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_with_comma() {
        rowmatch("\"1,2\"\n",
                 [["1,2"]])
    }

    #[test]
    fn test_quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"\n",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn test_blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<|endoftext|>


<commit_before>//! Compares the decoding results with reference renderings.

extern crate crc32fast;
extern crate glob;
extern crate image;

use std::fs;
use std::io;
use std::path::PathBuf;
use std::u32;

use crc32fast::Hasher as Crc32;

const BASE_PATH: [&str; 2] = [".", "tests"];
const IMAGE_DIR: &str = "images";
const OUTPUT_DIR: &str = "output";
const REFERENCE_DIR: &str = "reference";

fn process_images<F>(dir: &str, input_decoder: Option<&str>, func: F)
where
    F: Fn(&PathBuf, PathBuf, &str),
{
    let base: PathBuf = BASE_PATH.iter().collect();
    let decoders = &["tga", "tiff", "png", "gif", "bmp", "ico", "jpg", "hdr", "pbm", "webp"];
    for decoder in decoders {
        let mut path = base.clone();
        path.push(dir);
        path.push(decoder);
        path.push("**");
        path.push(
            "*.".to_string() + match input_decoder {
                Some(val) => val,
                None => decoder,
            },
        );
        let pattern = &*format!("{}", path.display());
        for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
            func(&base, path, decoder)
        }
    }
}

#[cfg(feature = "png")]
#[test]
fn render_images() {
    process_images(IMAGE_DIR, None, |base, path, decoder| {
        println!("render_images {}", path.display());
        let img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(e)) => {
                println!("UNSUPPORTED {}: {}", path.display(), e);
                return;
            }
            Err(err) => panic!(format!("decoding of {:?} failed with: {}", path, err)),
        };
        let mut crc = Crc32::new();
        crc.update(&*img);

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };
        let mut out_path = base.clone();

        out_path.push(OUTPUT_DIR);
        out_path.push(decoder);
        out_path.push(testsuite.as_os_str());
        fs::create_dir_all(&out_path).unwrap();
        out_path.push(format!(
            "{}.{}.{}",
            filename.as_os_str().to_str().unwrap(),
            format!("{:x}", crc.finalize()),
            "png"
        ));
        img.save(out_path).unwrap();
    })
}

/// Describes a single test case of `check_references`.
struct ReferenceTestCase {
    orig_filename: String,
    crc: u32,
    ty: ReferenceTestCaseType,
}

enum ReferenceTestCaseType {
    /// The test image is loaded using `image::open`, and the result is compared
    /// against the reference image.
    SingleImage,

    /// From the test image file, a single frame is extracted using
    /// `image::gif::Decoder`, and the result is compared against the reference
    /// image.
    AnimatedGifFrame {
        /// A zero-based frame number.
        frame: usize,
    },
}

impl std::str::FromStr for ReferenceTestCase {
    type Err = &'static str;

    /// Construct `ReferenceTestCase` from the file name of a reference
    /// image.
    fn from_str(filename: &str) -> Result<Self, Self::Err> {
        let mut filename_parts = filename.rsplitn(3, '.');

        // Ignore the file extension
        filename_parts.next().unwrap();

        // The penultimate part of `filename_parts` represents the metadata,
        // describing the test type and other details.
        let meta_str = filename_parts.next().ok_or("missing metadata part")?;
        let meta = meta_str.split('_').collect::<Vec<_>>();
        let (crc, ty);

        if meta.len() == 1 {
            // `CRC`
            crc = parse_crc(&meta[0]).ok_or("malformed CRC")?;
            ty = ReferenceTestCaseType::SingleImage;
        } else if meta.len() == 3 && meta[0] == "anim" {
            // `anim_FRAME_CRC`
            crc = parse_crc(&meta[2]).ok_or("malformed CRC")?;
            let frame: usize = meta[1].parse().map_err(|_| "malformed frame number")?;
            ty = ReferenceTestCaseType::AnimatedGifFrame {
                frame: frame.checked_sub(1).ok_or("frame number must be 1-based")?,
            };
        } else {
            return Err("unrecognized reference image metadata format");
        }

        // The remaining part represents the original file name
        let orig_filename = filename_parts
            .next()
            .ok_or("missing original file name")?
            .to_owned();

        Ok(Self {
            orig_filename,
            crc,
            ty,
        })
    }
}

/// Parse the given string as a hexadecimal CRC hash, used by `check_references`.
fn parse_crc(src: &str) -> Option<u32> {
    u32::from_str_radix(src, 16).ok()
}

#[test]
fn check_references() {
    process_images(REFERENCE_DIR, Some("png"), |base, path, decoder| {
        println!("check_references {}", path.display());

        let ref_img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(_)) => return,
            Err(err) => panic!(format!("{}", err)),
        };

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };

        // Parse the file name to obtain the test case information
        let filename_str = filename.as_os_str().to_str().unwrap();
        let case: ReferenceTestCase = filename_str.parse().unwrap();

        // Check the reference image's CRC
        let ref_crc = case.crc;

        let ref_crc_actual = {
            let mut hasher = Crc32::new();
            hasher.update(&*ref_img);
            hasher.finalize()
        };

        if ref_crc_actual != ref_crc {
            panic!(
                "Reference rendering hash check failed (expected = {:08x}, actual = {:08x}).",
                ref_crc, ref_crc_actual
            );
        }

        let mut img_path = base.clone();
        img_path.push(IMAGE_DIR);
        img_path.push(decoder);
        img_path.push(testsuite.as_os_str());
        img_path.push(case.orig_filename);

        // Load the test image
        let test_img;

        match case.ty {
            ReferenceTestCaseType::AnimatedGifFrame { frame: frame_num } => {
                #[cfg(feature = "gif_codec")]
                {
                    // Interpret the input file as an animation file
                    use image::AnimationDecoder;
                    let stream = io::BufReader::new(fs::File::open(&img_path).unwrap());
                    let decoder = match image::gif::Decoder::new(stream) {
                        Ok(decoder) => decoder,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => {
                            panic!(format!("decoding of {:?} failed with: {}", img_path, err))
                        }
                    };

                    let mut frames = match decoder.into_frames().collect_frames() {
                        Ok(frames) => frames,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => panic!(format!(
                            "collecting frames of {:?} failed with: {}",
                            img_path, err
                        )),
                    };

                    // Select a single frame
                    let frame = frames.drain(frame_num..).nth(0).unwrap();

                    // Convert the frame to a`RgbaImage`
                    test_img = frame.into_buffer();
                }

                #[cfg(not(feature = "gif_codec"))]
                {
                    println!("Skipping - GIF codec is not enabled");
                    return;
                }
            }

            ReferenceTestCaseType::SingleImage => {
                // Read the input file as a single image
                match image::open(&img_path) {
                    Ok(img) => test_img = img.to_rgba(),
                    // Do not fail on unsupported error
                    // This might happen because the testsuite contains unsupported images
                    // or because a specific decoder included via a feature.
                    Err(image::ImageError::UnsupportedError(_)) => return,
                    Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
                };
            }
        }

        if *ref_img != *test_img {
            panic!("Reference rendering does not match.");
        }
    })
}

#[cfg(feature = "hdr")]
#[test]
fn check_hdr_references() {
    let mut ref_path: PathBuf = BASE_PATH.iter().collect();
    ref_path.push(REFERENCE_DIR);
    ref_path.push("hdr");
    let mut path: PathBuf = BASE_PATH.iter().collect();
    path.push(IMAGE_DIR);
    path.push("hdr");
    path.push("*");
    path.push("*.hdr");
    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        use std::path::Component::Normal;
        let mut ref_path = ref_path.clone();
        // append 2 last components of image path to reference path
        for c in path.components()
            .rev()
            .take(2)
            .collect::<Vec<_>>()
            .iter()
            .rev()
        {
            match *c {
                Normal(name) => ref_path.push(name),
                _ => panic!(),
            }
        }
        ref_path.set_extension("raw");
        println!("{}", ref_path.display());
        println!("{}", path.display());
        let decoder = image::hdr::HDRDecoder::new(io::BufReader::new(
            fs::File::open(&path).unwrap(),
        )).unwrap();
        let decoded = decoder.read_image_hdr().unwrap();
        let reference = image::hdr::read_raw_file(&ref_path).unwrap();
        assert_eq!(decoded, reference);
    }
}

/// Check that BMP files with large values could cause OOM issues are rejected.
///
/// The images are postfixed with `bad_bmp` to not be loaded by the other test.
#[test]
fn bad_bmps() {
    let path: PathBuf = BASE_PATH
        .iter()
        .collect::<PathBuf>()
        .join(IMAGE_DIR)
        .join("bmp/images")
        .join("*.bad_bmp");

    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        let im = image::open(path);
        assert!(im.is_err());
    }
}
<commit_msg>Check the CRC of test images, not reference images<commit_after>//! Compares the decoding results with reference renderings.

extern crate crc32fast;
extern crate glob;
extern crate image;

use std::fs;
use std::io;
use std::path::PathBuf;
use std::u32;

use crc32fast::Hasher as Crc32;

const BASE_PATH: [&str; 2] = [".", "tests"];
const IMAGE_DIR: &str = "images";
const OUTPUT_DIR: &str = "output";
const REFERENCE_DIR: &str = "reference";

fn process_images<F>(dir: &str, input_decoder: Option<&str>, func: F)
where
    F: Fn(&PathBuf, PathBuf, &str),
{
    let base: PathBuf = BASE_PATH.iter().collect();
    let decoders = &["tga", "tiff", "png", "gif", "bmp", "ico", "jpg", "hdr", "pbm", "webp"];
    for decoder in decoders {
        let mut path = base.clone();
        path.push(dir);
        path.push(decoder);
        path.push("**");
        path.push(
            "*.".to_string() + match input_decoder {
                Some(val) => val,
                None => decoder,
            },
        );
        let pattern = &*format!("{}", path.display());
        for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
            func(&base, path, decoder)
        }
    }
}

#[cfg(feature = "png")]
#[test]
fn render_images() {
    process_images(IMAGE_DIR, None, |base, path, decoder| {
        println!("render_images {}", path.display());
        let img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(e)) => {
                println!("UNSUPPORTED {}: {}", path.display(), e);
                return;
            }
            Err(err) => panic!(format!("decoding of {:?} failed with: {}", path, err)),
        };
        let mut crc = Crc32::new();
        crc.update(&*img);

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };
        let mut out_path = base.clone();

        out_path.push(OUTPUT_DIR);
        out_path.push(decoder);
        out_path.push(testsuite.as_os_str());
        fs::create_dir_all(&out_path).unwrap();
        out_path.push(format!(
            "{}.{}.{}",
            filename.as_os_str().to_str().unwrap(),
            format!("{:x}", crc.finalize()),
            "png"
        ));
        img.save(out_path).unwrap();
    })
}

/// Describes a single test case of `check_references`.
struct ReferenceTestCase {
    orig_filename: String,
    crc: u32,
    ty: ReferenceTestCaseType,
}

enum ReferenceTestCaseType {
    /// The test image is loaded using `image::open`, and the result is compared
    /// against the reference image.
    SingleImage,

    /// From the test image file, a single frame is extracted using
    /// `image::gif::Decoder`, and the result is compared against the reference
    /// image.
    AnimatedGifFrame {
        /// A zero-based frame number.
        frame: usize,
    },
}

impl std::str::FromStr for ReferenceTestCase {
    type Err = &'static str;

    /// Construct `ReferenceTestCase` from the file name of a reference
    /// image.
    fn from_str(filename: &str) -> Result<Self, Self::Err> {
        let mut filename_parts = filename.rsplitn(3, '.');

        // Ignore the file extension
        filename_parts.next().unwrap();

        // The penultimate part of `filename_parts` represents the metadata,
        // describing the test type and other details.
        let meta_str = filename_parts.next().ok_or("missing metadata part")?;
        let meta = meta_str.split('_').collect::<Vec<_>>();
        let (crc, ty);

        if meta.len() == 1 {
            // `CRC`
            crc = parse_crc(&meta[0]).ok_or("malformed CRC")?;
            ty = ReferenceTestCaseType::SingleImage;
        } else if meta.len() == 3 && meta[0] == "anim" {
            // `anim_FRAME_CRC`
            crc = parse_crc(&meta[2]).ok_or("malformed CRC")?;
            let frame: usize = meta[1].parse().map_err(|_| "malformed frame number")?;
            ty = ReferenceTestCaseType::AnimatedGifFrame {
                frame: frame.checked_sub(1).ok_or("frame number must be 1-based")?,
            };
        } else {
            return Err("unrecognized reference image metadata format");
        }

        // The remaining part represents the original file name
        let orig_filename = filename_parts
            .next()
            .ok_or("missing original file name")?
            .to_owned();

        Ok(Self {
            orig_filename,
            crc,
            ty,
        })
    }
}

/// Parse the given string as a hexadecimal CRC hash, used by `check_references`.
fn parse_crc(src: &str) -> Option<u32> {
    u32::from_str_radix(src, 16).ok()
}

#[test]
fn check_references() {
    process_images(REFERENCE_DIR, Some("png"), |base, path, decoder| {
        println!("check_references {}", path.display());

        let ref_img = match image::open(&path) {
            Ok(img) => img.to_rgba(),
            // Do not fail on unsupported error
            // This might happen because the testsuite contains unsupported images
            // or because a specific decoder included via a feature.
            Err(image::ImageError::UnsupportedError(_)) => return,
            Err(err) => panic!(format!("{}", err)),
        };

        let (filename, testsuite) = {
            let mut path: Vec<_> = path.components().collect();
            (path.pop().unwrap(), path.pop().unwrap())
        };

        // Parse the file name to obtain the test case information
        let filename_str = filename.as_os_str().to_str().unwrap();
        let case: ReferenceTestCase = filename_str.parse().unwrap();

        let mut img_path = base.clone();
        img_path.push(IMAGE_DIR);
        img_path.push(decoder);
        img_path.push(testsuite.as_os_str());
        img_path.push(case.orig_filename);

        // Load the test image
        let test_img;

        match case.ty {
            ReferenceTestCaseType::AnimatedGifFrame { frame: frame_num } => {
                #[cfg(feature = "gif_codec")]
                {
                    // Interpret the input file as an animation file
                    use image::AnimationDecoder;
                    let stream = io::BufReader::new(fs::File::open(&img_path).unwrap());
                    let decoder = match image::gif::Decoder::new(stream) {
                        Ok(decoder) => decoder,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => {
                            panic!(format!("decoding of {:?} failed with: {}", img_path, err))
                        }
                    };

                    let mut frames = match decoder.into_frames().collect_frames() {
                        Ok(frames) => frames,
                        Err(image::ImageError::UnsupportedError(_)) => return,
                        Err(err) => panic!(format!(
                            "collecting frames of {:?} failed with: {}",
                            img_path, err
                        )),
                    };

                    // Select a single frame
                    let frame = frames.drain(frame_num..).nth(0).unwrap();

                    // Convert the frame to a`RgbaImage`
                    test_img = frame.into_buffer();
                }

                #[cfg(not(feature = "gif_codec"))]
                {
                    println!("Skipping - GIF codec is not enabled");
                    return;
                }
            }

            ReferenceTestCaseType::SingleImage => {
                // Read the input file as a single image
                match image::open(&img_path) {
                    Ok(img) => test_img = img.to_rgba(),
                    // Do not fail on unsupported error
                    // This might happen because the testsuite contains unsupported images
                    // or because a specific decoder included via a feature.
                    Err(image::ImageError::UnsupportedError(_)) => return,
                    Err(err) => panic!(format!("decoding of {:?} failed with: {}", img_path, err)),
                };
            }
        }

        let test_crc_actual = {
            let mut hasher = Crc32::new();
            hasher.update(&*test_img);
            hasher.finalize()
        };

        if test_crc_actual != case.crc {
            panic!(
                "The decoded image's hash does not match (expected = {:08x}, actual = {:08x}).",
                case.crc, test_crc_actual
            );
        }

        if *ref_img != *test_img {
            panic!("Reference rendering does not match.");
        }
    })
}

#[cfg(feature = "hdr")]
#[test]
fn check_hdr_references() {
    let mut ref_path: PathBuf = BASE_PATH.iter().collect();
    ref_path.push(REFERENCE_DIR);
    ref_path.push("hdr");
    let mut path: PathBuf = BASE_PATH.iter().collect();
    path.push(IMAGE_DIR);
    path.push("hdr");
    path.push("*");
    path.push("*.hdr");
    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        use std::path::Component::Normal;
        let mut ref_path = ref_path.clone();
        // append 2 last components of image path to reference path
        for c in path.components()
            .rev()
            .take(2)
            .collect::<Vec<_>>()
            .iter()
            .rev()
        {
            match *c {
                Normal(name) => ref_path.push(name),
                _ => panic!(),
            }
        }
        ref_path.set_extension("raw");
        println!("{}", ref_path.display());
        println!("{}", path.display());
        let decoder = image::hdr::HDRDecoder::new(io::BufReader::new(
            fs::File::open(&path).unwrap(),
        )).unwrap();
        let decoded = decoder.read_image_hdr().unwrap();
        let reference = image::hdr::read_raw_file(&ref_path).unwrap();
        assert_eq!(decoded, reference);
    }
}

/// Check that BMP files with large values could cause OOM issues are rejected.
///
/// The images are postfixed with `bad_bmp` to not be loaded by the other test.
#[test]
fn bad_bmps() {
    let path: PathBuf = BASE_PATH
        .iter()
        .collect::<PathBuf>()
        .join(IMAGE_DIR)
        .join("bmp/images")
        .join("*.bad_bmp");

    let pattern = &*format!("{}", path.display());
    for path in glob::glob(pattern).unwrap().filter_map(Result::ok) {
        let im = image::open(path);
        assert!(im.is_err());
    }
}
<|endoftext|>


<commit_before>use {Future, IntoFuture, Poll, Async};
use core::result;

/// A future representing a value that is immediately ready.
///
/// Created by the `result` function.
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless polled"]
pub struct Result<T, E> {
    inner: Option<result::Result<T, E>>,
}

impl<T, E> IntoFuture for result::Result<T, E> {
    type Future = Result<T, E>;
    type Item = T;
    type Error = E;

    fn into_future(self) -> Result<T, E> {
        result(self)
    }
}

/// Creates a new "leaf future" which will resolve with the given result.
///
/// The returned future represents a computation which is finished immediately.
/// This can be useful with the `finished` and `failed` base future types to
/// convert an immediate value to a future to interoperate elsewhere.
///
/// # Examples
///
/// ```
/// use futures::future::*;
///
/// let future_of_1 = result::<u32, u32>(Ok(1));
/// let future_of_err_2 = result::<u32, u32>(Err(2));
/// ```
pub fn result<T, E>(r: result::Result<T, E>) -> Result<T, E> {
    Result { inner: Some(r) }
}

/// Creates a "leaf future" from an immediate value of a finished and
/// successful computation.
///
/// The returned future is similar to `result` where it will immediately run a
/// scheduled callback with the provided value.
///
/// # Examples
///
/// ```
/// use futures::future::*;
///
/// let future_of_1 = ok::<u32, u32>(1);
/// ```
pub fn ok<T, E>(t: T) -> Result<T, E> {
    result(Ok(t))
}

/// Creates a "leaf future" from an immediate value of a failed computation.
///
/// The returned future is similar to `result` where it will immediately run a
/// scheduled callback with the provided value.
///
/// # Examples
///
/// ```
/// use futures::future::*;
///
/// let future_of_err_1 = err::<u32, u32>(1);
/// ```
pub fn err<T, E>(e: E) -> Result<T, E> {
    result(Err(e))
}

impl<T, E> Future for Result<T, E> {
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        self.inner.take().expect("cannot poll Result twice").map(Async::Ready)
    }
}

impl<T, E> From<result::Result<T, E>> for Result<T, E> {
    fn from(r: result::Result<T, E>) -> Self {
        result(r)
    }
}
<commit_msg>Fix futures-core doctests<commit_after>use {Future, IntoFuture, Poll, Async};
use core::result;

/// A future representing a value that is immediately ready.
///
/// Created by the `result` function.
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless polled"]
pub struct Result<T, E> {
    inner: Option<result::Result<T, E>>,
}

impl<T, E> IntoFuture for result::Result<T, E> {
    type Future = Result<T, E>;
    type Item = T;
    type Error = E;

    fn into_future(self) -> Result<T, E> {
        result(self)
    }
}

/// Creates a new "leaf future" which will resolve with the given result.
///
/// The returned future represents a computation which is finished immediately.
/// This can be useful with the `finished` and `failed` base future types to
/// convert an immediate value to a future to interoperate elsewhere.
///
/// # Examples
///
/// ```
/// use futures_core::future::*;
///
/// let future_of_1 = result::<u32, u32>(Ok(1));
/// let future_of_err_2 = result::<u32, u32>(Err(2));
/// ```
pub fn result<T, E>(r: result::Result<T, E>) -> Result<T, E> {
    Result { inner: Some(r) }
}

/// Creates a "leaf future" from an immediate value of a finished and
/// successful computation.
///
/// The returned future is similar to `result` where it will immediately run a
/// scheduled callback with the provided value.
///
/// # Examples
///
/// ```
/// use futures_core::future::*;
///
/// let future_of_1 = ok::<u32, u32>(1);
/// ```
pub fn ok<T, E>(t: T) -> Result<T, E> {
    result(Ok(t))
}

/// Creates a "leaf future" from an immediate value of a failed computation.
///
/// The returned future is similar to `result` where it will immediately run a
/// scheduled callback with the provided value.
///
/// # Examples
///
/// ```
/// use futures_core::future::*;
///
/// let future_of_err_1 = err::<u32, u32>(1);
/// ```
pub fn err<T, E>(e: E) -> Result<T, E> {
    result(Err(e))
}

impl<T, E> Future for Result<T, E> {
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        self.inner.take().expect("cannot poll Result twice").map(Async::Ready)
    }
}

impl<T, E> From<result::Result<T, E>> for Result<T, E> {
    fn from(r: result::Result<T, E>) -> Self {
        result(r)
    }
}
<|endoftext|>