<commit_before>//! Websocket integration
use std::collections::VecDeque;
use std::io;

use actix::dev::{
    AsyncContextParts, ContextFut, ContextParts, Envelope, Mailbox, StreamHandler,
    ToEnvelope,
};
use actix::fut::ActorFuture;
use actix::{
    Actor, ActorContext, ActorState, Addr, AsyncContext, Handler,
    Message as ActixMessage, SpawnHandle,
};
use actix_codec::{Decoder, Encoder};
use actix_http::ws::{hash_key, Codec};
pub use actix_http::ws::{
    CloseCode, CloseReason, Frame, HandshakeError, Message, ProtocolError,
};

use actix_web::dev::{Head, HttpResponseBuilder};
use actix_web::error::{Error, ErrorInternalServerError, PayloadError};
use actix_web::http::{header, Method, StatusCode};
use actix_web::{HttpMessage, HttpRequest, HttpResponse};
use bytes::{Bytes, BytesMut};
use futures::sync::oneshot::Sender;
use futures::{Async, Future, Poll, Stream};

/// Do websocket handshake and start ws actor.
pub fn start(actor: A, req: &HttpRequest, stream: T) -> Result<HttpResponse, Error>
where
    A: Actor<Context = WebsocketContext> + StreamHandler<Message, ProtocolError>,
    T: Stream<Item = Bytes, Error = PayloadError> + 'static,
{
    let mut res = handshake(req)?;
    Ok(res.streaming(WebsocketContext::create(actor, stream)))
}

/// Prepare `WebSocket` handshake response.
///
/// This function returns handshake `HttpResponse`, ready to send to peer.
/// It does not perform any IO.
///
// /// `protocols` is a sequence of known protocols. On successful handshake,
// /// the returned response headers contain the first protocol in this list
// /// which the server also knows.
pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeError> {
    // WebSocket accepts only GET
    if *req.method() != Method::GET {
        return Err(HandshakeError::GetMethodRequired);
    }

    // Check for "UPGRADE" to websocket header
    let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {
        if let Ok(s) = hdr.to_str() {
            s.to_lowercase().contains("websocket")
        } else {
            false
        }
    } else {
        false
    };
    if !has_hdr {
        return Err(HandshakeError::NoWebsocketUpgrade);
    }

    // Upgrade connection
    if !req.upgrade() {
        return Err(HandshakeError::NoConnectionUpgrade);
    }

    // check supported version
    if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
        return Err(HandshakeError::NoVersionHeader);
    }
    let supported_ver = {
        if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {
            hdr == "13" || hdr == "8" || hdr == "7"
        } else {
            false
        }
    };
    if !supported_ver {
        return Err(HandshakeError::UnsupportedVersion);
    }

    // check client handshake for validity
    if !req.headers().contains_key(header::SEC_WEBSOCKET_KEY) {
        return Err(HandshakeError::BadWebsocketKey);
    }
    let key = {
        let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
        hash_key(key.as_ref())
    };

    Ok(HttpResponse::build(StatusCode::SWITCHING_PROTOCOLS)
        .header(header::CONNECTION, "upgrade")
        .header(header::UPGRADE, "websocket")
        .header(header::TRANSFER_ENCODING, "chunked")
        .header(header::SEC_WEBSOCKET_ACCEPT, key.as_str())
        .take())
}

/// Execution context for `WebSockets` actors
pub struct WebsocketContext
where
    A: Actor<Context = WebsocketContext>,
{
    inner: ContextParts,
    messages: VecDeque<Option<Message>>,
}

impl ActorContext for WebsocketContext
where
    A: Actor<Context = Self>,
{
    fn stop(&mut self) {
        self.inner.stop();
    }

    fn terminate(&mut self) {
        self.inner.terminate()
    }

    fn state(&self) -> ActorState {
        self.inner.state()
    }
}

impl AsyncContext for WebsocketContext
where
    A: Actor<Context = Self>,
{
    fn spawn<F>(&mut self, fut: F) -> SpawnHandle
    where
        F: ActorFuture<Item = (), Error = (), Actor = A> + 'static,
    {
        self.inner.spawn(fut)
    }

    fn wait<F>(&mut self, fut: F)
    where
        F: ActorFuture<Item = (), Error = (), Actor = A> + 'static,
    {
        self.inner.wait(fut)
    }

    #[doc(hidden)]
    #[inline]
    fn waiting(&self) -> bool {
        self.inner.waiting()
            || self.inner.state() == ActorState::Stopping
            || self.inner.state() == ActorState::Stopped
    }

    fn cancel_future(&mut self, handle: SpawnHandle) -> bool {
        self.inner.cancel_future(handle)
    }

    #[inline]
    fn address(&self) -> Addr {
        self.inner.address()
    }
}

impl WebsocketContext
where
    A: Actor<Context = Self>,
{
    #[inline]
    /// Create a new Websocket context from a request and an actor
    pub fn create<S>(actor: A, stream: S) -> impl Stream<Item = Bytes, Error = Error>
    where
        A: StreamHandler<Message, ProtocolError>,
        S: Stream<Item = Bytes, Error = PayloadError> + 'static,
    {
        let mb = Mailbox::default();
        let mut ctx = WebsocketContext {
            inner: ContextParts::new(mb.sender_producer()),
            messages: VecDeque::new(),
        };
        ctx.add_stream(WsStream::new(stream));

        WebsocketContextFut::new(ctx, actor, mb)
    }

    /// Create a new Websocket context
    pub fn with_factory<S, F>(
        stream: S,
        f: F,
    ) -> impl Stream<Item = Bytes, Error = Error>
    where
        F: FnOnce(&mut Self) -> A + 'static,
        A: StreamHandler<Message, ProtocolError>,
        S: Stream<Item = Bytes, Error = PayloadError> + 'static,
    {
        let mb = Mailbox::default();
        let mut ctx = WebsocketContext {
            inner: ContextParts::new(mb.sender_producer()),
            messages: VecDeque::new(),
        };
        ctx.add_stream(WsStream::new(stream));

        let act = f(&mut ctx);

        WebsocketContextFut::new(ctx, act, mb)
    }
}

impl WebsocketContext
where
    A: Actor<Context = Self>,
{
    /// Write payload
    ///
    /// This is a low-level function that accepts framed messages that should
    /// be created using `Frame::message()`. If you want to send text or binary
    /// data you should prefer the `text()` or `binary()` convenience functions
    /// that handle the framing for you.
    #[inline]
    pub fn write_raw(&mut self, msg: Message) {
        self.messages.push_back(Some(msg));
    }

    /// Send text frame
    #[inline]
    pub fn text<T: Into<String>>(&mut self, text: T) {
        self.write_raw(Message::Text(text.into()));
    }

    /// Send binary frame
    #[inline]
    pub fn binary<B: Into<Bytes>>(&mut self, data: B) {
        self.write_raw(Message::Binary(data.into()));
    }

    /// Send ping frame
    #[inline]
    pub fn ping(&mut self, message: &str) {
        self.write_raw(Message::Ping(message.to_string()));
    }

    /// Send pong frame
    #[inline]
    pub fn pong(&mut self, message: &str) {
        self.write_raw(Message::Pong(message.to_string()));
    }

    /// Send close frame
    #[inline]
    pub fn close(&mut self, reason: Option<CloseReason>) {
        self.write_raw(Message::Close(reason));
    }

    /// Handle of the running future
    ///
    /// SpawnHandle is the handle returned by `AsyncContext::spawn()` method.
    pub fn handle(&self) -> SpawnHandle {
        self.inner.curr_handle()
    }

    /// Set mailbox capacity
    ///
    /// By default mailbox capacity is 16 messages.
    pub fn set_mailbox_capacity(&mut self, cap: usize) {
        self.inner.set_mailbox_capacity(cap)
    }
}

