<commit_before>// Copyright (c) 2017 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use mysql_common::row::convert::FromRowError;
use mysql_common::{io::ReadMysqlExt, packets::parse_local_infile_packet};
use tokio::prelude::*;

use std::{borrow::Cow, marker::PhantomData, result::Result as StdResult, sync::Arc};

use crate::{
    connection_like::Connection,
    error::*,
    prelude::{FromRow, Protocol},
    Column, Row,
};

/// Result set metadata.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ResultSetMeta {
    /// Text result set, that may contain rows.
    Text(Arc<[Column]>),
    /// Binary result set, that may contain rows.
    Binary(Arc<[Column]>),
    /// Error result set.
    Error(ServerError),
}

impl ResultSetMeta {
    fn columns(&self) -> StdResult<&Arc<[Column]>, &ServerError> {
        match self {
            ResultSetMeta::Text(cols) | ResultSetMeta::Binary(cols) => Ok(cols),
            ResultSetMeta::Error(err) => Err(err),
        }
    }
}

/// Result of a query or statement execution.
///
/// Represents an asynchronous query result, that may not be fully consumed. Note,
/// that unconsumed query results are dropped implicitly when corresponding connection
/// is dropped or queried.
#[derive(Debug)]
pub struct QueryResult<'a, 't: 'a, P> {
    conn: Connection<'a, 't>,
    __phantom: PhantomData<P>,
}

impl<'a, 't: 'a, P> QueryResult<'a, 't, P>
where
    P: Protocol,
{
    pub(crate) fn new<T: Into<Connection<'a, 't>>>(conn: T) -> Self {
        QueryResult {
            conn: conn.into(),
            __phantom: PhantomData,
        }
    }

    /// Returns `true` if this query result may contain rows.
    ///
    /// If `false` then no rows possible for this query tesult (e.g. result of an UPDATE query).
    fn has_rows(&self) -> bool {
        self.conn
            .get_pending_result()
            .and_then(|meta| meta.columns().map(|columns| columns.len() > 0).ok())
            .unwrap_or(false)
    }

    /// `true` if there are no more rows nor result sets in this query.
    pub fn is_empty(&self) -> bool {
        !self.has_rows() && !self.conn.more_results_exists()
    }

    pub async fn next(&mut self) -> Result<Option<Row>> {
        loop {
            let columns = match self.conn.get_pending_result() {
                Some(ResultSetMeta::Text(cols)) | Some(ResultSetMeta::Binary(cols)) => {
                    Ok(Some(cols.clone()))
                }
                Some(ResultSetMeta::Error(err)) => Err(Error::from(err.clone())),
                None => Ok(None),
            };

            match columns {
                Ok(Some(columns)) => {
                    if columns.is_empty() {
                        // Empty, but not yet consumed result set.
                        self.conn.set_pending_result(None);
                        return Ok(None);
                    } else {
                        // Not yet consumed non-empty result set.
                        let packet = match self.conn.read_packet().await {
                            Ok(packet) => packet,
                            Err(err) => {
                                // Next row contained an error. No more data will follow.
                                self.conn.set_pending_result(None);
                                return Err(err);
                            }
                        };

                        if P::is_last_result_set_packet(self.conn.capabilities(), &packet) {
                            // `packet` is a result set terminator.
                            self.conn.set_pending_result(None);
                            return Ok(None);
                        } else {
                            // `packet` is a result set row.
                            return Ok(Some(P::read_result_set_row(&packet, columns)?));
                        }
                    }
                }
                Ok(None) => {
                    // Consumed result set.
                    if self.conn.more_results_exists() {
                        // More data will follow.
                        self.conn.sync_seq_id();
                        self.conn.read_result_set::<P>().await?;
                        continue;
                    } else {
                        // The end of a query result.
                        return Ok(None);
                    }
                }
                Err(err) => {
                    // Error result set. No more data will follow.
                    self.conn.set_pending_result(None);
                    return Err(err);
                }
            }
        }
    }

    /// Last insert id, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.conn.last_insert_id()
    }

    /// Number of affected rows as reported by the server, or `0`.
    pub fn affected_rows(&self) -> u64 {
        self.conn.affected_rows()
    }

    /// Text information as reported by the server, or an empty string.
    pub fn info(&self) -> Cow<'_, str> {
        self.conn.info()
    }

    /// Number of warnings as reported by the server, or `0`.
    pub fn warnings(&self) -> u16 {
        self.conn.get_warnings()
    }

    /// Collects the current result set of this query result.
    ///
    /// It is parametrized by `R` and internally calls `R::from_row(Row)` on each row.
    ///
    /// It will collect rows up to a neares result set boundary. This means that you should call
    /// `collect` as many times as result sets in your query result. For example query
    /// `SELECT 'foo'; SELECT 'foo', 'bar';` will produce `QueryResult` with two result sets in it.
    /// One can use `QueryResult::is_empty` to make sure that there is no more result sets.
    ///
    /// # Panic
    ///
    /// It'll panic if any row isn't convertible to `R` (i.e. programmer error or unknown schema).
    /// * In case of programmer error see [`FromRow`] docs;
    /// * In case of unknown schema use [`QueryResult::try_collect`].
    pub async fn collect<R>(&mut self) -> Result<Vec<R>>
    where
        R: FromRow + Send + 'static,
    {
        self.reduce(Vec::new(), |mut acc, row| {
            acc.push(FromRow::from_row(row));
            acc
        })
        .await
    }

    /// Collects the current result set of this query result.
    ///
    /// It works the same way as [`QueryResult::collect`] but won't panic if row isn't convertible
    /// to `R`.
    pub async fn try_collect<R>(&mut self) -> Result<Vec<StdResult<R, FromRowError>>>
    where
        R: FromRow + Send + 'static,
    {
        self.reduce(Vec::new(), |mut acc, row| {
            acc.push(FromRow::from_row_opt(row));
            acc
        })
        .await
    }

    /// Collects the current result set of this query result and drops everything else.
    ///
    /// # Panic
    ///
    /// It'll panic if any row isn't convertible to `R` (i.e. programmer error or unknown schema).
    /// * In case of programmer error see `FromRow` docs;
    /// * In case of unknown schema use [`QueryResult::try_collect`].
    pub async fn collect_and_drop<R>(mut self) -> Result<Vec<R>>
    where
        R: FromRow + Send + 'static,
    {
        let output = self.collect::<R>().await?;
        self.drop_result().await?;
        Ok(output)
    }

    /// Collects the current result set of this query result and drops everything else.
    ///
    /// It works the same way as [`QueryResult::collect_and_drop`] but won't panic if row isn't
    /// convertible to `R`.
    pub async fn try_collect_and_drop<R>(mut self) -> Result<Vec<StdResult<R, FromRowError>>>
    where
        R: FromRow + Send + 'static,
    {
        let output = self.try_collect().await?;
        self.drop_result().await?;
        Ok(output)
    }

    /// Executes `fun` on every row of the current result set.
    ///
    /// It will stop on the nearest result set boundary (see `QueryResult::collect` docs).
    pub async fn for_each<F>(&mut self, mut fun: F) -> Result<()>
    where
        F: FnMut(Row),
    {
        if self.is_empty() {
            Ok(())
        } else {
            while let Some(row) = self.next().await? {
                fun(row);
            }
            Ok(())
        }
    }

    /// Executes `fun` on every row of the current result set and drops everything else.
    pub async fn for_each_and_drop<F>(mut self, fun: F) -> Result<()>
    where
        F: FnMut(Row),
    {
        self.for_each(fun).await?;
        self.drop_result().await?;
        Ok(())
    }

    /// Maps every row of the current result set to `U` using `fun`.
    ///
    /// It will stop on the nearest result set boundary (see `QueryResult::collect` docs).
    pub async fn map<F, U>(&mut self, mut fun: F) -> Result<Vec<U>>
    where
        F: FnMut(Row) -> U,
    {
        let mut acc = Vec::new();
        while let Some(row) = self.next().await? {
            acc.push(fun(crate::from_row(row)));
        }
        Ok(acc)
    }

    /// Map every row of the current result set to `U` using `fun` and drops everything else.
    pub async fn map_and_drop<F, U>(mut self, fun: F) -> Result<Vec<U>>
    where
        F: FnMut(Row) -> U,
    {
        let rows = self.map(fun).await?;
        self.drop_result().await?;
        Ok(rows)
    }

    /// Reduces rows of the current result set to `U` using `fun`.
    ///
    /// It will stop on the nearest result set boundary (see `QueryResult::collect` docs).
    pub async fn reduce<T, F, U>(&mut self, mut init: U, mut fun: F) -> Result<U>
    where
        F: FnMut(U, T) -> U,
        T: FromRow + Send + 'static,
    {
        while let Some(row) = self.next().await? {
            init = fun(init, crate::from_row(row));
        }
        Ok(init)
    }

    /// Reduces rows of the current result set to `U` using `fun` and drops everything else.
    pub async fn reduce_and_drop<T, F, U>(mut self, init: U, fun: F) -> Result<U>
    where
        F: FnMut(U, T) -> U,
        T: FromRow + Send + 'static,
    {
        let acc = self.reduce(init, fun).await?;
        self.drop_result().await?;
        Ok(acc)
    }

    /// Drops this query result.
    pub async fn drop_result(mut self) -> Result<()> {
        loop {
            while let Some(_) = self.next().await? {}
            if !self.conn.more_results_exists() {
                break Ok(());
            }
        }
    }

    /// Returns a reference to a columns list of this query result.
    ///
    /// Empty list means that this result set was never meant to contain rows.
    pub fn columns_ref(&self) -> &[Column] {
        self.conn
            .get_pending_result()
            .and_then(|meta| meta.columns().map(|cols| &cols[..]).ok())
            .unwrap_or_default()
    }

    /// Returns a copy of a columns list of this query result.
    pub fn columns(&self) -> Option<Arc<[Column]>> {
        self.conn
            .get_pending_result()
            .and_then(|meta| meta.columns().map(|columns| columns.clone()).ok())
    }
}

impl crate::Conn {
    /// Will read result set and write pending result into `self` (if any).
    pub(crate) async fn read_result_set<P>(&mut self) -> Result<()>
    where
        P: Protocol,
    {
        let packet = self.read_packet().await?;

        match packet.get(0) {
            Some(0x00) => self.set_pending_result(Some(P::result_set_meta(Arc::from(
                Vec::new().into_boxed_slice(),
            )))),
            Some(0xFB) => self.handle_local_infile::<P>(&*packet).await?,
            _ => self.handle_result_set::<P>(&*packet).await?,
        }

        Ok(())
    }

    /// Will handle local infile packet.
    pub(crate) async fn handle_local_infile<P>(&mut self, packet: &[u8]) -> Result<()>
    where
        P: Protocol,
    {
        let local_infile = parse_local_infile_packet(&*packet)?;
        let (local_infile, handler) = match self.opts().local_infile_handler() {
            Some(handler) => ((local_infile.into_owned(), handler)),
            None => return Err(DriverError::NoLocalInfileHandler.into()),
        };
        let mut reader = handler.handle(local_infile.file_name_ref()).await?;

        let mut buf = [0; 4096];
        loop {
            let read = reader.read(&mut buf[..]).await?;
            self.write_packet(&buf[..read]).await?;

            if read == 0 {
                break;
            }
        }

        self.read_packet().await?;
        self.set_pending_result(Some(P::result_set_meta(Arc::from(
            Vec::new().into_boxed_slice(),
        ))));
        Ok(())
    }

    /// Helper that handles result set packet.
    ///
    /// Requires that `packet` contains non-zero length-encoded integer.
    pub(crate) async fn handle_result_set<P>(&mut self, mut packet: &[u8]) -> Result<()>
    where
        P: Protocol,
    {
        let column_count = packet.read_lenenc_int()?;
        let columns = self.read_column_defs(column_count as usize).await?;
        let meta = P::result_set_meta(Arc::from(columns.into_boxed_slice()));
        self.set_pending_result(Some(meta));
        Ok(())
    }
}
<commit_msg>Make QueryResult::new public<commit_after>// Copyright (c) 2017 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use mysql_common::row::convert::FromRowError;
use mysql_common::{io::ReadMysqlExt, packets::parse_local_infile_packet};
use tokio::prelude::*;

use std::{borrow::Cow, marker::PhantomData, result::Result as StdResult, sync::Arc};

use crate::{
    connection_like::Connection,
    error::*,
    prelude::{FromRow, Protocol},
    Column, Row,
};

/// Result set metadata.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ResultSetMeta {
    /// Text result set, that may contain rows.
    Text(Arc<[Column]>),
    /// Binary result set, that may contain rows.
    Binary(Arc<[Column]>),
    /// Error result set.
    Error(ServerError),
}

impl ResultSetMeta {
    fn columns(&self) -> StdResult<&Arc<[Column]>, &ServerError> {
        match self {
            ResultSetMeta::Text(cols) | ResultSetMeta::Binary(cols) => Ok(cols),
            ResultSetMeta::Error(err) => Err(err),
        }
    }
}

/// Result of a query or statement execution.
///
/// Represents an asynchronous query result, that may not be fully consumed. Note,
/// that unconsumed query results are dropped implicitly when corresponding connection
/// is dropped or queried.
#[derive(Debug)]
pub struct QueryResult<'a, 't: 'a, P> {
    conn: Connection<'a, 't>,
    __phantom: PhantomData<P>,
}

impl<'a, 't: 'a, P> QueryResult<'a, 't, P>
where
    P: Protocol,
{
    pub fn new<T: Into<Connection<'a, 't>>>(conn: T) -> Self {
        QueryResult {
            conn: conn.into(),
            __phantom: PhantomData,
        }
    }

    /// Returns `true` if this query result may contain rows.
    ///
    /// If `false` then no rows possible for this query tesult (e.g. result of an UPDATE query).
    fn has_rows(&self) -> bool {
        self.conn
            .get_pending_result()
            .and_then(|meta| meta.columns().map(|columns| columns.len() > 0).ok())
            .unwrap_or(false)
    }

    /// `true` if there are no more rows nor result sets in this query.
    pub fn is_empty(&self) -> bool {
        !self.has_rows() && !self.conn.more_results_exists()
    }

    pub async fn next(&mut self) -> Result<Option<Row>> {
        loop {
            let columns = match self.conn.get_pending_result() {
                Some(ResultSetMeta::Text(cols)) | Some(ResultSetMeta::Binary(cols)) => {
                    Ok(Some(cols.clone()))
                }
                Some(ResultSetMeta::Error(err)) => Err(Error::from(err.clone())),
                None => Ok(None),
            };

            match columns {
                Ok(Some(columns)) => {
                    if columns.is_empty() {
                        // Empty, but not yet consumed result set.
                        self.conn.set_pending_result(None);
                        return Ok(None);
                    } else {
                        // Not yet consumed non-empty result set.
                        let packet = match self.conn.read_packet().await {
                            Ok(packet) => packet,
                            Err(err) => {
                                // Next row contained an error. No more data will follow.
                                self.conn.set_pending_result(None);
                                return Err(err);
                            }
                        };

                        if P::is_last_result_set_packet(self.conn.capabilities(), &packet) {
                            // `packet` is a result set terminator.
                            self.conn.set_pending_result(None);
                            return Ok(None);
                        } else {
                            // `packet` is a result set row.
                            return Ok(Some(P::read_result_set_row(&packet, columns)?));
                        }
                    }
                }
                Ok(None) => {
                    // Consumed result set.
                    if self.conn.more_results_exists() {
                        // More data will follow.
                        self.conn.sync_seq_id();
                        self.conn.read_result_set::<P>().await?;
                        continue;
                    } else {
                        // The end of a query result.
                        return Ok(None);
                    }
                }
                Err(err) => {
                    // Error result set. No more data will follow.
                    self.conn.set_pending_result(None);
                    return Err(err);
                }
            }
        }
    }

    /// Last insert id, if any.
    pub fn last_insert_id(&self) -> Option<u64> {
        self.conn.last_insert_id()
    }

    /// Number of affected rows as reported by the server, or `0`.
    pub fn affected_rows(&self) -> u64 {
        self.conn.affected_rows()
    }

    /// Text information as reported by the server, or an empty string.
    pub fn info(&self) -> Cow<'_, str> {
        self.conn.info()
    }

    /// Number of warnings as reported by the server, or `0`.
    pub fn warnings(&self) -> u16 {
        self.conn.get_warnings()
    }

    /// Collects the current result set of this query result.
    ///
    /// It is parametrized by `R` and internally calls `R::from_row(Row)` on each row.
    ///
    /// It will collect rows up to a neares result set boundary. This means that you should call
    /// `collect` as many times as result sets in your query result. For example query
    /// `SELECT 'foo'; SELECT 'foo', 'bar';` will produce `QueryResult` with two result sets in it.
    /// One can use `QueryResult::is_empty` to make sure that there is no more result sets.
    ///
    /// # Panic
    ///
    /// It'll panic if any row isn't convertible to `R` (i.e. programmer error or unknown schema).
    /// * In case of programmer error see [`FromRow`] docs;
    /// * In case of unknown schema use [`QueryResult::try_collect`].
    pub async fn collect<R>(&mut self) -> Result<Vec<R>>
    where
        R: FromRow + Send + 'static,
    {
        self.reduce(Vec::new(), |mut acc, row| {
            acc.push(FromRow::from_row(row));
            acc
        })
        .await
    }

    /// Collects the current result set of this query result.
    ///
    /// It works the same way as [`QueryResult::collect`] but won't panic if row isn't convertible
    /// to `R`.
    pub async fn try_collect<R>(&mut self) -> Result<Vec<StdResult<R, FromRowError>>>
    where
        R: FromRow + Send + 'static,
    {
        self.reduce(Vec::new(), |mut acc, row| {
            acc.push(FromRow::from_row_opt(row));
            acc
        })
        .await
    }

    /// Collects the current result set of this query result and drops everything else.
    ///
    /// # Panic
    ///
    /// It'll panic if any row isn't convertible to `R` (i.e. programmer error or unknown schema).
    /// * In case of programmer error see `FromRow` docs;
    /// * In case of unknown schema use [`QueryResult::try_collect`].
    pub async fn collect_and_drop<R>(mut self) -> Result<Vec<R>>
    where
        R: FromRow + Send + 'static,
    {
        let output = self.collect::<R>().await?;
        self.drop_result().await?;
        Ok(output)
    }

    /// Collects the current result set of this query result and drops everything else.
    ///
    /// It works the same way as [`QueryResult::collect_and_drop`] but won't panic if row isn't
    /// convertible to `R`.
    pub async fn try_collect_and_drop<R>(mut self) -> Result<Vec<StdResult<R, FromRowError>>>
    where
        R: FromRow + Send + 'static,
    {
        let output = self.try_collect().await?;
        self.drop_result().await?;
        Ok(output)
    }

    /// Executes `fun` on every row of the current result set.
    ///
    /// It will stop on the nearest result set boundary (see `QueryResult::collect` docs).
    pub async fn for_each<F>(&mut self, mut fun: F) -> Result<()>
    where
        F: FnMut(Row),
    {
        if self.is_empty() {
            Ok(())
        } else {
            while let Some(row) = self.next().await? {
                fun(row);
            }
            Ok(())
        }
    }

    /// Executes `fun` on every row of the current result set and drops everything else.
    pub async fn for_each_and_drop<F>(mut self, fun: F) -> Result<()>
    where
        F: FnMut(Row),
    {
        self.for_each(fun).await?;
        self.drop_result().await?;
        Ok(())
    }

    /// Maps every row of the current result set to `U` using `fun`.
    ///
    /// It will stop on the nearest result set boundary (see `QueryResult::collect` docs).
    pub async fn map<F, U>(&mut self, mut fun: F) -> Result<Vec<U>>
    where
        F: FnMut(Row) -> U,
    {
        let mut acc = Vec::new();
        while let Some(row) = self.next().await? {
            acc.push(fun(crate::from_row(row)));
        }
        Ok(acc)
    }

    /// Map every row of the current result set to `U` using `fun` and drops everything else.
    pub async fn map_and_drop<F, U>(mut self, fun: F) -> Result<Vec<U>>
    where
        F: FnMut(Row) -> U,
    {
        let rows = self.map(fun).await?;
        self.drop_result().await?;
        Ok(rows)
    }

    /// Reduces rows of the current result set to `U` using `fun`.
    ///
    /// It will stop on the nearest result set boundary (see `QueryResult::collect` docs).
    pub async fn reduce<T, F, U>(&mut self, mut init: U, mut fun: F) -> Result<U>
    where
        F: FnMut(U, T) -> U,
        T: FromRow + Send + 'static,
    {
        while let Some(row) = self.next().await? {
            init = fun(init, crate::from_row(row));
        }
        Ok(init)
    }

    /// Reduces rows of the current result set to `U` using `fun` and drops everything else.
    pub async fn reduce_and_drop<T, F, U>(mut self, init: U, fun: F) -> Result<U>
    where
        F: FnMut(U, T) -> U,
        T: FromRow + Send + 'static,
    {
        let acc = self.reduce(init, fun).await?;
        self.drop_result().await?;
        Ok(acc)
    }

    /// Drops this query result.
    pub async fn drop_result(mut self) -> Result<()> {
        loop {
            while let Some(_) = self.next().await? {}
            if !self.conn.more_results_exists() {
                break Ok(());
            }
        }
    }

    /// Returns a reference to a columns list of this query result.
    ///
    /// Empty list means that this result set was never meant to contain rows.
    pub fn columns_ref(&self) -> &[Column] {
        self.conn
            .get_pending_result()
            .and_then(|meta| meta.columns().map(|cols| &cols[..]).ok())
            .unwrap_or_default()
    }

    /// Returns a copy of a columns list of this query result.
    pub fn columns(&self) -> Option<Arc<[Column]>> {
        self.conn
            .get_pending_result()
            .and_then(|meta| meta.columns().map(|columns| columns.clone()).ok())
    }
}