impl AsyncContextParts for WebsocketContext
where
    A: Actor<Context = Self>,
{
    fn parts(&mut self) -> &mut ContextParts {
        &mut self.inner
    }
}

struct WebsocketContextFut
where
    A: Actor<Context = WebsocketContext>,
{
    fut: ContextFut<A, WebsocketContext>,
    encoder: Codec,
    buf: BytesMut,
    closed: bool,
}

impl WebsocketContextFut
where
    A: Actor<Context = WebsocketContext>,
{
    fn new(ctx: WebsocketContext, act: A, mailbox: Mailbox) -> Self {
        let fut = ContextFut::new(ctx, act, mailbox);
        WebsocketContextFut {
            fut,
            encoder: Codec::new(),
            buf: BytesMut::new(),
            closed: false,
        }
    }
}

impl Stream for WebsocketContextFut
where
    A: Actor<Context = WebsocketContext>,
{
    type Item = Bytes;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Bytes>, Error> {
        if self.fut.alive() && self.fut.poll().is_err() {
            return Err(ErrorInternalServerError("error"));
        }

        // encode messages
        while let Some(item) = self.fut.ctx().messages.pop_front() {
            if let Some(msg) = item {
                self.encoder.encode(msg, &mut self.buf)?;
            } else {
                self.closed = true;
                break;
            }
        }

        if !self.buf.is_empty() {
            Ok(Async::Ready(Some(self.buf.take().freeze())))
        } else if self.fut.alive() && !self.closed {
            Ok(Async::NotReady)
        } else {
            Ok(Async::Ready(None))
        }
    }
}

impl ToEnvelope for WebsocketContext
where
    A: Actor<Context = WebsocketContext> + Handler<M>,
    M: ActixMessage + Send + 'static,
    M::Result: Send,
{
    fn pack(msg: M, tx: Option<Sender<M::Result>>) -> Envelope {
        Envelope::new(msg, tx)
    }
}

struct WsStream<S> {
    stream: S,
    decoder: Codec,
    buf: BytesMut,
    closed: bool,
}

impl<S> WsStream<S>
where
    S: Stream<Item = Bytes, Error = PayloadError>,
{
    fn new(stream: S) -> Self {
        Self {
            stream,
            decoder: Codec::new(),
            buf: BytesMut::new(),
            closed: false,
        }
    }
}

impl<S> Stream for WsStream<S>
where
    S: Stream<Item = Bytes, Error = PayloadError>,
{
    type Item = Message;
    type Error = ProtocolError;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if !self.closed {
            loop {
                match self.stream.poll() {
                    Ok(Async::Ready(Some(chunk))) => {
                        self.buf.extend_from_slice(&chunk[..]);
                    }
                    Ok(Async::Ready(None)) => {
                        self.closed = true;
                        break;
                    }
                    Ok(Async::NotReady) => break,
                    Err(e) => {
                        return Err(ProtocolError::Io(io::Error::new(
                            io::ErrorKind::Other,
                            format!("{}", e),
                        )));
                    }
                }
            }
        }

        match self.decoder.decode(&mut self.buf)? {
            None => {
                if self.closed {
                    Ok(Async::Ready(None))
                } else {
                    Ok(Async::NotReady)
                }
            }
            Some(frm) => {
                let msg = match frm {
                    Frame::Text(data) => {
                        if let Some(data) = data {
                            Message::Text(
                                std::str::from_utf8(&data)
                                    .map_err(|_| ProtocolError::BadEncoding)?
                                    .to_string(),
                            )
                        } else {
                            Message::Text(String::new())
                        }
                    }
                    Frame::Binary(data) => Message::Binary(
                        data.map(|b| b.freeze()).unwrap_or_else(|| Bytes::new()),
                    ),
                    Frame::Ping(s) => Message::Ping(s),
                    Frame::Pong(s) => Message::Pong(s),
                    Frame::Close(reason) => Message::Close(reason),
                };
                Ok(Async::Ready(Some(msg)))
            }
        }
    }
}
<commit_msg>add handshake test<commit_after>//! Websocket integration
use std::collections::VecDeque;
use std::io;

use actix::dev::{
    AsyncContextParts, ContextFut, ContextParts, Envelope, Mailbox, StreamHandler,
    ToEnvelope,
};
use actix::fut::ActorFuture;
use actix::{
    Actor, ActorContext, ActorState, Addr, AsyncContext, Handler,
    Message as ActixMessage, SpawnHandle,
};
use actix_codec::{Decoder, Encoder};
use actix_http::ws::{hash_key, Codec};
pub use actix_http::ws::{
    CloseCode, CloseReason, Frame, HandshakeError, Message, ProtocolError,
};

use actix_web::dev::{Head, HttpResponseBuilder};
use actix_web::error::{Error, ErrorInternalServerError, PayloadError};
use actix_web::http::{header, Method, StatusCode};
use actix_web::{HttpMessage, HttpRequest, HttpResponse};
use bytes::{Bytes, BytesMut};
use futures::sync::oneshot::Sender;
use futures::{Async, Future, Poll, Stream};

/// Do websocket handshake and start ws actor.
pub fn start(actor: A, req: &HttpRequest, stream: T) -> Result<HttpResponse, Error>
where
    A: Actor<Context = WebsocketContext> + StreamHandler<Message, ProtocolError>,
    T: Stream<Item = Bytes, Error = PayloadError> + 'static,
{
    let mut res = handshake(req)?;
    Ok(res.streaming(WebsocketContext::create(actor, stream)))
}

/// Prepare `WebSocket` handshake response.
///
/// This function returns handshake `HttpResponse`, ready to send to peer.
/// It does not perform any IO.
///
// /// `protocols` is a sequence of known protocols. On successful handshake,
// /// the returned response headers contain the first protocol in this list
// /// which the server also knows.
pub fn handshake(req: &HttpRequest) -> Result<HttpResponseBuilder, HandshakeError> {
    // WebSocket accepts only GET
    if *req.method() != Method::GET {
        return Err(HandshakeError::GetMethodRequired);
    }

    // Check for "UPGRADE" to websocket header
    let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {
        if let Ok(s) = hdr.to_str() {
            s.to_ascii_lowercase().contains("websocket")
        } else {
            false
        }
    } else {
        false
    };
    if !has_hdr {
        return Err(HandshakeError::NoWebsocketUpgrade);
    }

    // Upgrade connection
    if !req.upgrade() {
        return Err(HandshakeError::NoConnectionUpgrade);
    }

    // check supported version
    if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
        return Err(HandshakeError::NoVersionHeader);
    }
    let supported_ver = {
        if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {
            hdr == "13" || hdr == "8" || hdr == "7"
        } else {
            false
        }
    };
    if !supported_ver {
        return Err(HandshakeError::UnsupportedVersion);
    }

    // check client handshake for validity
    if !req.headers().contains_key(header::SEC_WEBSOCKET_KEY) {
        return Err(HandshakeError::BadWebsocketKey);
    }
    let key = {
        let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
        hash_key(key.as_ref())
    };

    Ok(HttpResponse::build(StatusCode::SWITCHING_PROTOCOLS)
        .header(header::CONNECTION, "upgrade")
        .header(header::UPGRADE, "websocket")
        .header(header::TRANSFER_ENCODING, "chunked")
        .header(header::SEC_WEBSOCKET_ACCEPT, key.as_str())
        .take())
}

/// Execution context for `WebSockets` actors
pub struct WebsocketContext
where
    A: Actor<Context = WebsocketContext>,
{
    inner: ContextParts,
    messages: VecDeque<Option<Message>>,
}

impl ActorContext for WebsocketContext
where
    A: Actor<Context = Self>,
{
    fn stop(&mut self) {
        self.inner.stop();
    }

    fn terminate(&mut self) {
        self.inner.terminate()
    }

    fn state(&self) -> ActorState {
        self.inner.state()
    }
}