impl crate::Conn {
    /// Will read result set and write pending result into `self` (if any).
    pub(crate) async fn read_result_set<P>(&mut self) -> Result<()>
    where
        P: Protocol,
    {
        let packet = self.read_packet().await?;

        match packet.get(0) {
            Some(0x00) => self.set_pending_result(Some(P::result_set_meta(Arc::from(
                Vec::new().into_boxed_slice(),
            )))),
            Some(0xFB) => self.handle_local_infile::<P>(&*packet).await?,
            _ => self.handle_result_set::<P>(&*packet).await?,
        }

        Ok(())
    }

    /// Will handle local infile packet.
    pub(crate) async fn handle_local_infile<P>(&mut self, packet: &[u8]) -> Result<()>
    where
        P: Protocol,
    {
        let local_infile = parse_local_infile_packet(&*packet)?;
        let (local_infile, handler) = match self.opts().local_infile_handler() {
            Some(handler) => ((local_infile.into_owned(), handler)),
            None => return Err(DriverError::NoLocalInfileHandler.into()),
        };
        let mut reader = handler.handle(local_infile.file_name_ref()).await?;

        let mut buf = [0; 4096];
        loop {
            let read = reader.read(&mut buf[..]).await?;
            self.write_packet(&buf[..read]).await?;

            if read == 0 {
                break;
            }
        }

        self.read_packet().await?;
        self.set_pending_result(Some(P::result_set_meta(Arc::from(
            Vec::new().into_boxed_slice(),
        ))));
        Ok(())
    }

    /// Helper that handles result set packet.
    ///
    /// Requires that `packet` contains non-zero length-encoded integer.
    pub(crate) async fn handle_result_set<P>(&mut self, mut packet: &[u8]) -> Result<()>
    where
        P: Protocol,
    {
        let column_count = packet.read_lenenc_int()?;
        let columns = self.read_column_defs(column_count as usize).await?;
        let meta = P::result_set_meta(Arc::from(columns.into_boxed_slice()));
        self.set_pending_result(Some(meta));
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::ops::DerefMut;

use game::*;
use game::data::*;
use ecs::*;
use util::{LeakyReserver, Schedule};
use coord::Coord;

pub struct EntityIdReserver(RefCell<LeakyReserver<EntityId>>);

impl EntityIdReserver {
    pub fn new() -> Self {
        EntityIdReserver(RefCell::new(LeakyReserver::new()))
    }

    pub fn new_id(&self) -> EntityId {
        self.0.borrow_mut().reserve()
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableEntityIdReserver(LeakyReserver<EntityId>);

impl From<EntityIdReserver> for SerializableEntityIdReserver {
    fn from(r: EntityIdReserver) -> Self {
        SerializableEntityIdReserver(r.0.into_inner())
    }
}

impl From<SerializableEntityIdReserver> for EntityIdReserver {
    fn from(r: SerializableEntityIdReserver) -> Self {
        EntityIdReserver(RefCell::new(r.0))
    }
}

enum MainMenuSelection {
    NewGame,
    Quit,
    Continue,
    SaveAndQuit,
}

pub struct GameCtx<Renderer: KnowledgeRenderer, Input: InputSource> {
    renderer: RefCell<Renderer>,
    input_source: Input,
    turn_id: u64,
    action_id: u64,
    pc_observer: Shadowcast,
    behaviour_ctx: BehaviourCtx<Renderer>,
    rule_reactions: Vec<Reaction>,
    ecs_action: EcsAction,
    action_schedule: Schedule<ActionArgs>,
    width: usize,
    height: usize,
    rng: GameRng,
    language: Box<Language>,
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct GlobalIds {
    pc_id: EntityId,
    level_id: LevelId,
}

pub struct GameState {
    levels: LevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: EntityIdReserver,
}

impl GameState {
    pub fn new() -> Self {
        GameState {
            levels: LevelTable::new(),
            global_ids: None,
            entity_ids: EntityIdReserver::new(),
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableGameState {
    levels: SerializableLevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: SerializableEntityIdReserver,
}

impl From<GameState> for SerializableGameState {
    fn from(game_state: GameState) -> Self {
        let GameState { levels, global_ids, entity_ids } = game_state;
        SerializableGameState {
            levels: SerializableLevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: SerializableEntityIdReserver::from(entity_ids),
        }
    }
}

impl From<SerializableGameState> for GameState {
    fn from(game_state: SerializableGameState) -> Self {
        let SerializableGameState { levels, global_ids, entity_ids } = game_state;
        GameState {
            levels: LevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: EntityIdReserver::from(entity_ids),
        }
    }
}

impl<Renderer: KnowledgeRenderer, Input: 'static + InputSource + Clone> GameCtx<Renderer, Input> {
    pub fn new(renderer: Renderer, input_source: Input, seed: usize, width: usize, height: usize) -> Self {
        GameCtx {
            renderer: RefCell::new(renderer),
            input_source: input_source.clone(),
            turn_id: 0,
            action_id: 0,
            pc_observer: Shadowcast::new(),
            behaviour_ctx: BehaviourCtx::new(input_source),
            rule_reactions: Vec::new(),
            ecs_action: EcsAction::new(),
            action_schedule: Schedule::new(),
            width: width,
            height: height,
            rng: GameRng::new(seed),
            language: Box::new(languages::English),
        }
    }

    pub fn run(&mut self, args: Arguments) -> GameResult<()> {

        let mut current_game_state = save_file::load(args.user_path.as_path());

        loop {

            let control_map = control_spec::from_file(args.user_path.join(user_files::CONTROL)).unwrap_or_default();

            let mut menu = Menu::new();

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::Continue, MainMenuSelection::Continue));
            }

            menu.push(MenuItem::new(MenuMessageType::NewGame, MainMenuSelection::NewGame));

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::SaveAndQuit, MainMenuSelection::SaveAndQuit));
            } else {
                menu.push(MenuItem::new(MenuMessageType::Quit, MainMenuSelection::Quit));
            }

            let item = menu_operation::run(
                self.renderer.borrow_mut().deref_mut(),
                &mut self.input_source,
                Some(MessageType::Title),
                &self.language,
                menu);

            let mut game_state = match item {
                MainMenuSelection::Quit => {
                    return Ok(());
                }
                MainMenuSelection::SaveAndQuit => {
                    let game_state = current_game_state.take().expect("Missing game state");
                    save_file::save(args.user_path.as_path(), game_state);
                    return Ok(());
                }
                MainMenuSelection::NewGame => {
                    let mut game_state = GameState::new();

                    self.init_demo(&mut game_state);
                    self.intro_message(&control_map);
                    self.welcome_message(&game_state);

                    game_state
                }
                MainMenuSelection::Continue => current_game_state.take().expect("Missing game state"),
            };

            Self::install_control_map(&mut game_state, control_map);

            self.game_loop(&mut game_state)?;

            current_game_state = Some(game_state);
        }
    }

    fn install_control_map(game_state: &mut GameState, control_map: ControlMap) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

        let level = game_state.levels.level_mut(level_id);
        level.ecs.insert_control_map(pc_id, control_map);
    }

    fn game_loop(&mut self, game_state: &mut GameState) -> GameResult<()> {
        loop {

            let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

            self.turn_id += 1;

            let resolution = {
                let level = game_state.levels.level_mut(level_id);
                if let Some(turn_event) = level.turn_schedule.next() {

                    TurnEnv {
                        turn_id: self.turn_id,
                        action_id: &mut self.action_id,
                        level_id: level_id,
                        entity_id: turn_event.event,
                        pc_id: pc_id,
                        renderer: &self.renderer,
                        ecs: &mut level.ecs,
                        spatial_hash: &mut level.spatial_hash,
                        behaviour_ctx: &self.behaviour_ctx,
                        rule_reactions: &mut self.rule_reactions,
                        ecs_action: &mut self.ecs_action,
                        action_schedule: &mut self.action_schedule,
                        turn_schedule: &mut level.turn_schedule,
                        pc_observer: &self.pc_observer,
                        entity_ids: &game_state.entity_ids,
                        rng: &self.rng,
                        language: &self.language,
                    }.turn()?

                } else {
                    return Err(GameError::ScheduleEmpty);
                }
            };

            match resolution {
                TurnResolution::Quit(entity_id) => {
                    let level = game_state.levels.level_mut(level_id);
                    let ticket = level.turn_schedule.insert(entity_id, 0);
                    level.ecs.insert_schedule_ticket(entity_id, ticket);
                    return Ok(());
                }
                TurnResolution::Schedule(entity_id, delay) => {
                    let level = game_state.levels.level_mut(level_id);
                    let ticket = level.turn_schedule.insert(entity_id, delay);
                    level.ecs.insert_schedule_ticket(entity_id, ticket);
                }
                TurnResolution::LevelSwitch(level_switch) => {
                    self.switch_level(level_switch, game_state);
                }
            }
        }
    }

    fn welcome_message(&self, game_state: &GameState) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Unitialised game state");

        let ref ecs = game_state.levels.level(level_id).ecs;
        ecs.message_log_borrow_mut(pc_id).unwrap().add(MessageType::Welcome);
    }

    fn intro_message(&mut self, control_map: &ControlMap) {

        let mut message = Message::new();

        self.language.translate(MessageType::Intro, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate(MessageType::PressAnyKey, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate_controls(control_map, &mut message);

        display_message_scrolling(self.renderer.borrow_mut().deref_mut(), &mut self.input_source, &message, true);
    }

    fn init_demo(&mut self, game_state: &mut GameState) {

        let pc_id = game_state.entity_ids.new_id();

        let mut action = EcsAction::new();
        prototypes::pc(action.entity_mut(pc_id), Coord::new(0, 0));

        let level = Level::new_with_pc(TerrainType::DemoA,
                                       pc_id,
                                       &mut action,
                                       &game_state.entity_ids,
                                       &self.rng,
                                       self.action_id);

        self.action_id += 1;

        let level_id = game_state.levels.add_level(level);

        game_state.global_ids = Some(GlobalIds {
            pc_id: pc_id,
            level_id: level_id,
        });
    }

    fn switch_level(&mut self, level_switch: LevelSwitch, game_state: &mut GameState) {
        let global_ids = game_state.global_ids.as_mut().expect("Unitialised game state");

        let mut pc_insert = EcsAction::new();
        let mut pc_remove = EcsAction::new();

        {
            let current_level = game_state.levels.level_mut(global_ids.level_id);
            pc_remove.remove_entity_by_id(global_ids.pc_id, &current_level.ecs);
            current_level.commit_into(&mut pc_remove, &mut pc_insert, self.action_id);
        }

        self.action_id += 1;

        let new_level = Level::new_with_pc(level_switch.terrain_type,
                                           global_ids.pc_id,
                                           &mut pc_insert,
                                           &game_state.entity_ids,
                                           &self.rng,
                                           self.action_id);
        self.action_id += 1;

        global_ids.level_id = game_state.levels.add_level(new_level);
    }
}
<commit_msg>Turn and action ids are saved<commit_after>use std::cell::RefCell;
use std::ops::DerefMut;

use game::*;
use game::data::*;
use ecs::*;
use util::{LeakyReserver, Schedule};
use coord::Coord;

pub struct EntityIdReserver(RefCell<LeakyReserver<EntityId>>);

impl EntityIdReserver {
    pub fn new() -> Self {
        EntityIdReserver(RefCell::new(LeakyReserver::new()))
    }

    pub fn new_id(&self) -> EntityId {
        self.0.borrow_mut().reserve()
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableEntityIdReserver(LeakyReserver<EntityId>);

impl From<EntityIdReserver> for SerializableEntityIdReserver {
    fn from(r: EntityIdReserver) -> Self {
        SerializableEntityIdReserver(r.0.into_inner())
    }
}

impl From<SerializableEntityIdReserver> for EntityIdReserver {
    fn from(r: SerializableEntityIdReserver) -> Self {
        EntityIdReserver(RefCell::new(r.0))
    }
}

enum MainMenuSelection {
    NewGame,
    Quit,
    Continue,
    SaveAndQuit,
}

pub struct GameCtx<Renderer: KnowledgeRenderer, Input: InputSource> {
    renderer: RefCell<Renderer>,
    input_source: Input,
    pc_observer: Shadowcast,
    behaviour_ctx: BehaviourCtx<Renderer>,
    rule_reactions: Vec<Reaction>,
    ecs_action: EcsAction,
    action_schedule: Schedule<ActionArgs>,
    width: usize,
    height: usize,
    rng: GameRng,
    language: Box<Language>,
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct GlobalIds {
    pc_id: EntityId,
    level_id: LevelId,
}

pub struct GameState {
    levels: LevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: EntityIdReserver,
    turn_id: u64,
    action_id: u64,
}

impl GameState {
    pub fn new() -> Self {
        GameState {
            levels: LevelTable::new(),
            global_ids: None,
            entity_ids: EntityIdReserver::new(),
            turn_id: 0,
            action_id: 0,
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct SerializableGameState {
    levels: SerializableLevelTable,
    global_ids: Option<GlobalIds>,
    entity_ids: SerializableEntityIdReserver,
    turn_id: u64,
    action_id: u64,
}

impl From<GameState> for SerializableGameState {
    fn from(game_state: GameState) -> Self {
        let GameState { levels, global_ids, entity_ids, turn_id, action_id } = game_state;
        SerializableGameState {
            levels: SerializableLevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: SerializableEntityIdReserver::from(entity_ids),
            turn_id: turn_id,
            action_id: action_id,
        }
    }
}

impl From<SerializableGameState> for GameState {
    fn from(game_state: SerializableGameState) -> Self {
        let SerializableGameState { levels, global_ids, entity_ids, turn_id, action_id } = game_state;
        GameState {
            levels: LevelTable::from(levels),
            global_ids: global_ids,
            entity_ids: EntityIdReserver::from(entity_ids),
            turn_id: turn_id,
            action_id: action_id,
        }
    }
}

impl<Renderer: KnowledgeRenderer, Input: 'static + InputSource + Clone> GameCtx<Renderer, Input> {
    pub fn new(renderer: Renderer, input_source: Input, seed: usize, width: usize, height: usize) -> Self {
        GameCtx {
            renderer: RefCell::new(renderer),
            input_source: input_source.clone(),
            pc_observer: Shadowcast::new(),
            behaviour_ctx: BehaviourCtx::new(input_source),
            rule_reactions: Vec::new(),
            ecs_action: EcsAction::new(),
            action_schedule: Schedule::new(),
            width: width,
            height: height,
            rng: GameRng::new(seed),
            language: Box::new(languages::English),
        }
    }

    pub fn run(&mut self, args: Arguments) -> GameResult<()> {

        let mut current_game_state = save_file::load(args.user_path.as_path());

        loop {

            let control_map = control_spec::from_file(args.user_path.join(user_files::CONTROL)).unwrap_or_default();

            let mut menu = Menu::new();

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::Continue, MainMenuSelection::Continue));
            }

            menu.push(MenuItem::new(MenuMessageType::NewGame, MainMenuSelection::NewGame));

            if current_game_state.is_some() {
                menu.push(MenuItem::new(MenuMessageType::SaveAndQuit, MainMenuSelection::SaveAndQuit));
            } else {
                menu.push(MenuItem::new(MenuMessageType::Quit, MainMenuSelection::Quit));
            }

            let item = menu_operation::run(
                self.renderer.borrow_mut().deref_mut(),
                &mut self.input_source,
                Some(MessageType::Title),
                &self.language,
                menu);

            let mut game_state = match item {
                MainMenuSelection::Quit => {
                    return Ok(());
                }
                MainMenuSelection::SaveAndQuit => {
                    let game_state = current_game_state.take().expect("Missing game state");
                    save_file::save(args.user_path.as_path(), game_state);
                    return Ok(());
                }
                MainMenuSelection::NewGame => {
                    let mut game_state = GameState::new();

                    self.init_demo(&mut game_state);
                    self.intro_message(&control_map);
                    self.welcome_message(&game_state);

                    game_state
                }
                MainMenuSelection::Continue => current_game_state.take().expect("Missing game state"),
            };

            Self::install_control_map(&mut game_state, control_map);

            self.game_loop(&mut game_state)?;

            current_game_state = Some(game_state);
        }
    }

    fn install_control_map(game_state: &mut GameState, control_map: ControlMap) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

        let level = game_state.levels.level_mut(level_id);
        level.ecs.insert_control_map(pc_id, control_map);
    }

    fn game_loop(&mut self, game_state: &mut GameState) -> GameResult<()> {
        loop {

            let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Uninitialised game state");

            game_state.turn_id += 1;

            let resolution = {
                let level = game_state.levels.level_mut(level_id);
                if let Some(turn_event) = level.turn_schedule.next() {

                    TurnEnv {
                        turn_id: game_state.turn_id,
                        action_id: &mut game_state.action_id,
                        level_id: level_id,
                        entity_id: turn_event.event,
                        pc_id: pc_id,
                        renderer: &self.renderer,
                        ecs: &mut level.ecs,
                        spatial_hash: &mut level.spatial_hash,
                        behaviour_ctx: &self.behaviour_ctx,
                        rule_reactions: &mut self.rule_reactions,
                        ecs_action: &mut self.ecs_action,
                        action_schedule: &mut self.action_schedule,
                        turn_schedule: &mut level.turn_schedule,
                        pc_observer: &self.pc_observer,
                        entity_ids: &game_state.entity_ids,
                        rng: &self.rng,
                        language: &self.language,
                    }.turn()?

                } else {
                    return Err(GameError::ScheduleEmpty);
                }
            };

            match resolution {
                TurnResolution::Quit(entity_id) => {
                    let level = game_state.levels.level_mut(level_id);
                    let ticket = level.turn_schedule.insert(entity_id, 0);
                    level.ecs.insert_schedule_ticket(entity_id, ticket);
                    return Ok(());
                }
                TurnResolution::Schedule(entity_id, delay) => {
                    let level = game_state.levels.level_mut(level_id);
                    let ticket = level.turn_schedule.insert(entity_id, delay);
                    level.ecs.insert_schedule_ticket(entity_id, ticket);
                }
                TurnResolution::LevelSwitch(level_switch) => {
                    self.switch_level(level_switch, game_state);
                }
            }
        }
    }

    fn welcome_message(&self, game_state: &GameState) {
        let GlobalIds { pc_id, level_id } = game_state.global_ids.expect("Unitialised game state");

        let ref ecs = game_state.levels.level(level_id).ecs;
        ecs.message_log_borrow_mut(pc_id).unwrap().add(MessageType::Welcome);
    }

    fn intro_message(&mut self, control_map: &ControlMap) {

        let mut message = Message::new();

        self.language.translate(MessageType::Intro, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate(MessageType::PressAnyKey, &mut message);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        message.push(MessagePart::Newline);
        self.language.translate_controls(control_map, &mut message);

        display_message_scrolling(self.renderer.borrow_mut().deref_mut(), &mut self.input_source, &message, true);
    }

    fn init_demo(&mut self, game_state: &mut GameState) {

        let pc_id = game_state.entity_ids.new_id();

        let mut action = EcsAction::new();
        prototypes::pc(action.entity_mut(pc_id), Coord::new(0, 0));

        let level = Level::new_with_pc(TerrainType::DemoA,
                                       pc_id,
                                       &mut action,
                                       &game_state.entity_ids,
                                       &self.rng,
                                       game_state.action_id);

        game_state.action_id += 1;

        let level_id = game_state.levels.add_level(level);

        game_state.global_ids = Some(GlobalIds {
            pc_id: pc_id,
            level_id: level_id,
        });
    }

    fn switch_level(&mut self, level_switch: LevelSwitch, game_state: &mut GameState) {
        let global_ids = game_state.global_ids.as_mut().expect("Unitialised game state");

        let mut pc_insert = EcsAction::new();
        let mut pc_remove = EcsAction::new();

        {
            let current_level = game_state.levels.level_mut(global_ids.level_id);
            pc_remove.remove_entity_by_id(global_ids.pc_id, &current_level.ecs);
            current_level.commit_into(&mut pc_remove, &mut pc_insert, game_state.action_id);
        }

        game_state.action_id += 1;

        let new_level = Level::new_with_pc(level_switch.terrain_type,
                                           global_ids.pc_id,
                                           &mut pc_insert,
                                           &game_state.entity_ids,
                                           &self.rng,
                                           game_state.action_id);
        game_state.action_id += 1;

        global_ids.level_id = game_state.levels.add_level(new_level);
    }
}
<|endoftext|>


<commit_before>use enum_primitive::FromPrimitive;

use map;
use room::Room;
use platform::random_i32;
use direction::Direction::*;

use self::RoomId::*;
use self::RoomContents::*;

const DEBUG: bool = false;
const NUM_ROOMS: usize = 19;
const NUM_MONSTERS: usize = 4;
const NUM_ROOMS_WITH_TREASURE: usize = 4;
const NUM_ROOMS_WITH_TERROR: usize = 4;
const MIN_TREASURE_AMOUNT: u8 = 10;
const MAX_TREASURE_AMOUNT: u8 = 110;

enum_from_primitive! {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RoomId {
  Hallway = 0,
  AudienceChamber = 1,
  GreatHall = 2,
  PrivateMeeting = 3,
  InnerHallway = 4,
  Entrance = 5,
  Kitchen = 6,
  StoreRoom = 7,
  Lift = 8,
  RearVestibule = 9,
  Exit = 10,
  Dungeon = 11,
  Guardroom = 12,
  MasterBedroom = 13,
  UpperHallway = 14,
  Treasury = 15,
  ChambermaidsBedroom = 16,
  DressingChamber = 17,
  SmallRoom = 18,
}
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone)]
pub enum MonsterId {
  Werewolf = 0,
  Fleshgorger = 1,
  Maldemer = 2,
  Dragon = 3,
}
}

#[derive(Debug, Copy, Clone)]
pub enum RoomContents {
  Treasure(u8),
  Terror(MonsterId),
}

pub type GameMap<'a> = map::Map<'a, RoomId, RoomContents>;

impl<'a> GameMap<'a> {
  pub fn create_rooms() -> [Room<RoomId, RoomContents>; NUM_ROOMS] {
    [Room::new(); NUM_ROOMS]
  }

  pub fn populate(&mut self) {
    self.describe_and_connect();
    self.allot_treasure();
    self.allot_terror();
    self.ensure_treasure();
  }

  fn allot_terror(&mut self) {
    for _ in 0..NUM_ROOMS_WITH_TERROR {
      loop {
        let room_id = random_room_id();
        if room_id != Entrance && room_id != Exit {
          let room = self.room(room_id);
          if room.contents.is_none() {
            let monster_id = random_monster_id();
            if DEBUG {
              println!("DEBUG: Placing {:?} in {:?}.", monster_id, room_id);
            }
            room.contents = Some(Terror(monster_id));
            break;
          }
        }
      }
    }
  }

  fn allot_treasure(&mut self) {
    for _ in 0..NUM_ROOMS_WITH_TREASURE {
      loop {
        let room_id = random_room_id();
        if room_id != Entrance && room_id != Exit {
          let room = self.room(room_id);
          if room.contents.is_none() {
            let amount = random_treasure_amount();
            if DEBUG {
              println!("DEBUG: Placing ${} in {:?}.", amount, room_id);
            }
            room.contents = Some(Treasure(amount));
            break;
          }
        }
      }
    }
  }

  fn ensure_treasure(&mut self) {
    for &room_id in [Treasury, PrivateMeeting].iter() {
      let amount = random_treasure_amount();
      if DEBUG {
        println!("DEBUG: Placing ${} in {:?}.", amount, room_id);
      }
      self.room(room_id).contents = Some(Treasure(amount));
    }
  }

  fn describe_and_connect(&mut self) {
    self.room(Entrance).describe(
      "Entrance",
      "You are at the entrance to a forbidding-looking \
       stone castle. You are facing east."
    );

    self.room(Hallway).describe(
      "Hallway",
      "You are in the hallway. \
       There is a door to the south. \
       Through the windows to the north you can see a secret herb garden."
    );

    self.room(AudienceChamber).describe(
      "Audience Chamber",
      "This is the audience chamber. \
       There is a window to the west. By looking to the right \
       through it you can see the entrance to the castle. \
       Doors leave this room to the north, east, and south."
    );

    self.connect(Entrance, East, Hallway);
    self.connect(Hallway, South, AudienceChamber);
  }
}

fn random_room_id() -> RoomId {
  loop {
    let r = random_i32(0, NUM_ROOMS as i32);
    match RoomId::from_i32(r) {
      Some(room_id) => { return room_id; },
      None => {}
    }
  }
}

fn random_monster_id() -> MonsterId {
  loop {
    let r = random_i32(0, NUM_MONSTERS as i32);
    match MonsterId::from_i32(r) {
      Some(monster_id) => { return monster_id; },
      None => {}
    }
  }
}

fn random_treasure_amount() -> u8 {
  random_i32(MIN_TREASURE_AMOUNT as i32, MAX_TREASURE_AMOUNT as i32) as u8
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl map::MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}
<commit_msg>Factor out GameMap::allot().<commit_after>use enum_primitive::FromPrimitive;

use map;
use room::Room;
use platform::random_i32;
use direction::Direction::*;

use self::RoomId::*;
use self::RoomContents::*;

const DEBUG: bool = false;
const NUM_ROOMS: usize = 19;
const NUM_MONSTERS: usize = 4;
const NUM_ROOMS_WITH_TREASURE: usize = 4;
const NUM_ROOMS_WITH_TERROR: usize = 4;
const MIN_TREASURE_AMOUNT: u8 = 10;
const MAX_TREASURE_AMOUNT: u8 = 110;

enum_from_primitive! {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RoomId {
  Hallway = 0,
  AudienceChamber = 1,
  GreatHall = 2,
  PrivateMeeting = 3,
  InnerHallway = 4,
  Entrance = 5,
  Kitchen = 6,
  StoreRoom = 7,
  Lift = 8,
  RearVestibule = 9,
  Exit = 10,
  Dungeon = 11,
  Guardroom = 12,
  MasterBedroom = 13,
  UpperHallway = 14,
  Treasury = 15,
  ChambermaidsBedroom = 16,
  DressingChamber = 17,
  SmallRoom = 18,
}
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone)]
pub enum MonsterId {
  Werewolf = 0,
  Fleshgorger = 1,
  Maldemer = 2,
  Dragon = 3,
}
}