impl AsyncContext for WebsocketContext
where
    A: Actor<Context = Self>,
{
    fn spawn<F>(&mut self, fut: F) -> SpawnHandle
    where
        F: ActorFuture<Item = (), Error = (), Actor = A> + 'static,
    {
        self.inner.spawn(fut)
    }

    fn wait<F>(&mut self, fut: F)
    where
        F: ActorFuture<Item = (), Error = (), Actor = A> + 'static,
    {
        self.inner.wait(fut)
    }

    #[doc(hidden)]
    #[inline]
    fn waiting(&self) -> bool {
        self.inner.waiting()
            || self.inner.state() == ActorState::Stopping
            || self.inner.state() == ActorState::Stopped
    }

    fn cancel_future(&mut self, handle: SpawnHandle) -> bool {
        self.inner.cancel_future(handle)
    }

    #[inline]
    fn address(&self) -> Addr {
        self.inner.address()
    }
}

impl WebsocketContext
where
    A: Actor<Context = Self>,
{
    #[inline]
    /// Create a new Websocket context from a request and an actor
    pub fn create<S>(actor: A, stream: S) -> impl Stream<Item = Bytes, Error = Error>
    where
        A: StreamHandler<Message, ProtocolError>,
        S: Stream<Item = Bytes, Error = PayloadError> + 'static,
    {
        let mb = Mailbox::default();
        let mut ctx = WebsocketContext {
            inner: ContextParts::new(mb.sender_producer()),
            messages: VecDeque::new(),
        };
        ctx.add_stream(WsStream::new(stream));

        WebsocketContextFut::new(ctx, actor, mb)
    }

    /// Create a new Websocket context
    pub fn with_factory<S, F>(
        stream: S,
        f: F,
    ) -> impl Stream<Item = Bytes, Error = Error>
    where
        F: FnOnce(&mut Self) -> A + 'static,
        A: StreamHandler<Message, ProtocolError>,
        S: Stream<Item = Bytes, Error = PayloadError> + 'static,
    {
        let mb = Mailbox::default();
        let mut ctx = WebsocketContext {
            inner: ContextParts::new(mb.sender_producer()),
            messages: VecDeque::new(),
        };
        ctx.add_stream(WsStream::new(stream));

        let act = f(&mut ctx);

        WebsocketContextFut::new(ctx, act, mb)
    }
}

impl WebsocketContext
where
    A: Actor<Context = Self>,
{
    /// Write payload
    ///
    /// This is a low-level function that accepts framed messages that should
    /// be created using `Frame::message()`. If you want to send text or binary
    /// data you should prefer the `text()` or `binary()` convenience functions
    /// that handle the framing for you.
    #[inline]
    pub fn write_raw(&mut self, msg: Message) {
        self.messages.push_back(Some(msg));
    }

    /// Send text frame
    #[inline]
    pub fn text<T: Into<String>>(&mut self, text: T) {
        self.write_raw(Message::Text(text.into()));
    }

    /// Send binary frame
    #[inline]
    pub fn binary<B: Into<Bytes>>(&mut self, data: B) {
        self.write_raw(Message::Binary(data.into()));
    }

    /// Send ping frame
    #[inline]
    pub fn ping(&mut self, message: &str) {
        self.write_raw(Message::Ping(message.to_string()));
    }

    /// Send pong frame
    #[inline]
    pub fn pong(&mut self, message: &str) {
        self.write_raw(Message::Pong(message.to_string()));
    }

    /// Send close frame
    #[inline]
    pub fn close(&mut self, reason: Option<CloseReason>) {
        self.write_raw(Message::Close(reason));
    }

    /// Handle of the running future
    ///
    /// SpawnHandle is the handle returned by `AsyncContext::spawn()` method.
    pub fn handle(&self) -> SpawnHandle {
        self.inner.curr_handle()
    }

    /// Set mailbox capacity
    ///
    /// By default mailbox capacity is 16 messages.
    pub fn set_mailbox_capacity(&mut self, cap: usize) {
        self.inner.set_mailbox_capacity(cap)
    }
}

impl AsyncContextParts for WebsocketContext
where
    A: Actor<Context = Self>,
{
    fn parts(&mut self) -> &mut ContextParts {
        &mut self.inner
    }
}

struct WebsocketContextFut
where
    A: Actor<Context = WebsocketContext>,
{
    fut: ContextFut<A, WebsocketContext>,
    encoder: Codec,
    buf: BytesMut,
    closed: bool,
}

impl WebsocketContextFut
where
    A: Actor<Context = WebsocketContext>,
{
    fn new(ctx: WebsocketContext, act: A, mailbox: Mailbox) -> Self {
        let fut = ContextFut::new(ctx, act, mailbox);
        WebsocketContextFut {
            fut,
            encoder: Codec::new(),
            buf: BytesMut::new(),
            closed: false,
        }
    }
}

impl Stream for WebsocketContextFut
where
    A: Actor<Context = WebsocketContext>,
{
    type Item = Bytes;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Bytes>, Error> {
        if self.fut.alive() && self.fut.poll().is_err() {
            return Err(ErrorInternalServerError("error"));
        }

        // encode messages
        while let Some(item) = self.fut.ctx().messages.pop_front() {
            if let Some(msg) = item {
                self.encoder.encode(msg, &mut self.buf)?;
            } else {
                self.closed = true;
                break;
            }
        }

        if !self.buf.is_empty() {
            Ok(Async::Ready(Some(self.buf.take().freeze())))
        } else if self.fut.alive() && !self.closed {
            Ok(Async::NotReady)
        } else {
            Ok(Async::Ready(None))
        }
    }
}

impl ToEnvelope for WebsocketContext
where
    A: Actor<Context = WebsocketContext> + Handler<M>,
    M: ActixMessage + Send + 'static,
    M::Result: Send,
{
    fn pack(msg: M, tx: Option<Sender<M::Result>>) -> Envelope {
        Envelope::new(msg, tx)
    }
}

struct WsStream<S> {
    stream: S,
    decoder: Codec,
    buf: BytesMut,
    closed: bool,
}

impl<S> WsStream<S>
where
    S: Stream<Item = Bytes, Error = PayloadError>,
{
    fn new(stream: S) -> Self {
        Self {
            stream,
            decoder: Codec::new(),
            buf: BytesMut::new(),
            closed: false,
        }
    }
}