#[derive(Debug, Copy, Clone)]
pub enum RoomContents {
  Treasure(u8),
  Terror(MonsterId),
}

pub type GameMap<'a> = map::Map<'a, RoomId, RoomContents>;

impl<'a> GameMap<'a> {
  pub fn create_rooms() -> [Room<RoomId, RoomContents>; NUM_ROOMS] {
    [Room::new(); NUM_ROOMS]
  }

  pub fn populate(&mut self) {
    self.describe_and_connect();
    self.allot_treasure();
    self.allot_terror();
    self.ensure_treasure();
  }

  fn allot<F>(&mut self, num_rooms: usize, allotter: F)
    where F: Fn() -> RoomContents
  {
    for _ in 0..num_rooms {
      loop {
        let room_id = random_room_id();
        if room_id != Entrance && room_id != Exit {
          let room = self.room(room_id);
          if room.contents.is_none() {
            let contents = allotter();
            if DEBUG {
              println!("DEBUG: Placing {:?} in {:?}.", contents, room_id);
            }
            room.contents = Some(contents);
            break;
          }
        }
      }
    }
  }

  fn allot_terror(&mut self) {
    self.allot(NUM_ROOMS_WITH_TERROR, || Terror(random_monster_id()))
  }

  fn allot_treasure(&mut self) {
    self.allot(NUM_ROOMS_WITH_TREASURE,
               || Treasure(random_treasure_amount()))
  }

  fn ensure_treasure(&mut self) {
    for &room_id in [Treasury, PrivateMeeting].iter() {
      let amount = random_treasure_amount();
      if DEBUG {
        println!("DEBUG: Placing ${} in {:?}.", amount, room_id);
      }
      self.room(room_id).contents = Some(Treasure(amount));
    }
  }

  fn describe_and_connect(&mut self) {
    self.room(Entrance).describe(
      "Entrance",
      "You are at the entrance to a forbidding-looking \
       stone castle. You are facing east."
    );

    self.room(Hallway).describe(
      "Hallway",
      "You are in the hallway. \
       There is a door to the south. \
       Through the windows to the north you can see a secret herb garden."
    );

    self.room(AudienceChamber).describe(
      "Audience Chamber",
      "This is the audience chamber. \
       There is a window to the west. By looking to the right \
       through it you can see the entrance to the castle. \
       Doors leave this room to the north, east, and south."
    );

    self.connect(Entrance, East, Hallway);
    self.connect(Hallway, South, AudienceChamber);
  }
}

fn random_room_id() -> RoomId {
  loop {
    let r = random_i32(0, NUM_ROOMS as i32);
    match RoomId::from_i32(r) {
      Some(room_id) => { return room_id; },
      None => {}
    }
  }
}

fn random_monster_id() -> MonsterId {
  loop {
    let r = random_i32(0, NUM_MONSTERS as i32);
    match MonsterId::from_i32(r) {
      Some(monster_id) => { return monster_id; },
      None => {}
    }
  }
}

fn random_treasure_amount() -> u8 {
  random_i32(MIN_TREASURE_AMOUNT as i32, MAX_TREASURE_AMOUNT as i32) as u8
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl map::MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use crate::gpio::{
    interrupt::{AsyncInterrupt, EventLoop},
    mem::GpioMem,
    Level, Mode,
    PullUpDown::{self, *},
    Result, Trigger,
};

// Maximum GPIO pins on the BCM2835. The actual number of pins
// exposed through the Pi's GPIO header depends on the model.
pub const MAX: usize = 54;

/// Unconfigured GPIO pin.
#[derive(Debug)]
pub struct Pin {
    pub(crate) pin: u8,
    event_loop: Arc<Mutex<EventLoop>>,
    gpio_mem: Arc<GpioMem>,
    gpio_cdev: Arc<File>,
}

impl Pin {
    #[inline]
    pub(crate) fn new(
        pin: u8,
        event_loop: Arc<Mutex<EventLoop>>,
        gpio_mem: Arc<GpioMem>,
        gpio_cdev: Arc<File>,
    ) -> Pin {
        Pin {
            pin,
            event_loop,
            gpio_mem,
            gpio_cdev,
        }
    }

    /// Mutably borrows the pin as an [`InputPin`] and sets its mode to [`Mode::Input`].
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    #[inline]
    pub fn as_input(&mut self) -> InputPin {
        InputPin::new(self, Off)
    }

    /// Additionally to `as_input`, activates the pin's pull-up resistor.
    #[inline]
    pub fn as_input_pullup(&mut self) -> InputPin {
        InputPin::new(self, PullUp)
    }

    /// Additionally to `as_input`, activates the pin's pull-down resistor.
    #[inline]
    pub fn as_input_pulldown(&mut self) -> InputPin {
        InputPin::new(self, PullDown)
    }

    /// Mutably borrows the pin as an [`OutputPin`] and sets its mode to [`Mode::Output`].
    ///
    /// [`OutputPin`]: struct.OutputPin.html
    /// [`Mode::Output`]: enum.Mode.html#variant.Output
    #[inline]
    pub fn as_output(&mut self) -> OutputPin {
        OutputPin::new(self)
    }

    /// Mutably borrows the pin as an [`AltPin`] and sets its mode to the given mode.
    ///
    /// [`AltPin`]: struct.AltPin.html
    /// [`Mode`]: enum.Mode.html
    #[inline]
    pub fn as_alt(&mut self, mode: Mode) -> AltPin {
        AltPin::new(self, mode)
    }

    #[inline]
    pub(crate) fn set_mode(&mut self, mode: Mode) {
        (*self.gpio_mem).set_mode(self.pin, mode);
    }

    /// Returns the current GPIO pin mode.
    #[inline]
    pub fn mode(&self) -> Mode {
        (*self.gpio_mem).mode(self.pin)
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    #[inline]
    pub(crate) fn set_pullupdown(&self, pud: PullUpDown) {
        (*self.gpio_mem).set_pullupdown(self.pin, pud);
    }

    #[inline]
    pub(crate) fn read(&self) -> Level {
        (*self.gpio_mem).level(self.pin)
    }

    #[inline]
    pub(crate) fn set_low(&mut self) {
        (*self.gpio_mem).set_low(self.pin);
    }

    #[inline]
    pub(crate) fn set_high(&mut self) {
        (*self.gpio_mem).set_high(self.pin);
    }

    #[inline]
    pub(crate) fn write(&mut self, level: Level) {
        match level {
            Level::Low => self.set_low(),
            Level::High => self.set_high(),
        };
    }
}

macro_rules! impl_input {
    () => {
        /// Reads the pin's current logic level.
        #[inline]
        pub fn read(&self) -> Level {
            self.pin.read()
        }
    }
}

macro_rules! impl_output {
    () => {
        /// Sets pin's logic level to low.
        #[inline]
        pub fn set_low(&mut self) {
            self.pin.set_low()
        }

        /// Sets pin's logic level to high.
        #[inline]
        pub fn set_high(&mut self) {
            self.pin.set_high()
        }

        /// Sets pin's logic level.
        #[inline]
        pub fn write(&mut self, level: Level) {
            self.pin.write(level)
        }
    }
}

macro_rules! impl_drop {
    ($struct:ident) => {
        impl<'a> $struct<'a> {
            /// Returns the value of `clear_on_drop`.
            pub fn clear_on_drop(&self) -> bool {
                self.clear_on_drop
            }

            /// When enabled, resets pin's mode to its original state when it goes out of scope.
            /// By default, this is set to `true`.
            ///
            /// # Note
            ///
            /// Drop methods aren't called when a program is abnormally terminated, for
            /// instance when a user presses Ctrl + C, and the `SIGINT` signal
            /// isn't caught. You catch those using crates such as [`simple_signal`].
            ///
            /// [`simple_signal`]: https://crates.io/crates/simple-signal
            /// [`cleanup`]: #method.cleanup
            pub fn set_clear_on_drop(&mut self, clear_on_drop: bool) {
                self.clear_on_drop = clear_on_drop;
            }
        }

        impl<'a> Drop for $struct<'a> {
            /// Resets the pin's mode if `clear_on_drop` is set to `true` (default).
            fn drop(&mut self) {
                if !self.clear_on_drop {
                    return;
                }

                if let Some(prev_mode) = self.prev_mode {
                    self.pin.set_mode(prev_mode)
                }
            }
        }
    };
}

/// GPIO pin configured as input.
#[derive(Debug)]
pub struct InputPin<'a> {
    pub(crate) pin: &'a mut Pin,
    prev_mode: Option<Mode>,
    async_interrupt: Option<AsyncInterrupt>,
    clear_on_drop: bool,
}

impl<'a> InputPin<'a> {
    pub(crate) fn new(pin: &'a mut Pin, pud_mode: PullUpDown) -> InputPin<'a> {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Input {
            None
        } else {
            pin.set_mode(Mode::Input);
            Some(prev_mode)
        };

        pin.set_pullupdown(pud_mode);

        InputPin {
            pin,
            prev_mode,
            async_interrupt: None,
            clear_on_drop: true,
        }
    }

    impl_input!();

    /// Configures a synchronous interrupt trigger.
    ///
    /// After configuring a synchronous interrupt trigger, you can use
    /// [`poll_interrupt`] to wait for a trigger event.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`poll_interrupt`]: #method.poll_interrupt
    pub fn set_interrupt(&mut self, trigger: Trigger) -> Result<()> {
        self.clear_async_interrupt()?;

        // Each pin can only be configured for a single trigger type
        (*self.pin.event_loop.lock().unwrap()).set_interrupt(self.pin.pin, trigger)
    }

    /// Removes a previously configured synchronous interrupt trigger.
    pub fn clear_interrupt(&mut self) -> Result<()> {
        (*self.pin.event_loop.lock().unwrap()).clear_interrupt(self.pin.pin)
    }

    /// Blocks until an interrupt is triggered on the pin, or until a timeout occurs.
    ///
    /// This only works after the pin has been configured for synchronous interrupts using
    /// [`set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to `poll_interrupt`.
    /// If `reset` is set too `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// [`set_interrupt`]: #method.set_interrupt
    pub fn poll_interrupt(
        &mut self,
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<Level>> {
        let opt = (*self.pin.event_loop.lock().unwrap()).poll(&[self], reset, timeout)?;

        if let Some(trigger) = opt {
            Ok(Some(trigger.1))
        } else {
            Ok(None)
        }
    }

    /// Configures an asynchronous interrupt trigger, which will execute the callback on a
    /// separate thread when the interrupt is triggered.
    ///
    /// The callback closure or function pointer is called with a single [`Level`] argument.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`Level`]: enum.Level.html
    pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
    where
        C: FnMut(Level) + Send + 'static,
    {
        self.clear_interrupt()?;
        self.clear_async_interrupt()?;

        self.async_interrupt = Some(AsyncInterrupt::new(
            (*self.pin.gpio_cdev).as_raw_fd(),
            self.pin.pin,
            trigger,
            callback,
        )?);

        Ok(())
    }

    pub fn clear_async_interrupt(&mut self) -> Result<()> {
        if let Some(mut interrupt) = self.async_interrupt.take() {
            interrupt.stop()?;
        }

        Ok(())
    }
}

impl_drop!(InputPin);

/// GPIO pin configured as output.
#[derive(Debug)]
pub struct OutputPin<'a> {
    pin: &'a mut Pin,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
}

impl<'a> OutputPin<'a> {
    pub(crate) fn new(pin: &'a mut Pin) -> OutputPin<'a> {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Output {
            None
        } else {
            pin.set_mode(Mode::Output);
            Some(prev_mode)
        };

        OutputPin {
            pin,
            prev_mode,
            clear_on_drop: true,
        }
    }

    impl_input!();
    impl_output!();
}

impl_drop!(OutputPin);

/// GPIO pin configured with an alternate function.
#[derive(Debug)]
pub struct AltPin<'a> {
    pin: &'a mut Pin,
    mode: Mode,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
}

impl<'a> AltPin<'a> {
    pub(crate) fn new(pin: &'a mut Pin, mode: Mode) -> AltPin<'a> {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == mode {
            None
        } else {
            pin.set_mode(mode);
            Some(prev_mode)
        };

        AltPin {
            pin,
            mode,
            prev_mode,
            clear_on_drop: true,
        }
    }

    impl_input!();
    impl_output!();
}
impl_drop!(AltPin);
<commit_msg>Add license<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use crate::gpio::{
    interrupt::{AsyncInterrupt, EventLoop},
    mem::GpioMem,
    Level, Mode,
    PullUpDown::{self, *},
    Result, Trigger,
};

// Maximum GPIO pins on the BCM2835. The actual number of pins
// exposed through the Pi's GPIO header depends on the model.
pub const MAX: usize = 54;

/// Unconfigured GPIO pin.
#[derive(Debug)]
pub struct Pin {
    pub(crate) pin: u8,
    event_loop: Arc<Mutex<EventLoop>>,
    gpio_mem: Arc<GpioMem>,
    gpio_cdev: Arc<File>,
}

impl Pin {
    #[inline]
    pub(crate) fn new(
        pin: u8,
        event_loop: Arc<Mutex<EventLoop>>,
        gpio_mem: Arc<GpioMem>,
        gpio_cdev: Arc<File>,
    ) -> Pin {
        Pin {
            pin,
            event_loop,
            gpio_mem,
            gpio_cdev,
        }
    }

    /// Mutably borrows the pin as an [`InputPin`] and sets its mode to [`Mode::Input`].
    ///
    /// [`InputPin`]: struct.InputPin.html
    /// [`Mode::Input`]: enum.Mode.html#variant.Input
    #[inline]
    pub fn as_input(&mut self) -> InputPin {
        InputPin::new(self, Off)
    }

    /// Additionally to `as_input`, activates the pin's pull-up resistor.
    #[inline]
    pub fn as_input_pullup(&mut self) -> InputPin {
        InputPin::new(self, PullUp)
    }

    /// Additionally to `as_input`, activates the pin's pull-down resistor.
    #[inline]
    pub fn as_input_pulldown(&mut self) -> InputPin {
        InputPin::new(self, PullDown)
    }

    /// Mutably borrows the pin as an [`OutputPin`] and sets its mode to [`Mode::Output`].
    ///
    /// [`OutputPin`]: struct.OutputPin.html
    /// [`Mode::Output`]: enum.Mode.html#variant.Output
    #[inline]
    pub fn as_output(&mut self) -> OutputPin {
        OutputPin::new(self)
    }

    /// Mutably borrows the pin as an [`AltPin`] and sets its mode to the given mode.
    ///
    /// [`AltPin`]: struct.AltPin.html
    /// [`Mode`]: enum.Mode.html
    #[inline]
    pub fn as_alt(&mut self, mode: Mode) -> AltPin {
        AltPin::new(self, mode)
    }

    #[inline]
    pub(crate) fn set_mode(&mut self, mode: Mode) {
        (*self.gpio_mem).set_mode(self.pin, mode);
    }

    /// Returns the current GPIO pin mode.
    #[inline]
    pub fn mode(&self) -> Mode {
        (*self.gpio_mem).mode(self.pin)
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    #[inline]
    pub(crate) fn set_pullupdown(&self, pud: PullUpDown) {
        (*self.gpio_mem).set_pullupdown(self.pin, pud);
    }

    #[inline]
    pub(crate) fn read(&self) -> Level {
        (*self.gpio_mem).level(self.pin)
    }

    #[inline]
    pub(crate) fn set_low(&mut self) {
        (*self.gpio_mem).set_low(self.pin);
    }

    #[inline]
    pub(crate) fn set_high(&mut self) {
        (*self.gpio_mem).set_high(self.pin);
    }

    #[inline]
    pub(crate) fn write(&mut self, level: Level) {
        match level {
            Level::Low => self.set_low(),
            Level::High => self.set_high(),
        };
    }
}

macro_rules! impl_input {
    () => {
        /// Reads the pin's current logic level.
        #[inline]
        pub fn read(&self) -> Level {
            self.pin.read()
        }
    }
}

macro_rules! impl_output {
    () => {
        /// Sets pin's logic level to low.
        #[inline]
        pub fn set_low(&mut self) {
            self.pin.set_low()
        }

        /// Sets pin's logic level to high.
        #[inline]
        pub fn set_high(&mut self) {
            self.pin.set_high()
        }

        /// Sets pin's logic level.
        #[inline]
        pub fn write(&mut self, level: Level) {
            self.pin.write(level)
        }
    }
}

macro_rules! impl_drop {
    ($struct:ident) => {
        impl<'a> $struct<'a> {
            /// Returns the value of `clear_on_drop`.
            pub fn clear_on_drop(&self) -> bool {
                self.clear_on_drop
            }

            /// When enabled, resets pin's mode to its original state when it goes out of scope.
            /// By default, this is set to `true`.
            ///
            /// # Note
            ///
            /// Drop methods aren't called when a program is abnormally terminated, for
            /// instance when a user presses Ctrl + C, and the `SIGINT` signal
            /// isn't caught. You catch those using crates such as [`simple_signal`].
            ///
            /// [`simple_signal`]: https://crates.io/crates/simple-signal
            /// [`cleanup`]: #method.cleanup
            pub fn set_clear_on_drop(&mut self, clear_on_drop: bool) {
                self.clear_on_drop = clear_on_drop;
            }
        }

        impl<'a> Drop for $struct<'a> {
            /// Resets the pin's mode if `clear_on_drop` is set to `true` (default).
            fn drop(&mut self) {
                if !self.clear_on_drop {
                    return;
                }

                if let Some(prev_mode) = self.prev_mode {
                    self.pin.set_mode(prev_mode)
                }
            }
        }
    };
}

/// GPIO pin configured as input.
#[derive(Debug)]
pub struct InputPin<'a> {
    pub(crate) pin: &'a mut Pin,
    prev_mode: Option<Mode>,
    async_interrupt: Option<AsyncInterrupt>,
    clear_on_drop: bool,
}

impl<'a> InputPin<'a> {
    pub(crate) fn new(pin: &'a mut Pin, pud_mode: PullUpDown) -> InputPin<'a> {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Input {
            None
        } else {
            pin.set_mode(Mode::Input);
            Some(prev_mode)
        };

        pin.set_pullupdown(pud_mode);

        InputPin {
            pin,
            prev_mode,
            async_interrupt: None,
            clear_on_drop: true,
        }
    }

    impl_input!();

    /// Configures a synchronous interrupt trigger.
    ///
    /// After configuring a synchronous interrupt trigger, you can use
    /// [`poll_interrupt`] to wait for a trigger event.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`poll_interrupt`]: #method.poll_interrupt
    pub fn set_interrupt(&mut self, trigger: Trigger) -> Result<()> {
        self.clear_async_interrupt()?;

        // Each pin can only be configured for a single trigger type
        (*self.pin.event_loop.lock().unwrap()).set_interrupt(self.pin.pin, trigger)
    }

    /// Removes a previously configured synchronous interrupt trigger.
    pub fn clear_interrupt(&mut self) -> Result<()> {
        (*self.pin.event_loop.lock().unwrap()).clear_interrupt(self.pin.pin)
    }

    /// Blocks until an interrupt is triggered on the pin, or until a timeout occurs.
    ///
    /// This only works after the pin has been configured for synchronous interrupts using
    /// [`set_interrupt`]. Asynchronous interrupt triggers are automatically polled on a separate thread.
    ///
    /// If `reset` is set to `false`, returns immediately if an interrupt trigger event was cached in a
    /// previous call to `poll_interrupt`.
    /// If `reset` is set too `true`, clears any cached interrupt trigger events before polling.
    ///
    /// The `timeout` duration indicates how long the call will block while waiting
    /// for interrupt trigger events, after which an `Ok(None))` is returned.
    /// `timeout` can be set to `None` to wait indefinitely.
    ///
    /// [`set_interrupt`]: #method.set_interrupt
    pub fn poll_interrupt(
        &mut self,
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<Option<Level>> {
        let opt = (*self.pin.event_loop.lock().unwrap()).poll(&[self], reset, timeout)?;

        if let Some(trigger) = opt {
            Ok(Some(trigger.1))
        } else {
            Ok(None)
        }
    }

    /// Configures an asynchronous interrupt trigger, which will execute the callback on a
    /// separate thread when the interrupt is triggered.
    ///
    /// The callback closure or function pointer is called with a single [`Level`] argument.
    ///
    /// Any previously configured (a)synchronous interrupt triggers will be cleared.
    ///
    /// [`Level`]: enum.Level.html
    pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
    where
        C: FnMut(Level) + Send + 'static,
    {
        self.clear_interrupt()?;
        self.clear_async_interrupt()?;

        self.async_interrupt = Some(AsyncInterrupt::new(
            (*self.pin.gpio_cdev).as_raw_fd(),
            self.pin.pin,
            trigger,
            callback,
        )?);

        Ok(())
    }

    pub fn clear_async_interrupt(&mut self) -> Result<()> {
        if let Some(mut interrupt) = self.async_interrupt.take() {
            interrupt.stop()?;
        }

        Ok(())
    }
}

impl_drop!(InputPin);

/// GPIO pin configured as output.
#[derive(Debug)]
pub struct OutputPin<'a> {
    pin: &'a mut Pin,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
}

impl<'a> OutputPin<'a> {
    pub(crate) fn new(pin: &'a mut Pin) -> OutputPin<'a> {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == Mode::Output {
            None
        } else {
            pin.set_mode(Mode::Output);
            Some(prev_mode)
        };

        OutputPin {
            pin,
            prev_mode,
            clear_on_drop: true,
        }
    }

    impl_input!();
    impl_output!();
}

impl_drop!(OutputPin);

/// GPIO pin configured with an alternate function.
#[derive(Debug)]
pub struct AltPin<'a> {
    pin: &'a mut Pin,
    mode: Mode,
    prev_mode: Option<Mode>,
    clear_on_drop: bool,
}

impl<'a> AltPin<'a> {
    pub(crate) fn new(pin: &'a mut Pin, mode: Mode) -> AltPin<'a> {
        let prev_mode = pin.mode();

        let prev_mode = if prev_mode == mode {
            None
        } else {
            pin.set_mode(mode);
            Some(prev_mode)
        };

        AltPin {
            pin,
            mode,
            prev_mode,
            clear_on_drop: true,
        }
    }

    impl_input!();
    impl_output!();
}
impl_drop!(AltPin);
<|endoftext|>


<commit_before>///  cairo_fill_rule_t is used to select how paths are filled. For both fill rules, whether or not a point is included in the fill is determined by taking a ray from that point to infinity and looking at intersections with the path. The ray can be in any direction, as long as it doesn't pass through the end point of a segment or have a tricky intersection such as intersecting tangent to the path. (Note that filling is not actually implemented in this way. This is just a description of the rule that is applied.)
///
/// The default fill rule is CAIRO_FILL_RULE_WINDING.
///
/// New entries may be added in future versions.
///
/// CAIRO_FILL_RULE_WINDING
/// 	If the path crosses the ray from left-to-right, counts +1. If the path crosses the ray from right to left, counts -1. (Left and right are determined from the perspective of looking along the ray from the starting point.) If the total count is non-zero, the point will be filled. (Since 1.0)
///
/// CAIRO_FILL_RULE_EVEN_ODD
/// 	Counts the total number of intersections, without regard to the orientation of the contour. If the total number of intersections is odd, the point will be filled. (Since 1.0)
///
/// Since 1.0
#[repr(i32)]
pub enum FillRule {
  Winding = 0,
  EvenOdd = 1
}

<commit_msg>Copied Cairo documentation for fill rule<commit_after>//! Describe which rule used to select how paths are filled.

///  cairo_fill_rule_t is used to select how paths are filled. For both fill rules, whether or not a point is included in the fill is determined by taking a ray from that point to infinity and looking at intersections with the path. The ray can be in any direction, as long as it doesn't pass through the end point of a segment or have a tricky intersection such as intersecting tangent to the path. (Note that filling is not actually implemented in this way. This is just a description of the rule that is applied.)
///
/// The default fill rule is CAIRO_FILL_RULE_WINDING.
///
/// New entries may be added in future versions.
///
/// Since 1.0
#[repr(i32)]
pub enum FillRule {
  /// If the path crosses the ray from left-to-right, counts +1. If the path crosses the ray from right to left, counts -1. (Left and right are determined from the perspective of looking along the ray from the starting point.) If the total count is non-zero, the point will be filled. (Since 1.0)
  Winding = 0,
  /// Counts the total number of intersections, without regard to the orientation of the contour. If the total number of intersections is odd, the point will be filled. (Since 1.0)
  EvenOdd = 1
}

<|endoftext|>


<commit_before>extern crate rand;
use rand::{Rng, SeedableRng, XorShiftRng};

pub const GRID_HEIGHT: i16 = 900;
pub const GRID_WIDTH: i16 = 1440;

pub const BLOCK_SIZE: i16 = 1;

pub const WINDOW_HEIGHT: i16 = GRID_HEIGHT * BLOCK_SIZE;
pub const WINDOW_WIDTH: i16 = GRID_WIDTH * BLOCK_SIZE;

pub const RAIN_SPARSENESS: i16 = 5;

pub const X: bool = true;
pub const o: bool = false;

#[derive(Eq, PartialEq, Clone)]
pub struct Loc {
    pub x: i16,
    pub y: i16
}

pub struct Map {pub map: Box<[[bool; GRID_HEIGHT as usize]; GRID_WIDTH as usize]>}

pub struct GameState {
    pub particles: Vec<Loc>,
    pub obstacles: Vec<Loc>,
    pub indexes_to_remove: Vec<usize>,
    pub map: Map, //2D array to check occupancy
    pub max_x: i16,
    pub max_y: i16,
    pub rng: XorShiftRng
}

impl Map {
    fn is_occupied(&self, x: i16, y: i16) -> bool {
        //Not valid is not occupied (false) to allow particles to leave the grid.
        //To prevent double bound checking with GameState::is_valid(), unwrap get()
        //and return defailt false on None
        if let Some(arr) = self.map.get(x as usize) {
            *arr.get(y as usize).unwrap_or(&false)
        } else {
            false
        }
    }

    fn get_neighbours(&mut self, x: i16, y: i16) -> [bool; 8] {
        [self.is_occupied(x-1, y-1), self.is_occupied(x, y-1), self.is_occupied(x+1, y-1),
        self.is_occupied(x-1, y), self.is_occupied(x+1, y),
        self.is_occupied(x-1, y+1), self.is_occupied(x, y+1), self.is_occupied(x-1, y+1)]
    }

    fn add_coord_map(&mut self, x: i16, y: i16) {
        self.map[x as usize][y as usize] = true;
    }

    fn remove_coord_map(&mut self, x: i16, y: i16) {
        self.map[x as usize][y as usize] = false;
    }
}

impl GameState {
    //check if point in inside the grid
    pub fn is_valid(x: i16, y: i16) -> bool {
        if x<0 || x>=GRID_WIDTH || y<0 || y>=GRID_HEIGHT {false} else {true}
    }

    pub fn update(&mut self) {
        //MAIN LOGIC
        for (index, particle) in self.particles.iter_mut().enumerate() {
            let (x, y) = (particle.x, particle.y);
            let neighbours = self.map.get_neighbours(x, y);
            let (x_new, y_new) = match neighbours {
               /*above  side below*/
                [_,_,_, _,_, _,o,_] => ( x, y+1 ),
                [_,X,_, o,X, _,X,_] => ( x-1, y ),
                [_,X,_, X,o, _,X,_] => ( x+1, y ),
                [_,X,_, o,o, _,X,_] => ( if y&2 == 0 {x-1} else {x+1}, y ), //deterministic choice
                //[_,X,_, o,o, _,X,_] => ( if self.rng.gen::<i16>()&2 == 0 {x-1} else {x+1}, y ), //random choice
                                        _ => continue,
            };
            if GameState::is_valid(x_new, y_new) {
                self.map.remove_coord_map(x, y);
                self.map.add_coord_map(x_new, y_new);
                *particle = Loc {x: x_new, y: y_new};
            } else {
                self.map.map[x as usize][y as usize] = false;
                self.indexes_to_remove.push(index); //prepare list of particles to remove
            }
        }
        //must use .rev(), otherwise you can't remove the last element
        for index in self.indexes_to_remove.iter().rev() {
            self.particles.swap_remove(*index);
        }
        self.indexes_to_remove.clear();
    }//end update

    pub fn rain(&mut self) {
        for x in 1*(GRID_WIDTH/20)..19*(GRID_WIDTH/20) {
            if !self.map.is_occupied(x, 0) && self.rng.gen::<i16>()&RAIN_SPARSENESS == 0 {
                self.particles.push(Loc {x: x, y: 0});
                self.map.add_coord_map(x, 0);
            }
        }
    }

    fn remove_indices_matching_loc(items: &mut Vec<Loc>,
                               indexes_to_remove: &mut Vec<usize>,
                               loc: Loc) {
        {
            let mut it = items.iter();

            while let Some(index) = it.position(|p| p == &loc) {
                indexes_to_remove.push(index);
            }
        }

        for index in indexes_to_remove.iter().rev() {
            items.swap_remove(*index);
        }

        indexes_to_remove.clear();
    }

    fn remove_particle_list (&mut self, x: i16, y: i16) {
        Self::remove_indices_matching_loc(&mut self.particles,
                                          &mut self.indexes_to_remove,
                                          Loc {x: x, y: y})
    }

    fn remove_obstacle_list (&mut self, x: i16, y: i16) {
        Self::remove_indices_matching_loc(&mut self.obstacles,
                                          &mut self.indexes_to_remove,
                                          Loc {x: x, y: y})
    }

    fn add_particle (&mut self, x: i16, y: i16) {
        self.map.add_coord_map(x, y);
        let loc = Loc {x: x, y: y};
        self.particles.push(loc);
    }

    fn add_obstacle (&mut self, x: i16, y: i16) {
        self.map.add_coord_map(x, y);
        let loc = Loc {x: x, y: y};
        self.obstacles.push(loc);
    }

    fn remove_coord(&mut self, x: i16, y: i16) {
        self.map.remove_coord_map(x, y);
        self.remove_obstacle_list(x, y);
        self.remove_particle_list(x, y);
    }

    pub fn remove_square (&mut self, ux: i16, uy: i16, dx: i16, dy: i16) {
        if Self::is_valid(ux + dx, uy + dy) {
            for x in ux..ux+dx {
                for y in uy..uy+dy {
                    if self.map.is_occupied(x, y) {
                        self.remove_coord(x, y);
                    }
                }
            }
        }
    }

    pub fn paint_square_obstacles (&mut self, ux: i16, uy: i16, dx: i16, dy: i16) {
        if Self::is_valid(ux + dx, uy + dy) {
            for x in ux..ux+dx {
                for y in uy..uy+dy {
                    if !self.map.is_occupied(x, y) {
                        self.map.add_coord_map(x, y);
                        self.add_obstacle(x, y);
                    }
                }
            }
        }
    }

    pub fn new() -> GameState {
        GameState {
                particles: Vec::with_capacity(10_000),
                obstacles: Vec::with_capacity(10_000),
                indexes_to_remove: Vec::with_capacity(1000),
                map: Map { map: box [[false; GRID_HEIGHT as usize]; GRID_WIDTH as usize] },
                max_x: GRID_WIDTH,
                max_y: GRID_HEIGHT,
                rng: SeedableRng::from_seed([1, 2, 3, 4])
            }
    }

}//end impl GameState
<commit_msg>Sped up remove_square code<commit_after>extern crate rand;
use rand::{Rng, SeedableRng, XorShiftRng};

pub const GRID_HEIGHT: i16 = 900;
pub const GRID_WIDTH: i16 = 1440;

pub const BLOCK_SIZE: i16 = 1;

pub const WINDOW_HEIGHT: i16 = GRID_HEIGHT * BLOCK_SIZE;
pub const WINDOW_WIDTH: i16 = GRID_WIDTH * BLOCK_SIZE;

pub const RAIN_SPARSENESS: i16 = 5;

pub const X: bool = true;
pub const o: bool = false;

#[derive(Eq, PartialEq, Clone)]
pub struct Loc {
    pub x: i16,
    pub y: i16
}

pub struct Map {pub map: Box<[[bool; GRID_HEIGHT as usize]; GRID_WIDTH as usize]>}

pub struct GameState {
    pub particles: Vec<Loc>,
    pub obstacles: Vec<Loc>,
    pub indexes_to_remove: Vec<usize>,
    pub map: Map, //2D array to check occupancy
    pub max_x: i16,
    pub max_y: i16,
    pub rng: XorShiftRng
}

impl Map {
    fn is_occupied(&self, x: i16, y: i16) -> bool {
        //Not valid is not occupied (false) to allow particles to leave the grid.
        //To prevent double bound checking with GameState::is_valid(), unwrap get()
        //and return defailt false on None
        if let Some(arr) = self.map.get(x as usize) {
            *arr.get(y as usize).unwrap_or(&false)
        } else {
            false
        }
    }

    fn get_neighbours(&mut self, x: i16, y: i16) -> [bool; 8] {
        [self.is_occupied(x-1, y-1), self.is_occupied(x, y-1), self.is_occupied(x+1, y-1),
        self.is_occupied(x-1, y), self.is_occupied(x+1, y),
        self.is_occupied(x-1, y+1), self.is_occupied(x, y+1), self.is_occupied(x-1, y+1)]
    }

    fn add_coord_map(&mut self, x: i16, y: i16) {
        self.map[x as usize][y as usize] = true;
    }

    fn remove_coord_map(&mut self, x: i16, y: i16) {
        self.map[x as usize][y as usize] = false;
    }
}

impl GameState {
    //check if point in inside the grid
    pub fn is_valid(x: i16, y: i16) -> bool {
        if x<0 || x>=GRID_WIDTH || y<0 || y>=GRID_HEIGHT {false} else {true}
    }

    pub fn update(&mut self) {
        //MAIN LOGIC
        for (index, particle) in self.particles.iter_mut().enumerate() {
            let (x, y) = (particle.x, particle.y);
            let neighbours = self.map.get_neighbours(x, y);
            let (x_new, y_new) = match neighbours {
               /*above  side below*/
                [_,_,_, _,_, _,o,_] => ( x, y+1 ),
                [_,X,_, o,X, _,X,_] => ( x-1, y ),
                [_,X,_, X,o, _,X,_] => ( x+1, y ),
                [_,X,_, o,o, _,X,_] => ( if y&2 == 0 {x-1} else {x+1}, y ), //deterministic choice
                //[_,X,_, o,o, _,X,_] => ( if self.rng.gen::<i16>()&2 == 0 {x-1} else {x+1}, y ), //random choice
                                        _ => continue,
            };
            if Self::is_valid(x_new, y_new) {
                self.map.remove_coord_map(x, y);
                self.map.add_coord_map(x_new, y_new);
                *particle = Loc {x: x_new, y: y_new};
            } else {
                self.map.map[x as usize][y as usize] = false;
                self.indexes_to_remove.push(index); //prepare list of particles to remove
            }
        }
        //must use .rev(), otherwise you can't remove the last element
        for index in self.indexes_to_remove.iter().rev() {
            self.particles.swap_remove(*index);
        }
        self.indexes_to_remove.clear();
    }//end update

    pub fn rain(&mut self) {
        for x in 1*(GRID_WIDTH/20)..19*(GRID_WIDTH/20) {
            if !self.map.is_occupied(x, 0) && self.rng.gen::<i16>()&RAIN_SPARSENESS == 0 {
                self.particles.push(Loc {x: x, y: 0});
                self.map.add_coord_map(x, 0);
            }
        }
    }

    fn remove_indices_in_rect(items: &mut Vec<Loc>,
                              indexes_to_remove: &mut Vec<usize>,
                              ul: Loc, lr: Loc) {
        for (index, p) in items.iter().enumerate() {
            if p.x >= ul.x && p.y >= ul.y && p.x < lr.x && p.y < lr.y {
                indexes_to_remove.push(index);
            }
        }

        for index in indexes_to_remove.iter().rev() {
            items.swap_remove(*index);
        }

        indexes_to_remove.clear();
    }

    fn add_particle (&mut self, x: i16, y: i16) {
        self.map.add_coord_map(x, y);
        let loc = Loc {x: x, y: y};
        self.particles.push(loc);
    }

    fn add_obstacle (&mut self, x: i16, y: i16) {
        self.map.add_coord_map(x, y);
        let loc = Loc {x: x, y: y};
        self.obstacles.push(loc);
    }

    pub fn remove_square (&mut self, ux: i16, uy: i16, dx: i16, dy: i16) {
        if Self::is_valid(ux + dx, uy + dy) {
            for x in ux..ux+dx {
                for y in uy..uy+dy {
                    self.map.remove_coord_map(x, y);
                }
            }

            let ul = Loc {x: ux, y: uy};
            let lr = Loc {x: ux + dx, y: uy + dy};
            Self::remove_indices_in_rect(&mut self.obstacles,
                                              &mut self.indexes_to_remove,
                                              ul.clone(), lr.clone());
            Self::remove_indices_in_rect(&mut self.particles,
                                              &mut self.indexes_to_remove,
                                              ul, lr);
        }
    }

    pub fn paint_square_obstacles (&mut self, ux: i16, uy: i16, dx: i16, dy: i16) {
        if Self::is_valid(ux + dx, uy + dy) {
            for x in ux..ux+dx {
                for y in uy..uy+dy {
                    if !self.map.is_occupied(x, y) {
                        self.map.add_coord_map(x, y);
                        self.add_obstacle(x, y);
                    }
                }
            }
        }
    }

    pub fn new() -> GameState {
        GameState {
                particles: Vec::with_capacity(10_000),
                obstacles: Vec::with_capacity(10_000),
                indexes_to_remove: Vec::with_capacity(1000),
                map: Map { map: box [[false; GRID_HEIGHT as usize]; GRID_WIDTH as usize] },
                max_x: GRID_WIDTH,
                max_y: GRID_HEIGHT,
                rng: SeedableRng::from_seed([1, 2, 3, 4])
            }
    }

}//end impl GameState
<|endoftext|>