impl<S> Stream for WsStream<S>
where
    S: Stream<Item = Bytes, Error = PayloadError>,
{
    type Item = Message;
    type Error = ProtocolError;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if !self.closed {
            loop {
                match self.stream.poll() {
                    Ok(Async::Ready(Some(chunk))) => {
                        self.buf.extend_from_slice(&chunk[..]);
                    }
                    Ok(Async::Ready(None)) => {
                        self.closed = true;
                        break;
                    }
                    Ok(Async::NotReady) => break,
                    Err(e) => {
                        return Err(ProtocolError::Io(io::Error::new(
                            io::ErrorKind::Other,
                            format!("{}", e),
                        )));
                    }
                }
            }
        }

        match self.decoder.decode(&mut self.buf)? {
            None => {
                if self.closed {
                    Ok(Async::Ready(None))
                } else {
                    Ok(Async::NotReady)
                }
            }
            Some(frm) => {
                let msg = match frm {
                    Frame::Text(data) => {
                        if let Some(data) = data {
                            Message::Text(
                                std::str::from_utf8(&data)
                                    .map_err(|_| ProtocolError::BadEncoding)?
                                    .to_string(),
                            )
                        } else {
                            Message::Text(String::new())
                        }
                    }
                    Frame::Binary(data) => Message::Binary(
                        data.map(|b| b.freeze()).unwrap_or_else(|| Bytes::new()),
                    ),
                    Frame::Ping(s) => Message::Ping(s),
                    Frame::Pong(s) => Message::Pong(s),
                    Frame::Close(reason) => Message::Close(reason),
                };
                Ok(Async::Ready(Some(msg)))
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::http::{header, Method};
    use actix_web::test::TestRequest;

    #[test]
    fn test_handshake() {
        let req = TestRequest::default()
            .method(Method::POST)
            .to_http_request();
        assert_eq!(
            HandshakeError::GetMethodRequired,
            handshake(&req).err().unwrap()
        );

        let req = TestRequest::default().to_http_request();
        assert_eq!(
            HandshakeError::NoWebsocketUpgrade,
            handshake(&req).err().unwrap()
        );

        let req = TestRequest::default()
            .header(header::UPGRADE, header::HeaderValue::from_static("test"))
            .to_http_request();
        assert_eq!(
            HandshakeError::NoWebsocketUpgrade,
            handshake(&req).err().unwrap()
        );

        let req = TestRequest::default()
            .header(
                header::UPGRADE,
                header::HeaderValue::from_static("websocket"),
            )
            .to_http_request();
        assert_eq!(
            HandshakeError::NoConnectionUpgrade,
            handshake(&req).err().unwrap()
        );

        let req = TestRequest::default()
            .header(
                header::UPGRADE,
                header::HeaderValue::from_static("websocket"),
            )
            .header(
                header::CONNECTION,
                header::HeaderValue::from_static("upgrade"),
            )
            .to_http_request();
        assert_eq!(
            HandshakeError::NoVersionHeader,
            handshake(&req).err().unwrap()
        );

        let req = TestRequest::default()
            .header(
                header::UPGRADE,
                header::HeaderValue::from_static("websocket"),
            )
            .header(
                header::CONNECTION,
                header::HeaderValue::from_static("upgrade"),
            )
            .header(
                header::SEC_WEBSOCKET_VERSION,
                header::HeaderValue::from_static("5"),
            )
            .to_http_request();
        assert_eq!(
            HandshakeError::UnsupportedVersion,
            handshake(&req).err().unwrap()
        );

        let req = TestRequest::default()
            .header(
                header::UPGRADE,
                header::HeaderValue::from_static("websocket"),
            )
            .header(
                header::CONNECTION,
                header::HeaderValue::from_static("upgrade"),
            )
            .header(
                header::SEC_WEBSOCKET_VERSION,
                header::HeaderValue::from_static("13"),
            )
            .to_http_request();
        assert_eq!(
            HandshakeError::BadWebsocketKey,
            handshake(&req).err().unwrap()
        );

        let req = TestRequest::default()
            .header(
                header::UPGRADE,
                header::HeaderValue::from_static("websocket"),
            )
            .header(
                header::CONNECTION,
                header::HeaderValue::from_static("upgrade"),
            )
            .header(
                header::SEC_WEBSOCKET_VERSION,
                header::HeaderValue::from_static("13"),
            )
            .header(
                header::SEC_WEBSOCKET_KEY,
                header::HeaderValue::from_static("13"),
            )
            .to_http_request();

        assert_eq!(
            StatusCode::SWITCHING_PROTOCOLS,
            handshake(&req).unwrap().finish().status()
        );
    }
}
<|endoftext|>


<commit_before>use std::result;

use piston::input::*;

use rand::Rng;

use super::block::Tetromino;
use super::block::TETROMINOS;

pub type Result<T> = result::Result<T, String>;

use super::BLOCK_SIZE;
use super::BOARD_WIDTH;
use super::BOARD_HEIGHT;

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum GameState {
    Playing,
    Paused,
    Over // donezo. player lost or quit
}

pub struct Board {
    pub cells: [[u8; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
    pub current_piece: Tetromino, // current active Tetromino
    pub state: GameState,

    // line_counts[i] = # of filled blocks in row i
    line_counts: [i64; BOARD_HEIGHT as usize] ,
    // for "random bag" generation of the next tetromino
    tetrominos_bag: Vec<Tetromino>
}

impl Board {
    pub fn init_board() -> Board {
        let mut bag = TETROMINOS.to_vec();
        ::rand::thread_rng().shuffle(&mut bag);

        Board {
            cells: [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
            current_piece: bag.remove(0),
            state: GameState::Playing,

            line_counts: [0; BOARD_HEIGHT as usize],
            tetrominos_bag: bag
        }
    }

    pub fn handle_key_press(&mut self, inp: Input) {
        match inp {
            Input::Press(but) => {
                match but {
                    Button::Keyboard(Key::Up) => {
                        self.current_piece.rotate_right();

                        // undo rotation if it was invalid...lol
                        if self.current_piece_out_of_bounds() {
                            self.current_piece.rotate_left();
                        }
                    }
                    Button::Keyboard(Key::Down) => {
                        // pressing down drops piece to bottom
                        while self.can_move_current_piece_down() {
                            self.current_piece.move_down();
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        if self.can_move_current_piece_left() {
                            self.current_piece.move_left();
                        }
                    }
                    Button::Keyboard(Key::Right) => {
                        if self.can_move_current_piece_right() {
                            self.current_piece.move_right();
                        }
                    }
                    // P = pause button
                    Button::Keyboard(Key::P) => {
                        if self.state == GameState::Playing {
                            self.state = GameState::Paused;
                        } else if self.state == GameState::Paused {
                            self.state = GameState::Playing;
                        }
                    }
                    // R = restart button
                    Button::Keyboard(Key::R) => {
                        // reset all game variables
                        let mut bag = TETROMINOS.to_vec();
                        ::rand::thread_rng().shuffle(&mut bag);

                        self.cells = [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize];
                        self.current_piece = bag.remove(0);
                        self.state = GameState::Playing;
                        self.line_counts = [0; BOARD_HEIGHT as usize];
                        self.tetrominos_bag = bag;
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    // iterate thru board and clear the specified row
    fn clear_row(&mut self, row: usize) {
        for col in 0..BOARD_WIDTH {
            self.cells[row][col as usize] = 0;
        }
        self.line_counts[row] = 0;

        // move all cells above (and including) the cleared row down by one
        for r in (1..(row+1)).rev() {
            for col in 0..BOARD_WIDTH {
                self.cells[r as usize][col as usize] = self.cells[(r-1) as usize][col as usize];
            }
            self.line_counts[r as usize] = self.line_counts[(r-1) as usize];
        }
    }

    pub fn clear_line_if_needed(&mut self) {
        // iterate through rows affected by current piece
        let mut rows_affected = Vec::new();
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_y: usize = ((r as f64) + current_y) as usize;
                    rows_affected.push(board_y);
                }
            }
        }

        for row in rows_affected.iter() {
            if self.line_counts[*row] == BOARD_WIDTH as i64 {
                self.clear_row(*row);
            }
        }
    }

    pub fn advance_board(&mut self) -> Result<GameState> {
        if self.state != GameState::Playing {
            return Ok(self.state.clone())
        }
        // check if game is over or not
        // check top row of board and see if any of them are filled?
        for (col, colblock) in self.cells[0].iter().enumerate() {
            if *colblock == 1 {
                // TODO: clear current piece?
                self.state = GameState::Over;
                return Ok(GameState::Over)
            }
        }

        // make the existing piece fall
        if self.can_move_current_piece_down() {
            self.current_piece.move_down();
        } else {
            // add piece to board cells
            self.set_piece_on_board();

            // clear line if necessary
            self.clear_line_if_needed();

            // get new piece
            self.current_piece = self.get_next_piece();
        }

        Ok(GameState::Playing)
    }

    fn can_move_current_piece_down(&self) -> bool {
        if self.current_piece.bottommost() + 1.0 >= BOARD_HEIGHT as f64 {
            return false
        }
        // check if piece will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64);
                    let board_y: i64 = ((r as f64) + current_y + 1.0) as i64; // 1 down
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_left(&self) -> bool {
        if self.current_piece.leftmost() - 1.0 < 0.0 {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) - 1;
                    let board_y: i64 = ((r as f64) + current_y) as i64; // 1 down
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_right(&self) -> bool {
        if self.current_piece.rightmost() + 1.0 >= BOARD_WIDTH as f64  {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) + 1;
                    let board_y: i64 = ((r as f64) + current_y) as i64; // 1 down
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn current_piece_out_of_bounds(&self) -> bool {
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    if self.current_piece.leftmost() < 0.0 || self.current_piece.rightmost() >= BOARD_WIDTH as f64 || self.current_piece.bottommost() >= BOARD_HEIGHT as f64 {
                        return true
                    }
                }
            }
        }
        false
    }

    pub fn set_piece_on_board(&mut self) {
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let x: i64 = (col as i64) + (current_x as i64);
                    let y: i64 = (r as i64) + (current_y as i64);
                    self.cells[y as usize][x as usize] = 1;
                    self.line_counts[y as usize] += 1;
                }
            }
        }
    }

    pub fn get_next_piece(&mut self) -> Tetromino {
        if self.tetrominos_bag.len() == 0 {
            self.tetrominos_bag = TETROMINOS.to_vec();
            ::rand::thread_rng().shuffle(&mut self.tetrominos_bag);
        }
        self.tetrominos_bag.remove(0)
    }

    pub fn draw_board(self) -> Result<()> {
        // draw the board ?
        Ok(())
    }
}<commit_msg>add space and down for moving piece downwards<commit_after>use std::result;

use piston::input::*;

use rand::Rng;

use super::block::Tetromino;
use super::block::TETROMINOS;

pub type Result<T> = result::Result<T, String>;

use super::BLOCK_SIZE;
use super::BOARD_WIDTH;
use super::BOARD_HEIGHT;

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum GameState {
    Playing,
    Paused,
    Over // donezo. player lost or quit
}

pub struct Board {
    pub cells: [[u8; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
    pub current_piece: Tetromino, // current active Tetromino
    pub state: GameState,

    // line_counts[i] = # of filled blocks in row i
    line_counts: [i64; BOARD_HEIGHT as usize] ,
    // for "random bag" generation of the next tetromino
    tetrominos_bag: Vec<Tetromino>
}

impl Board {
    pub fn init_board() -> Board {
        let mut bag = TETROMINOS.to_vec();
        ::rand::thread_rng().shuffle(&mut bag);

        Board {
            cells: [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
            current_piece: bag.remove(0),
            state: GameState::Playing,

            line_counts: [0; BOARD_HEIGHT as usize],
            tetrominos_bag: bag
        }
    }

    pub fn handle_key_press(&mut self, inp: Input) {
        match inp {
            Input::Press(but) => {
                match but {
                    Button::Keyboard(Key::Up) => {
                        self.current_piece.rotate_right();

                        // undo rotation if it was invalid...lol
                        if self.current_piece_out_of_bounds() {
                            self.current_piece.rotate_left();
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        if self.can_move_current_piece_left() {
                            self.current_piece.move_left();
                        }
                    }
                    Button::Keyboard(Key::Right) => {
                        if self.can_move_current_piece_right() {
                            self.current_piece.move_right();
                        }
                    }
                    Button::Keyboard(Key::Down) => {
                        if self.can_move_current_piece_down() {
                            self.current_piece.move_down();
                        }
                    }
                    Button::Keyboard(Key::Space) => {
                        // pressing spacebar drops piece to bottom
                        while self.can_move_current_piece_down() {
                            self.current_piece.move_down();
                        }
                    }
                    // P = pause button
                    Button::Keyboard(Key::P) => {
                        if self.state == GameState::Playing {
                            self.state = GameState::Paused;
                        } else if self.state == GameState::Paused {
                            self.state = GameState::Playing;
                        }
                    }
                    // R = restart button
                    Button::Keyboard(Key::R) => {
                        // reset all game variables
                        let mut bag = TETROMINOS.to_vec();
                        ::rand::thread_rng().shuffle(&mut bag);

                        self.cells = [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize];
                        self.current_piece = bag.remove(0);
                        self.state = GameState::Playing;
                        self.line_counts = [0; BOARD_HEIGHT as usize];
                        self.tetrominos_bag = bag;
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    // iterate thru board and clear the specified row
    fn clear_row(&mut self, row: usize) {
        for col in 0..BOARD_WIDTH {
            self.cells[row][col as usize] = 0;
        }
        self.line_counts[row] = 0;

        // move all cells above (and including) the cleared row down by one
        for r in (1..(row+1)).rev() {
            for col in 0..BOARD_WIDTH {
                self.cells[r as usize][col as usize] = self.cells[(r-1) as usize][col as usize];
            }
            self.line_counts[r as usize] = self.line_counts[(r-1) as usize];
        }
    }

    pub fn clear_line_if_needed(&mut self) {
        // iterate through rows affected by current piece
        let mut rows_affected = Vec::new();
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_y: usize = ((r as f64) + current_y) as usize;
                    rows_affected.push(board_y);
                }
            }
        }

        for row in rows_affected.iter() {
            if self.line_counts[*row] == BOARD_WIDTH as i64 {
                self.clear_row(*row);
            }
        }
    }

    pub fn advance_board(&mut self) -> Result<GameState> {
        if self.state != GameState::Playing {
            return Ok(self.state.clone())
        }
        // check if game is over or not
        // check top row of board and see if any of them are filled?
        for (col, colblock) in self.cells[0].iter().enumerate() {
            if *colblock == 1 {
                // TODO: clear current piece?
                self.state = GameState::Over;
                return Ok(GameState::Over)
            }
        }

        // make the existing piece fall
        if self.can_move_current_piece_down() {
            self.current_piece.move_down();
        } else {
            // add piece to board cells
            self.set_piece_on_board();

            // clear line if necessary
            self.clear_line_if_needed();

            // get new piece
            self.current_piece = self.get_next_piece();
        }

        Ok(GameState::Playing)
    }

    fn can_move_current_piece_down(&self) -> bool {
        if self.current_piece.bottommost() + 1.0 >= BOARD_HEIGHT as f64 {
            return false
        }
        // check if piece will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64);
                    let board_y: i64 = ((r as f64) + current_y + 1.0) as i64; // 1 down
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_left(&self) -> bool {
        if self.current_piece.leftmost() - 1.0 < 0.0 {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) - 1;
                    let board_y: i64 = ((r as f64) + current_y) as i64; // 1 down
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_right(&self) -> bool {
        if self.current_piece.rightmost() + 1.0 >= BOARD_WIDTH as f64  {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) + 1;
                    let board_y: i64 = ((r as f64) + current_y) as i64; // 1 down
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn current_piece_out_of_bounds(&self) -> bool {
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    if self.current_piece.leftmost() < 0.0 || self.current_piece.rightmost() >= BOARD_WIDTH as f64 || self.current_piece.bottommost() >= BOARD_HEIGHT as f64 {
                        return true
                    }
                }
            }
        }
        false
    }

    pub fn set_piece_on_board(&mut self) {
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for (r, row) in self.current_piece.blocks.iter().enumerate() {
            for (col, colblock) in row.iter().enumerate() {
                if *colblock == 1 {
                    let x: i64 = (col as i64) + (current_x as i64);
                    let y: i64 = (r as i64) + (current_y as i64);
                    self.cells[y as usize][x as usize] = 1;
                    self.line_counts[y as usize] += 1;
                }
            }
        }
    }

    pub fn get_next_piece(&mut self) -> Tetromino {
        if self.tetrominos_bag.len() == 0 {
            self.tetrominos_bag = TETROMINOS.to_vec();
            ::rand::thread_rng().shuffle(&mut self.tetrominos_bag);
        }
        self.tetrominos_bag.remove(0)
    }

    pub fn draw_board(self) -> Result<()> {
        // draw the board ?
        Ok(())
    }
}<|endoftext|>


<commit_before>use piston::input::*;

use rand::Rng;

use super::BOARD_WIDTH;
use super::BOARD_HEIGHT;
use super::block::Tetromino;
use super::block::TETROMINOS;

// 1st entry is # of points for clearing 1 line, etc.
const SCORES_PER_LINE: [u64; 4] = [80, 200, 600, 2400];

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum GameState {
    Playing,
    Paused,
    Over // donezo. player lost or quit
}

pub struct Board {
    pub cells: [[u8; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
    pub current_piece: Tetromino, // current active Tetromino
    pub state: GameState,
    pub score: u64,

    // line_counts[i] = # of filled blocks in row i
    line_counts: [i64; BOARD_HEIGHT as usize],
    // for "random bag" generation of the next tetromino
    tetrominos_bag: Vec<Tetromino>
}

impl Board {
    pub fn init_board() -> Board {
        let mut bag = TETROMINOS.to_vec();
        ::rand::thread_rng().shuffle(&mut bag);

        Board {
            cells: [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
            current_piece: bag.remove(0),
            state: GameState::Playing,
            score: 0,

            line_counts: [0; BOARD_HEIGHT as usize],
            tetrominos_bag: bag
        }
    }

    pub fn handle_key_press(&mut self, inp: &Input) {
        match *inp {
            Input::Press(but) => {
                match but {
                    Button::Keyboard(Key::Up) => {
                        self.current_piece.rotate_right();

                        // undo rotation if it was invalid...lol
                        if self.current_piece_out_of_bounds() {
                            self.current_piece.rotate_left();
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        if self.can_move_current_piece_left() {
                            self.current_piece.move_left();
                        }
                    }
                    Button::Keyboard(Key::Right) => {
                        if self.can_move_current_piece_right() {
                            self.current_piece.move_right();
                        }
                    }
                    Button::Keyboard(Key::Down) => {
                        if self.can_move_current_piece_down() {
                            self.current_piece.move_down();
                        }
                    }
                    Button::Keyboard(Key::Space) => {
                        // pressing spacebar drops piece to bottom
                        while self.can_move_current_piece_down() {
                            self.current_piece.move_down();
                        }
                    }
                    // P = pause button
                    Button::Keyboard(Key::P) => {
                        if self.state == GameState::Playing {
                            self.state = GameState::Paused;
                        } else if self.state == GameState::Paused {
                            self.state = GameState::Playing;
                        }
                    }
                    // R = restart button
                    // TODO: disabled for multiplayer
                    Button::Keyboard(Key::R) => {
                        // reset all game variables
                        let mut bag = TETROMINOS.to_vec();
                        ::rand::thread_rng().shuffle(&mut bag);

                        self.cells = [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize];
                        self.current_piece = bag.remove(0);
                        self.state = GameState::Playing;
                        self.line_counts = [0; BOARD_HEIGHT as usize];
                        self.tetrominos_bag = bag;
                        self.score = 0;
                        println!("===== GAME RESTARTED =====");
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    // iterate thru board and clear the specified row
    fn clear_row(&mut self, row: usize) {
        for col in 0..BOARD_WIDTH {
            self.cells[row][col as usize] = 0;
        }
        self.line_counts[row] = 0;

        // move all cells above (and including) the cleared row down by one
        for r in (1..(row+1)).rev() {
            for col in 0..BOARD_WIDTH {
                self.cells[r as usize][col as usize] = self.cells[(r-1) as usize][col as usize];
            }
            self.line_counts[r as usize] = self.line_counts[(r-1) as usize];
        }
    }

    pub fn clear_line_if_needed(&mut self) {
        // iterate through rows affected by current piece
        let mut rows_affected = Vec::new();
        let current_y = self.current_piece.y_offset;

        for r in 0..self.current_piece.blocks.len() {
            for c in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[r][c] == 1 {
                    let board_y: usize = ((r as f64) + current_y) as usize;
                    rows_affected.push(board_y);
                }
            }
        }

        let mut rows_cleared = 0;
        for row in rows_affected.iter() {
            if self.line_counts[*row] == BOARD_WIDTH {
                self.clear_row(*row);
                rows_cleared += 1;
            }
        }
        if rows_cleared > 0 {
            self.score += SCORES_PER_LINE[rows_cleared - 1];
        }
    }

    pub fn advance_board(&mut self) {
        // println!("=== SCORE: {:?} ===", self.score);

        if self.state != GameState::Playing {
            return
        }
        // check if game is over or not
        // check top row of board and see if any of them are filled?
        for col in 0..self.cells[0].len() {
            if self.cells[0][col] == 1{
                self.state = GameState::Over;
                return
            }
        }

        // make the existing piece fall
        if self.can_move_current_piece_down() {
            self.current_piece.move_down();
        } else {
            // add piece to board cells
            self.set_piece_on_board();

            // clear line if necessary
            self.clear_line_if_needed();

            // get new piece
            self.current_piece = self.get_next_piece();
        }
    }

    fn can_move_current_piece_down(&self) -> bool {
        if self.current_piece.bottommost() + 1.0 >= BOARD_HEIGHT as f64 {
            return false
        }
        // check if piece will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64);
                    let board_y: i64 = (row as i64) + (current_y as i64) + 1;
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_left(&self) -> bool {
        if self.current_piece.leftmost() - 1.0 < 0.0 {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) - 1;
                    let board_y: i64 = (row as i64) + (current_y as i64);
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_right(&self) -> bool {
        if self.current_piece.rightmost() + 1.0 >= BOARD_WIDTH as f64  {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) + 1;
                    let board_y: i64 = (row as i64) + (current_y as i64);
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn current_piece_out_of_bounds(&self) -> bool {
        if self.current_piece.leftmost() < 0.0 || self.current_piece.rightmost() >= BOARD_WIDTH as f64 || self.current_piece.bottommost() >= BOARD_HEIGHT as f64 {
            return true
        }
        false
    }

    pub fn set_piece_on_board(&mut self) {
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let x: i64 = (col as i64) + (current_x as i64);
                    let y: i64 = (row as i64) + (current_y as i64);
                    self.cells[y as usize][x as usize] = 1;
                    self.line_counts[y as usize] += 1;
                }
            }
        }
    }

    pub fn get_next_piece(&mut self) -> Tetromino {
        if self.tetrominos_bag.len() == 0 {
            self.tetrominos_bag = TETROMINOS.to_vec();
            ::rand::thread_rng().shuffle(&mut self.tetrominos_bag);
        }
        self.tetrominos_bag.remove(0)
    }
}<commit_msg>disable pause and restart for multiplayer<commit_after>use piston::input::*;

use rand::Rng;

use super::BOARD_WIDTH;
use super::BOARD_HEIGHT;
use super::block::Tetromino;
use super::block::TETROMINOS;

// 1st entry is # of points for clearing 1 line, etc.
const SCORES_PER_LINE: [u64; 4] = [80, 200, 600, 2400];

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum GameState {
    Playing,
    Paused,
    Over // donezo. player lost or quit
}

pub struct Board {
    pub cells: [[u8; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
    pub current_piece: Tetromino, // current active Tetromino
    pub state: GameState,
    pub score: u64,

    // line_counts[i] = # of filled blocks in row i
    line_counts: [i64; BOARD_HEIGHT as usize],
    // for "random bag" generation of the next tetromino
    tetrominos_bag: Vec<Tetromino>
}

impl Board {
    pub fn init_board() -> Board {
        let mut bag = TETROMINOS.to_vec();
        ::rand::thread_rng().shuffle(&mut bag);

        Board {
            cells: [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize],
            current_piece: bag.remove(0),
            state: GameState::Playing,
            score: 0,

            line_counts: [0; BOARD_HEIGHT as usize],
            tetrominos_bag: bag
        }
    }

    pub fn handle_key_press(&mut self, inp: &Input) {
        match *inp {
            Input::Press(but) => {
                match but {
                    Button::Keyboard(Key::Up) => {
                        self.current_piece.rotate_right();

                        // undo rotation if it was invalid...lol
                        if self.current_piece_out_of_bounds() {
                            self.current_piece.rotate_left();
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        if self.can_move_current_piece_left() {
                            self.current_piece.move_left();
                        }
                    }
                    Button::Keyboard(Key::Right) => {
                        if self.can_move_current_piece_right() {
                            self.current_piece.move_right();
                        }
                    }
                    Button::Keyboard(Key::Down) => {
                        if self.can_move_current_piece_down() {
                            self.current_piece.move_down();
                        }
                    }
                    Button::Keyboard(Key::Space) => {
                        // pressing spacebar drops piece to bottom
                        while self.can_move_current_piece_down() {
                            self.current_piece.move_down();
                        }
                    }
                    // TODO: these options disabled for multiplayer

                    // P = pause button
                    // Button::Keyboard(Key::P) => {
                    //     if self.state == GameState::Playing {
                    //         self.state = GameState::Paused;
                    //     } else if self.state == GameState::Paused {
                    //         self.state = GameState::Playing;
                    //     }
                    // }

                    // R = restart button
                    // Button::Keyboard(Key::R) => {
                    //     // reset all game variables
                    //     let mut bag = TETROMINOS.to_vec();
                    //     ::rand::thread_rng().shuffle(&mut bag);

                    //     self.cells = [[0; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize];
                    //     self.current_piece = bag.remove(0);
                    //     self.state = GameState::Playing;
                    //     self.line_counts = [0; BOARD_HEIGHT as usize];
                    //     self.tetrominos_bag = bag;
                    //     self.score = 0;
                    //     println!("===== GAME RESTARTED =====");
                    // }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    // iterate thru board and clear the specified row
    fn clear_row(&mut self, row: usize) {
        for col in 0..BOARD_WIDTH {
            self.cells[row][col as usize] = 0;
        }
        self.line_counts[row] = 0;

        // move all cells above (and including) the cleared row down by one
        for r in (1..(row+1)).rev() {
            for col in 0..BOARD_WIDTH {
                self.cells[r as usize][col as usize] = self.cells[(r-1) as usize][col as usize];
            }
            self.line_counts[r as usize] = self.line_counts[(r-1) as usize];
        }
    }

    pub fn clear_line_if_needed(&mut self) {
        // iterate through rows affected by current piece
        let mut rows_affected = Vec::new();
        let current_y = self.current_piece.y_offset;

        for r in 0..self.current_piece.blocks.len() {
            for c in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[r][c] == 1 {
                    let board_y: usize = ((r as f64) + current_y) as usize;
                    rows_affected.push(board_y);
                }
            }
        }

        let mut rows_cleared = 0;
        for row in rows_affected.iter() {
            if self.line_counts[*row] == BOARD_WIDTH {
                self.clear_row(*row);
                rows_cleared += 1;
            }
        }
        if rows_cleared > 0 {
            self.score += SCORES_PER_LINE[rows_cleared - 1];
        }
    }

    pub fn advance_board(&mut self) {
        // println!("=== SCORE: {:?} ===", self.score);

        if self.state != GameState::Playing {
            return
        }
        // check if game is over or not
        // check top row of board and see if any of them are filled?
        for col in 0..self.cells[0].len() {
            if self.cells[0][col] == 1{
                self.state = GameState::Over;
                return
            }
        }

        // make the existing piece fall
        if self.can_move_current_piece_down() {
            self.current_piece.move_down();
        } else {
            // add piece to board cells
            self.set_piece_on_board();

            // clear line if necessary
            self.clear_line_if_needed();

            // get new piece
            self.current_piece = self.get_next_piece();
        }
    }

    fn can_move_current_piece_down(&self) -> bool {
        if self.current_piece.bottommost() + 1.0 >= BOARD_HEIGHT as f64 {
            return false
        }
        // check if piece will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64);
                    let board_y: i64 = (row as i64) + (current_y as i64) + 1;
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_left(&self) -> bool {
        if self.current_piece.leftmost() - 1.0 < 0.0 {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) - 1;
                    let board_y: i64 = (row as i64) + (current_y as i64);
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn can_move_current_piece_right(&self) -> bool {
        if self.current_piece.rightmost() + 1.0 >= BOARD_WIDTH as f64  {
            return false;
        }
        // check if will intersect with a piece already on the board
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let board_x: i64 = (col as i64) + (current_x as i64) + 1;
                    let board_y: i64 = (row as i64) + (current_y as i64);
                    if self.cells[board_y as usize][board_x as usize] == 1 {
                        return false;
                    }
                }
            }
        }
        true
    }

    fn current_piece_out_of_bounds(&self) -> bool {
        if self.current_piece.leftmost() < 0.0 || self.current_piece.rightmost() >= BOARD_WIDTH as f64 || self.current_piece.bottommost() >= BOARD_HEIGHT as f64 {
            return true
        }
        false
    }

    pub fn set_piece_on_board(&mut self) {
        let current_x = self.current_piece.x_offset;
        let current_y = self.current_piece.y_offset;
        for row in 0..self.current_piece.blocks.len() {
            for col in 0..self.current_piece.blocks[0].len() {
                if self.current_piece.blocks[row][col] == 1 {
                    let x: i64 = (col as i64) + (current_x as i64);
                    let y: i64 = (row as i64) + (current_y as i64);
                    self.cells[y as usize][x as usize] = 1;
                    self.line_counts[y as usize] += 1;
                }
            }
        }
    }

    pub fn get_next_piece(&mut self) -> Tetromino {
        if self.tetrominos_bag.len() == 0 {
            self.tetrominos_bag = TETROMINOS.to_vec();
            ::rand::thread_rng().shuffle(&mut self.tetrominos_bag);
        }
        self.tetrominos_bag.remove(0)
    }
}<|endoftext|>


<commit_before>#![feature(if_let)]
use std::io;
use std::io::fs;
use std::io::fs::PathExtensions;
use std::io::process::{ProcessExit, Command};
use std::path::Path;
use std::os;

fn main() {

	io::println("Searching for LibJIT");
	let ref out_path = Path::new(os::getenv("OUT_DIR").unwrap());
	let ref lib_path = Path::new("/usr/lib/libjit.so");
	let ref eventual_lib_path = Path::new("native/jit/.libs/libjit.so");
	let ref dest_path = out_path.join("libjit.so");
	let ref submod_path = Path::new("native");
	if lib_path.exists() {
		fs::copy(lib_path, dest_path).unwrap();
	} else if !dest_path.exists() {
		if !eventual_lib_path.exists() {
			io::println("No LibJIT found in /usr/lib/ so updating LibJIT");
			if !submod_path.exists() {
				io::println("Initialising submodule");
				run(Command::new("git").arg("submodule").arg("init"));
			}
			run(Command::new("git").arg("submodule").arg("update"));
			let old_cwd = os::getcwd();
			os::change_dir(submod_path);
			io::println("Configuring LibJIT");
			run(Command::new("sh").arg("auto_gen.sh"));
			run_cmd("./configure");

			io::println("Building LibJIT");
			run_cmd("make");
			os::change_dir(&old_cwd);
		}
		fs::copy(eventual_lib_path, dest_path).unwrap();
	}
	println!("cargo:rustc-flags=-L {}", out_path.display());
}
fn run_cmd(command: &str) {
	run(&mut Command::new(command))
}
fn run(command: &mut Command) {
	let process = command.output().unwrap();
	if !process.status.success() {
		panic!("Command '{}' failed with output: \n{}\n{}", command, String::from_utf8_lossy(process.output[]), String::from_utf8_lossy(process.error[]));
	}
}<commit_msg>Change cwd for each command not for whole program<commit_after>#![feature(if_let)]
use std::io;
use std::io::fs;
use std::io::fs::PathExtensions;
use std::io::process::{ProcessExit, Command};
use std::path::Path;
use std::os;

fn main() {

	io::println("Searching for LibJIT");
	let ref out_path = Path::new(os::getenv("OUT_DIR").unwrap());
	let ref lib_path = Path::new("/usr/lib/libjit.so");
	let ref eventual_lib_path = Path::new("native/jit/.libs/libjit.so");
	let ref dest_path = out_path.join("libjit.so");
	let ref submod_path = Path::new("native");
	if lib_path.exists() {
		fs::copy(lib_path, dest_path).unwrap();
	} else if !dest_path.exists() {
		if !eventual_lib_path.exists() {
			io::println("No LibJIT found in /usr/lib/ so updating LibJIT");
			if !submod_path.exists() {
				io::println("Initialising submodule");
				run(Command::new("git").arg("submodule").arg("init"));
			}
			run(Command::new("git").arg("submodule").arg("update"));
			io::println("Configuring LibJIT");
			run(Command::new("sh").arg("auto_gen.sh").cwd(submod_path));
			run(Command::new("./configure").cwd(submod_path));

			io::println("Building LibJIT");
			run(Command::new("make").cwd(submod_path));
		}
		fs::copy(eventual_lib_path, dest_path).unwrap();
	}
	println!("cargo:rustc-flags=-L {}", out_path.display());
    println!("cargo:root={}", out_path.display());
    println!("cargo:rustc-flags=-l jit:static");
}
fn run(command: &mut Command) {
	let process = command.output().unwrap();
	if !process.status.success() {
		panic!("Command '{}' failed with output: \n{}\n{}", command, String::from_utf8_lossy(process.output[]), String::from_utf8_lossy(process.error[]));
	}
}<|endoftext|>


<commit_before>use std::path::PathBuf;

use rayon::prelude::*;
use syntect::parsing::SyntaxSet;

use crate::config::Config;
use crate::page::{Page, Source};

pub fn build(in_dir: PathBuf) -> Result<(), String> {
    let in_dir = std::fs::canonicalize(in_dir).map_err(|e| e.to_string())?;
    let config_path = in_dir.join(PathBuf::from("_data/config.json5"));
    let config = Config::from_file(&config_path)?;

    let syntax_set = load_syntaxes();

    get_files_to_load(&in_dir)
        .into_par_iter()
        .map(|path| {
            std::fs::read_to_string(&path)
                .map(|contents| Source {
                    path: path.clone(),
                    contents,
                })
                .map_err(|e| format!("{}: {}", path.display(), e))
        })
        .map(|result| {
            result.and_then(|source| {
                Page::new(&source, &in_dir, &syntax_set)
                    .map_err(|e| format!("{}: {}", source.path.display(), e))
            })
        })
        .map(|result| {
            result.and_then(|page| {
                let path = page.path(&config.output).with_extension("html");
                println!(
                    "built final path {} from {} and {}",
                    &path.display(),
                    &page.metadata.slug,
                    &config.output.display()
                );
                let containing_dir = path
                    .parent()
                    .ok_or_else(|| format!("{} should have a containing dir!", path.display()))?;
                std::fs::create_dir_all(containing_dir)
                    .map_err(|e| format!("{}: {}", path.display(), e.to_string()))?;
                println!("writing {}", path.display());
                std::fs::write(&path, page.contents)
                    .map_err(|e| format!("{}: {}", path.display(), e))
            })
        })
        .fold(
            || Ok(()),
            |so_far, result| match (so_far, result) {
                (Ok(_), Ok(_)) => Ok(()),
                (Err(s), Ok(_)) => Err(s),
                (Ok(_), Err(e)) => Err(e),
                (Err(s), Err(e)) => Err(s + &e),
            },
        )
        .collect()
}

fn get_files_to_load(in_dir: &PathBuf) -> Vec<PathBuf> {
    let content_dir = in_dir.join("content");
    let content_glob = content_dir.to_string_lossy() + "/**/*.md";

    let (ok_files, err_files): (Vec<PathBuf>, Vec<String>) = glob::glob(&content_glob)
        .unwrap_or_else(|_| panic!("bad glob: '{}'", &content_glob))
        .fold((vec![], vec![]), |(mut good, mut bad), result| {
            match result {
                Ok(path) => good.push(path),
                Err(e) => bad.push(e.to_string()),
            };

            (good, bad)
        });

    for err in err_files {
        eprintln!("problem with {}", err);
    }

    ok_files
}

fn load_syntaxes() -> SyntaxSet {
    // let mut extra_syntaxes_dir = std::env::current_dir().map_err(|e| format!("{}", e))?;
    // extra_syntaxes_dir.push("syntaxes");

    let syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // let mut syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // syntax_builder
    //     .add_from_folder(&extra_syntaxes_dir, false)
    //     .map_err(|e| format!("could not load {}: {}", &extra_syntaxes_dir.display(), e))?;

    syntax_builder.build()
}
<commit_msg>Handle 'content' dir in output structure<commit_after>use std::path::PathBuf;

use rayon::prelude::*;
use syntect::parsing::SyntaxSet;

use crate::config::Config;
use crate::page::{Page, Source};

pub fn build(in_dir: PathBuf) -> Result<(), String> {
    let in_dir = std::fs::canonicalize(in_dir).map_err(|e| e.to_string())?;
    let config_path = in_dir.join(PathBuf::from("_data/config.json5"));
    let config = Config::from_file(&config_path)?;

    let syntax_set = load_syntaxes();

    get_files_to_load(&in_dir)
        .into_par_iter()
        .map(|path| {
            std::fs::read_to_string(&path)
                .map(|contents| Source {
                    path: path.clone(),
                    contents,
                })
                .map_err(|e| format!("{}: {}", path.display(), e))
        })
        .map(|result| {
            result.and_then(|source| {
                Page::new(&source, &in_dir.join("content"), &syntax_set)
                    .map_err(|e| format!("{}: {}", source.path.display(), e))
            })
        })
        .map(|result| {
            result.and_then(|page| {
                let path = page.path(&config.output).with_extension("html");
                println!(
                    "built final path {} from {} and {}",
                    &path.display(),
                    &page.metadata.slug,
                    &config.output.display()
                );
                let containing_dir = path
                    .parent()
                    .ok_or_else(|| format!("{} should have a containing dir!", path.display()))?;
                std::fs::create_dir_all(containing_dir)
                    .map_err(|e| format!("{}: {}", path.display(), e.to_string()))?;
                println!("writing {}", path.display());
                std::fs::write(&path, page.contents)
                    .map_err(|e| format!("{}: {}", path.display(), e))
            })
        })
        .fold(
            || Ok(()),
            |so_far, result| match (so_far, result) {
                (Ok(_), Ok(_)) => Ok(()),
                (Err(s), Ok(_)) => Err(s),
                (Ok(_), Err(e)) => Err(e),
                (Err(s), Err(e)) => Err(s + &e),
            },
        )
        .collect()
}

fn get_files_to_load(in_dir: &PathBuf) -> Vec<PathBuf> {
    let content_dir = in_dir.join("content");
    let content_glob = content_dir.to_string_lossy() + "/**/*.md";

    let (ok_files, err_files): (Vec<PathBuf>, Vec<String>) = glob::glob(&content_glob)
        .unwrap_or_else(|_| panic!("bad glob: '{}'", &content_glob))
        .fold((vec![], vec![]), |(mut good, mut bad), result| {
            match result {
                Ok(path) => good.push(path),
                Err(e) => bad.push(e.to_string()),
            };

            (good, bad)
        });

    for err in err_files {
        eprintln!("problem with {}", err);
    }

    ok_files
}

fn load_syntaxes() -> SyntaxSet {
    // let mut extra_syntaxes_dir = std::env::current_dir().map_err(|e| format!("{}", e))?;
    // extra_syntaxes_dir.push("syntaxes");

    let syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // let mut syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // syntax_builder
    //     .add_from_folder(&extra_syntaxes_dir, false)
    //     .map_err(|e| format!("could not load {}: {}", &extra_syntaxes_dir.display(), e))?;

    syntax_builder.build()
}
<|endoftext|>