<commit_before>// Hound -- A WAV encoding and decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::fs;
use std::io;
use std::io::Write;
use std::path;
use super::{Error, Result, Sample, WavSpec};

/// Extends the functionality of `io::Write` with additional methods.
///
/// The methods may be used on any type that implements `io::Write`.
pub trait WriteExt: io::Write {
    /// Writes an unsigned 8-bit integer.
    fn write_u8(&mut self, x: u8) -> io::Result<()>;

    /// Writes a signed 16-bit integer in little endian format.
    fn write_le_i16(&mut self, x: i16) -> io::Result<()>;

    /// Writes an unsigned 16-bit integer in little endian format.
    fn write_le_u16(&mut self, x: u16) -> io::Result<()>;

    /// Writes an unsigned 32-bit integer in little endian format.
    fn write_le_u32(&mut self, x: u32) -> io::Result<()>;
}

impl<W> WriteExt for W where W: io::Write {
    fn write_u8(&mut self, x: u8) -> io::Result<()> {
        let buf = [x];
        self.write_all(&buf)
    }

    fn write_le_i16(&mut self, x: i16) -> io::Result<()> {
        self.write_le_u16(x as u16)
    }

    fn write_le_u16(&mut self, x: u16) -> io::Result<()> {
        let mut buf = [0u8; 2];
        buf[0] = (x & 0xff) as u8;
        buf[1] = (x >> 8) as u8;
        self.write_all(&buf)
    }

    fn write_le_u32(&mut self, x: u32) -> io::Result<()> {
        let mut buf = [0u8; 4];
        buf[0] = ((x >> 00) & 0xff) as u8;
        buf[1] = ((x >> 08) & 0xff) as u8;
        buf[2] = ((x >> 16) & 0xff) as u8;
        buf[3] = ((x >> 24) & 0xff) as u8;
        self.write_all(&buf)
    }
}

/// Generates a bitmask with `channels` ones in the least significant bits.
fn channel_mask(channels: u16) -> u32 {
    (0 .. channels).map(|c| 1 << c).fold(0, |a, c| a | c)
}

#[test]
fn verify_channel_mask()
{
    assert_eq!(channel_mask(0), 0);
    assert_eq!(channel_mask(1), 1);
    assert_eq!(channel_mask(2), 3);
    assert_eq!(channel_mask(3), 7);
    assert_eq!(channel_mask(4), 15);
}

/// A writer that accepts samples and writes the WAVE format.
///
/// TODO: add example.
///
/// After all samples have been written, the file must be finalized. This can
/// be done by calling `finalize`. If `finalize` is not called, the file will
/// be finalized upon drop. However, finalization involves IO that may fail,
/// and without calling `finalize`, such a failure cannot be observed.
pub struct WavWriter<W> where W: io::Write + io::Seek {
    /// Specifies properties of the audio data.
    spec: WavSpec,

    /// The (container) bytes per sample. This is the bit rate / 8 rounded up.
    bytes_per_sample: u16,

    /// Whether the header has been written already.
    wrote_header: bool,

    /// The writer that will be written to.
    writer: io::BufWriter<W>,

    /// The number of bytes written to the data section.
    ///
    /// This is an `u32` because WAVE cannot accomodate more data.
    data_bytes_written: u32,

    /// Whether `finalize_internal` has been called.
    finalized: bool
}

impl<W> WavWriter<W> where W: io::Write + io::Seek {
    /// Creates a writer that writes the WAVE format to the underlying writer.
    ///
    /// The underlying writer is assumed to be at offset 0. `WavWriter` employs
    /// buffering internally to avoid too many `write` calls to the underlying
    /// writer.
    pub fn new(writer: W, spec: WavSpec) -> WavWriter<W> {
        WavWriter {
            spec: spec,
            bytes_per_sample: (spec.bits_per_sample as f32 / 8.0).ceil() as u16,
            wrote_header: false,
            writer: io::BufWriter::new(writer),
            data_bytes_written: 0,
            finalized: false
        }
    }

    /// Writes the RIFF WAVE header
    fn write_header(&mut self) -> io::Result<()> {
        // Useful links:
        // https://msdn.microsoft.com/en-us/library/ms713462.aspx
        // https://msdn.microsoft.com/en-us/library/ms713497.aspx

        let mut header = [0u8; 68];
        let spec = &self.spec;

        // Write the header in-memory first.
        {
            let mut buffer: io::Cursor<&mut [u8]> = io::Cursor::new(&mut header);
            try!(buffer.write_all("RIFF".as_bytes()));

            // Skip 4 bytes that will be filled with the file size afterwards.
            try!(buffer.write_le_u32(0));

            try!(buffer.write_all("WAVE".as_bytes()));
            try!(buffer.write_all("fmt ".as_bytes()));
            try!(buffer.write_le_u32(40)); // Size of the WAVE header chunk.

            // The following is based on the WAVEFORMATEXTENSIBLE struct as
            // documented on MSDN.

            // The field wFormatTag, value 1 means WAVE_FORMAT_PCM, but we use
            // the slightly more sophisticated WAVE_FORMAT_EXTENSIBLE.
            try!(buffer.write_le_u16(0xfffe));
            // The field nChannels.
            try!(buffer.write_le_u16(spec.channels));
            // The field nSamplesPerSec.
            try!(buffer.write_le_u32(spec.sample_rate));
            let bytes_per_sec = spec.sample_rate
                              * self.bytes_per_sample as u32
                              * spec.channels as u32;
            // The field nAvgBytesPerSec;
            try!(buffer.write_le_u32(bytes_per_sec));
            // The field nBlockAlign. Block align * sample rate = bytes per sec.
            try!(buffer.write_le_u16((bytes_per_sec / spec.sample_rate) as u16));
            // The field wBitsPerSample. This is actually the size of the
            // container, so this is a multiple of 8.
            try!(buffer.write_le_u16(self.bytes_per_sample as u16 * 8));
            // The field cbSize, the number of remaining bytes in the struct.
            try!(buffer.write_le_u16(22));
            // The field wValidBitsPerSample, the real number of bits per sample.
            try!(buffer.write_le_u16(self.spec.bits_per_sample));
            // The field dwChannelMask.
            // TODO: add the option to specify the channel mask. For now, use
            // the default assignment.
            try!(buffer.write_le_u32(channel_mask(self.spec.channels)));
            // The field SubFormat. We use KSDATAFORMAT_SUBTYPE_PCM. The
            // following GUIDS are defined:
            // - PCM:        00000001-0000-0010-8000-00aa00389b71
            // - IEEE_FLOAT: 00000003-0000-0010-8000-00aa00389b71
            // The byte order of a GUID is native for the first three sections,
            // which is assumed to be little endian, and big endian for the
            // last 8-byte section (which does contain a hyphen, for reasons
            // unknown to me).
            try!(buffer.write_all(&[0x01, 0x00, 0x00, 0x00,
                                    0x00, 0x00, 0x10, 0x00,
                                    0x80, 0x00, 0x00, 0xaa,
                                    0x00, 0x38, 0x9b, 0x71]));

            // So far the "fmt " section, now comes the "data" section. We will
            // only write the header here, actual data are the samples. The
            // number of bytes that this will take is not known at this point.
            // The 0 will be overwritten later.
            try!(buffer.write_all("data".as_bytes()));
            try!(buffer.write_le_u32(0));
        }

        // Then write the entire header at once.
        try!(self.writer.write_all(&header));

        Ok(())
    }

    /// Writes a single sample for one channel.
    ///
    /// WAVE interleaves channel data, so the channel that this writes the
    /// sample to depends on previous writes.
    pub fn write_sample<S: Sample>(&mut self, sample: S) -> Result<()> {
        if !self.wrote_header {
            try!(self.write_header());
            self.wrote_header = true;
        }

        // TODO: do we need bits per sample? Is the padding at the obvious side?
        try!(sample.write(&mut self.writer, self.spec.bits_per_sample));
        self.data_bytes_written += self.bytes_per_sample as u32;
        Ok(())
    }

    /// Performs finalization. After calling this, the writer should be destructed.
    fn finalize_internal(&mut self) -> Result<()> {
        self.finalized = true;

        // Flush remaining samples via the BufWriter.
        try!(self.writer.flush());

        // Extract the underlying writer and rewind it to the start, to update
        // the header fields of which we now know the value.
        let mut writer = self.writer.get_mut();

        // The header minus magic and 32-bit filesize is 60 bytes long.
        let file_size = self.data_bytes_written + 60;
        try!(writer.seek(io::SeekFrom::Start(4)));
        try!(writer.write_le_u32(file_size));
        try!(writer.seek(io::SeekFrom::Start(64)));
        try!(writer.write_le_u32(self.data_bytes_written));

        // Signal error if the last sample was not finished, but do so after
        // everything has been written, so that no data is lost, even though
        // the file is now ill-formed.
        if (self.data_bytes_written / self.bytes_per_sample as u32)
            % self.spec.channels as u32 != 0 {
            return Err(Error::UnfinishedSample);
        }

        Ok(())
    }

    /// Writes the parts of the WAVE format that require knowing all samples.
    ///
    /// This method must be called after all samples have been written. If it
    /// is not called, the destructor will finalize the file, but any IO errors
    /// that occur in the process cannot be observed in that manner.
    pub fn finalize(mut self) -> Result<()> {
        self.finalize_internal()
    }
}

impl<W> Drop for WavWriter<W> where W: io::Write + io::Seek {
    fn drop(&mut self) {
        // `finalize_internal` must be called only once. If that is done via
        // `finalize`, then this method is a no-op. If the user did not
        // finalize explicitly, then we should do it now. This can fail, but
        // drop should not panic, so a failure is ignored silently here.
        if !self.finalized {
            let _r = self.finalize_internal();
        }
    }
}

impl WavWriter<fs::File> {
    /// Creates a writer that writes the WAVE format to a file.
    ///
    /// This is a convenience constructor that creates the file and then
    /// constructs a `WavReader` from it. The file will be overwritten if it
    /// exists.
    pub fn create<P: AsRef<path::Path>>(filename: P, spec: WavSpec)
           -> io::Result<WavWriter<fs::File>> {
        let file = try!(fs::File::create(filename));
        Ok(WavWriter::new(file, spec))
    }
}

#[test]
fn short_write_should_signal_error() {
    let mut buffer = io::Cursor::new(Vec::new());

    let write_spec = WavSpec {
        channels: 17,
        sample_rate: 48000,
        bits_per_sample: 8
    };

    // Deliberately write one sample less than 17 * 5.
    let mut writer = WavWriter::new(&mut buffer, write_spec);
    for s in (0 .. 17 * 5 - 1) {
        writer.write_sample(s).unwrap();
    }
    let error = writer.finalize().err().unwrap();

    match error {
        Error::UnfinishedSample => { },
        _ => panic!("UnfinishedSample error should have been returned.")
    }
}
<commit_msg>improve documentation<commit_after>// Hound -- A WAV encoding and decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::fs;
use std::io;
use std::io::Write;
use std::path;
use super::{Error, Result, Sample, WavSpec};

/// Extends the functionality of `io::Write` with additional methods.
///
/// The methods may be used on any type that implements `io::Write`.
pub trait WriteExt: io::Write {
    /// Writes an unsigned 8-bit integer.
    fn write_u8(&mut self, x: u8) -> io::Result<()>;

    /// Writes a signed 16-bit integer in little endian format.
    fn write_le_i16(&mut self, x: i16) -> io::Result<()>;

    /// Writes an unsigned 16-bit integer in little endian format.
    fn write_le_u16(&mut self, x: u16) -> io::Result<()>;

    /// Writes an unsigned 32-bit integer in little endian format.
    fn write_le_u32(&mut self, x: u32) -> io::Result<()>;
}

impl<W> WriteExt for W where W: io::Write {
    fn write_u8(&mut self, x: u8) -> io::Result<()> {
        let buf = [x];
        self.write_all(&buf)
    }

    fn write_le_i16(&mut self, x: i16) -> io::Result<()> {
        self.write_le_u16(x as u16)
    }

    fn write_le_u16(&mut self, x: u16) -> io::Result<()> {
        let mut buf = [0u8; 2];
        buf[0] = (x & 0xff) as u8;
        buf[1] = (x >> 8) as u8;
        self.write_all(&buf)
    }

    fn write_le_u32(&mut self, x: u32) -> io::Result<()> {
        let mut buf = [0u8; 4];
        buf[0] = ((x >> 00) & 0xff) as u8;
        buf[1] = ((x >> 08) & 0xff) as u8;
        buf[2] = ((x >> 16) & 0xff) as u8;
        buf[3] = ((x >> 24) & 0xff) as u8;
        self.write_all(&buf)
    }
}

/// Generates a bitmask with `channels` ones in the least significant bits.
fn channel_mask(channels: u16) -> u32 {
    (0 .. channels).map(|c| 1 << c).fold(0, |a, c| a | c)
}

#[test]
fn verify_channel_mask()
{
    assert_eq!(channel_mask(0), 0);
    assert_eq!(channel_mask(1), 1);
    assert_eq!(channel_mask(2), 3);
    assert_eq!(channel_mask(3), 7);
    assert_eq!(channel_mask(4), 15);
}

/// A writer that accepts samples and writes the WAVE format.
///
/// TODO: add example.
///
/// After all samples have been written, the file must be finalized. This can
/// be done by calling `finalize`. If `finalize` is not called, the file will
/// be finalized upon drop. However, finalization involves IO that may fail,
/// and without calling `finalize`, such a failure cannot be observed.
pub struct WavWriter<W> where W: io::Write + io::Seek {
    /// Specifies properties of the audio data.
    spec: WavSpec,

    /// The (container) bytes per sample. This is the bit rate / 8 rounded up.
    bytes_per_sample: u16,

    /// Whether the header has been written already.
    wrote_header: bool,

    /// The writer that will be written to.
    writer: io::BufWriter<W>,

    /// The number of bytes written to the data section.
    ///
    /// This is an `u32` because WAVE cannot accomodate more data.
    data_bytes_written: u32,

    /// Whether `finalize_internal` has been called.
    finalized: bool
}

impl<W> WavWriter<W> where W: io::Write + io::Seek {
    /// Creates a writer that writes the WAVE format to the underlying writer.
    ///
    /// The underlying writer is assumed to be at offset 0. `WavWriter` employs
    /// buffering internally to avoid too many `write` calls to the underlying
    /// writer.
    pub fn new(writer: W, spec: WavSpec) -> WavWriter<W> {
        WavWriter {
            spec: spec,
            bytes_per_sample: (spec.bits_per_sample as f32 / 8.0).ceil() as u16,
            wrote_header: false,
            writer: io::BufWriter::new(writer),
            data_bytes_written: 0,
            finalized: false
        }
    }

    /// Writes the RIFF WAVE header
    fn write_header(&mut self) -> io::Result<()> {
        // Useful links:
        // https://msdn.microsoft.com/en-us/library/ms713462.aspx
        // https://msdn.microsoft.com/en-us/library/ms713497.aspx

        let mut header = [0u8; 68];
        let spec = &self.spec;

        // Write the header in-memory first.
        {
            let mut buffer: io::Cursor<&mut [u8]> = io::Cursor::new(&mut header);
            try!(buffer.write_all("RIFF".as_bytes()));

            // Skip 4 bytes that will be filled with the file size afterwards.
            try!(buffer.write_le_u32(0));

            try!(buffer.write_all("WAVE".as_bytes()));
            try!(buffer.write_all("fmt ".as_bytes()));
            try!(buffer.write_le_u32(40)); // Size of the WAVE header chunk.

            // The following is based on the WAVEFORMATEXTENSIBLE struct as
            // documented on MSDN.

            // The field wFormatTag, value 1 means WAVE_FORMAT_PCM, but we use
            // the slightly more sophisticated WAVE_FORMAT_EXTENSIBLE.
            try!(buffer.write_le_u16(0xfffe));
            // The field nChannels.
            try!(buffer.write_le_u16(spec.channels));
            // The field nSamplesPerSec.
            try!(buffer.write_le_u32(spec.sample_rate));
            let bytes_per_sec = spec.sample_rate
                              * self.bytes_per_sample as u32
                              * spec.channels as u32;
            // The field nAvgBytesPerSec;
            try!(buffer.write_le_u32(bytes_per_sec));
            // The field nBlockAlign. Block align * sample rate = bytes per sec.
            try!(buffer.write_le_u16((bytes_per_sec / spec.sample_rate) as u16));
            // The field wBitsPerSample. This is actually the size of the
            // container, so this is a multiple of 8.
            try!(buffer.write_le_u16(self.bytes_per_sample as u16 * 8));
            // The field cbSize, the number of remaining bytes in the struct.
            try!(buffer.write_le_u16(22));
            // The field wValidBitsPerSample, the real number of bits per sample.
            try!(buffer.write_le_u16(self.spec.bits_per_sample));
            // The field dwChannelMask.
            // TODO: add the option to specify the channel mask. For now, use
            // the default assignment.
            try!(buffer.write_le_u32(channel_mask(self.spec.channels)));
            // The field SubFormat. We use KSDATAFORMAT_SUBTYPE_PCM. The
            // following GUIDS are defined:
            // - PCM:        00000001-0000-0010-8000-00aa00389b71
            // - IEEE_FLOAT: 00000003-0000-0010-8000-00aa00389b71
            // The byte order of a GUID is native for the first three sections,
            // which is assumed to be little endian, and big endian for the
            // last 8-byte section (which does contain a hyphen, for reasons
            // unknown to me).
            try!(buffer.write_all(&[0x01, 0x00, 0x00, 0x00,
                                    0x00, 0x00, 0x10, 0x00,
                                    0x80, 0x00, 0x00, 0xaa,
                                    0x00, 0x38, 0x9b, 0x71]));

            // So far the "fmt " section, now comes the "data" section. We will
            // only write the header here, actual data are the samples. The
            // number of bytes that this will take is not known at this point.
            // The 0 will be overwritten later.
            try!(buffer.write_all("data".as_bytes()));
            try!(buffer.write_le_u32(0));
        }

        // Then write the entire header at once.
        try!(self.writer.write_all(&header));

        Ok(())
    }

    /// Writes a single sample for one channel.
    ///
    /// WAVE interleaves channel data, so the channel that this writes the
    /// sample to depends on previous writes.
    pub fn write_sample<S: Sample>(&mut self, sample: S) -> Result<()> {
        if !self.wrote_header {
            try!(self.write_header());
            self.wrote_header = true;
        }

        // TODO: do we need bits per sample? Is the padding at the obvious side?
        try!(sample.write(&mut self.writer, self.spec.bits_per_sample));
        self.data_bytes_written += self.bytes_per_sample as u32;
        Ok(())
    }

    /// Performs finalization. After calling this, the writer should be destructed.
    fn finalize_internal(&mut self) -> Result<()> {
        self.finalized = true;

        // Flush remaining samples via the BufWriter.
        try!(self.writer.flush());

        // Extract the underlying writer and rewind it to the start, to update
        // the header fields of which we now know the value.
        let mut writer = self.writer.get_mut();

        // The header minus magic and 32-bit filesize is 60 bytes long.
        let file_size = self.data_bytes_written + 60;
        try!(writer.seek(io::SeekFrom::Start(4)));
        try!(writer.write_le_u32(file_size));
        try!(writer.seek(io::SeekFrom::Start(64)));
        try!(writer.write_le_u32(self.data_bytes_written));

        // Signal error if the last sample was not finished, but do so after
        // everything has been written, so that no data is lost, even though
        // the file is now ill-formed.
        if (self.data_bytes_written / self.bytes_per_sample as u32)
            % self.spec.channels as u32 != 0 {
            return Err(Error::UnfinishedSample);
        }

        Ok(())
    }

    /// Writes the parts of the WAVE format that require knowing all samples.
    ///
    /// This method must be called after all samples have been written. If it
    /// is not called, the destructor will finalize the file, but any errors
    /// that occur in the process cannot be observed in that manner.
    pub fn finalize(mut self) -> Result<()> {
        self.finalize_internal()
    }
}

impl<W> Drop for WavWriter<W> where W: io::Write + io::Seek {
    fn drop(&mut self) {
        // `finalize_internal` must be called only once. If that is done via
        // `finalize`, then this method is a no-op. If the user did not
        // finalize explicitly, then we should do it now. This can fail, but
        // drop should not panic, so a failure is ignored silently here.
        if !self.finalized {
            let _r = self.finalize_internal();
        }
    }
}

impl WavWriter<fs::File> {
    /// Creates a writer that writes the WAVE format to a file.
    ///
    /// This is a convenience constructor that creates the file and then
    /// constructs a `WavReader` from it. The file will be overwritten if it
    /// exists.
    pub fn create<P: AsRef<path::Path>>(filename: P, spec: WavSpec)
           -> io::Result<WavWriter<fs::File>> {
        let file = try!(fs::File::create(filename));
        Ok(WavWriter::new(file, spec))
    }
}

#[test]
fn short_write_should_signal_error() {
    let mut buffer = io::Cursor::new(Vec::new());

    let write_spec = WavSpec {
        channels: 17,
        sample_rate: 48000,
        bits_per_sample: 8
    };

    // Deliberately write one sample less than 17 * 5.
    let mut writer = WavWriter::new(&mut buffer, write_spec);
    for s in (0 .. 17 * 5 - 1) {
        writer.write_sample(s).unwrap();
    }
    let error = writer.finalize().err().unwrap();

    match error {
        Error::UnfinishedSample => { },
        _ => panic!("UnfinishedSample error should have been returned.")
    }
}
<|endoftext|>


<commit_before>#[allow(clippy::unnecessary_wraps)] // FIXME: Should get redone with better errors
use crate::types::QueueOperation;
use actix_web::{
    http::{header, Method},
    App, HttpResponse, Json, Path, State,
};
use anyhow::Result;
use hybrid_clocks::{Timestamp, WallT};
use lazy_static::lazy_static;
use log::{debug, info};
use potboiler_common::{clock, db, get_raw_timestamp, types::Log};
use serde_derive::Deserialize;
use serde_json::{self, Map, Value};
use std::{env, io::Cursor, ops::Deref};
use time::Duration;
use uuid::{self, Uuid};

mod types;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
    static ref HOST: String = env::var("HOST").unwrap_or_else(|_| "localhost".to_string());
    static ref PORT: u16 = u16::from_str_radix(&env::var("PORT").unwrap_or_else(|_| "8000".to_string()), 10).unwrap();
}

#[derive(Deserialize)]
struct NewLogResponse {
    id: Uuid,
}

fn add_queue_operation(op: &QueueOperation) -> actix_web::Result<NewLogResponse> {
    let client = reqwest::Client::new();
    let mut res = client.post(SERVER_URL.deref()).json(op).send().expect("sender ok");
    assert_eq!(res.status(), reqwest::StatusCode::CREATED);
    Ok(res.json().unwrap())
}

fn create_queue(op: Json<types::QueueCreate>) -> actix_web::Result<HttpResponse> {
    let name = op.name.clone();
    match add_queue_operation(&QueueOperation::Create(op.into_inner())) {
        Ok(_) => {
            let new_url = format!("/queue/{}", &name);
            Ok(HttpResponse::Created().header(header::LOCATION, new_url).finish())
        }
        Err(val) => Err(val),
    }
}

#[derive(Deserialize)]
struct NamedQueueRoute {
    queue_name: String,
}

fn delete_queue(path: Path<NamedQueueRoute>) -> actix_web::Result<HttpResponse> {
    add_queue_operation(&QueueOperation::Delete(path.queue_name.clone()))?;
    Ok(HttpResponse::Ok().finish())
}

fn row_to_state(row: &db::Row) -> Result<types::QueueState> {
    let raw_state: String = row.get("state");
    // FIXME: format! bit is a hacky workaround for https://github.com/serde-rs/serde/issues/251
    Ok(serde_json::from_str(&format!("\"{}\"", raw_state)).unwrap())
}

fn parse_progress<F>(state: &AppState, progress: &types::QueueProgress, should_update: F) -> Result<HttpResponse>
where
    F: Fn(&types::QueueState, &Timestamp<WallT>) -> Option<(Timestamp<WallT>, String)>,
{
    let conn = state.pool.get().unwrap();
    let results = conn.query(&format!(
        "SELECT task_name, state, hlc_tstamp from {} where id='{}'",
        &progress.queue_name, &progress.id
    ))?;
    if results.is_empty() {
        Ok(HttpResponse::NotFound().body(format!("No queue item {} in {}", &progress.id, &progress.queue_name)))
    } else {
        let row = results.get(0);
        let state = row_to_state(&row)?;
        let hlc_tstamp: Vec<u8> = row.get("hlc_tstamp");
        let when = Timestamp::read_bytes(Cursor::new(hlc_tstamp))?;
        if let Some((log_when, status)) = should_update(&state, &when) {
            let raw_timestamp = get_raw_timestamp(&log_when)?;
            conn.execute(&format!(
                "UPDATE {} set hlc_tstamp='{}', worker='{}', state='{}' where id='{}'",
                &progress.queue_name,
                &raw_timestamp.sql(),
                &progress.worker_id,
                &status,
                &progress.id
            ))?;
            Ok(HttpResponse::NoContent().finish())
        } else {
            Ok(HttpResponse::Conflict().body("Out of date change"))
        }
    }
}

fn new_event(log: Json<Log>, state: State<AppState>) -> actix_web::Result<HttpResponse> {
    info!("log: {:?}", log);
    let log_when = log.when;
    state.clock.observe_timestamp(log_when);
    let op = serde_json::from_value::<QueueOperation>(log.data.clone())?;
    info!("op: {:?}", op);
    let conn = state.pool.get().unwrap();
    match op {
        QueueOperation::Create(create) => {
            info!("create: {:?}", create);
            let qc = types::QueueConfig {
                timeout_ms: create.timeout_ms,
            };
            match conn.execute(&format!(
                "INSERT INTO queues (key, config) VALUES('{}', '{}')",
                &create.name,
                &serde_json::to_value(&qc)?
            )) {
                Ok(_) => {
                    conn.execute(&format!(
                        "CREATE TABLE IF NOT EXISTS {} (id UUID PRIMARY KEY, task_name \
                         VARCHAR(2083) NOT NULL, state VARCHAR(8) NOT NULL, info JSONB NOT \
                         NULL, hlc_tstamp BYTEA NOT NULL, worker UUID NULL)",
                        &create.name
                    ))
                    .unwrap();
                }
                Err(db::Error::UniqueViolation) => {}
                Err(err) => Err(err).unwrap(),
            };
        }
        QueueOperation::Add(add) => {
            info!("add: {:?}", add);
            let raw_timestamp = get_raw_timestamp(&log.when)?;
            conn.execute(&format!(
                "INSERT INTO {} (id, task_name, state, info, hlc_tstamp) VALUES('{}', '{}', \
                 '{}', '{}', {})",
                add.queue_name,
                &log.id,
                &add.task_name,
                "pending",
                &serde_json::to_value(&add.info)?,
                &raw_timestamp.sql()
            ))
            .unwrap();
        }
        QueueOperation::Progress(progress) => {
            info!("progress: {:?}", progress);
            return Ok(parse_progress(state.deref(), &progress, |state, when| {
                if state == &types::QueueState::Pending || (state == &types::QueueState::Working && log_when > *when) {
                    Some((log_when, String::from("working")))
                } else {
                    None
                }
            })
            .unwrap());
        }
        QueueOperation::Done(done) => {
            info!("done: {:?}", done);
            return Ok(parse_progress(state.deref(), &done, |state, when| {
                if state != &types::QueueState::Done || (state == &types::QueueState::Done && log_when > *when) {
                    Some((log_when, String::from("done")))
                } else {
                    None
                }
            })
            .unwrap());
        }
        QueueOperation::Delete(queue_name) => {
            //let trans = conn.transaction()?;
            let trans = conn;
            trans.execute(&format!("DROP TABLE IF EXISTS {}", queue_name)).unwrap();
            trans
                .execute(&format!("DELETE FROM queues where key={}", &queue_name))
                .unwrap();
            //trans.commit()?;
        }
    };
    Ok(HttpResponse::NoContent().finish())
}

fn get_queue_items(state: State<AppState>, path: Path<NamedQueueRoute>) -> actix_web::Result<HttpResponse> {
    let conn = state.pool.get().unwrap();
    let config_row = conn
        .query(&format!("select config from queues where key='{}'", &path.queue_name))
        .unwrap();
    if config_row.is_empty() {
        return Ok(HttpResponse::NotFound().body(format!("No queue {}", path.queue_name)));
    }
    let config: types::QueueConfig = serde_json::from_value(config_row.get(0).get("config"))?;
    let results = conn
        .query(&format!(
            "select id, task_name, state, hlc_tstamp from {}",
            &path.queue_name
        ))
        .unwrap();
    let mut queue = Map::new();
    let now = state.clock.get_timestamp().time.as_timespec();
    let max_diff = Duration::milliseconds(config.timeout_ms);
    for row in &results {
        let id: Uuid = row.get("id");
        let mut state = row_to_state(&row).unwrap();
        if state == types::QueueState::Done {
            continue;
        }
        if state == types::QueueState::Working {
            let hlc_tstamp: Vec<u8> = row.get("hlc_tstamp");
            let when = Timestamp::read_bytes(Cursor::new(hlc_tstamp))?;
            let diff = now - when.time.as_timespec();
            if diff > max_diff {
                debug!("{} is out of date, so marking as pending", id);
                state = types::QueueState::Pending;
            }
        }
        let item = types::QueueListItem {
            task_name: row.get("task_name"),
            state,
        };
        queue.insert(id.to_string(), serde_json::to_value(&item)?);
    }
    let value = Value::Object(queue);
    Ok(HttpResponse::Ok().json(value))
}

#[derive(Deserialize)]
struct QueueItemRoute {
    queue_name: String,
    id: String,
}

fn get_queue_item(path: Path<QueueItemRoute>, state: State<AppState>) -> actix_web::Result<HttpResponse> {
    let conn = state.pool.get().unwrap();
    let results = conn
        .query(&format!(
            "select task_name, state, info, worker from {} where id='{}'",
            &path.queue_name, &path.id
        ))
        .unwrap();
    if results.is_empty() {
        Ok(HttpResponse::NotFound().body(format!("No queue item {} in {}", path.id, path.queue_name)))
    } else {
        let row = results.get(0);
        let item = types::QueueItem {
            task_name: row.get("task_name"),
            state: row_to_state(&row).unwrap(),
            info: row.get("info"),
            worker: row.get("worker"),
        };
        Ok(HttpResponse::Ok().json(item))
    }
}

fn add_queue_item(json: Json<Value>, path: Path<NamedQueueRoute>) -> actix_web::Result<HttpResponse> {
    let mut json_mut = json.into_inner();
    let map = json_mut.as_object_mut().unwrap();
    map.insert("queue_name".to_string(), serde_json::to_value(&path.queue_name)?);
    let op = serde_json::from_value::<types::QueueAdd>(json_mut)?;
    match add_queue_operation(&QueueOperation::Add(op)) {
        Ok(val) => {
            let new_url = format!("http://{}:8000/queue/{}/{}", HOST.deref(), &path.queue_name, &val.id);
            Ok(HttpResponse::Created().header(header::LOCATION, new_url).finish())
        }
        Err(val) => Err(val),
    }
}

fn build_queue_progress(json: Json<Value>, path: &Path<QueueItemRoute>) -> Result<types::QueueProgress> {
    let mut json_mut = json.into_inner();
    let map = json_mut.as_object_mut().unwrap();
    map.insert("queue_name".to_string(), serde_json::to_value(&path.queue_name)?);
    map.insert("id".to_string(), serde_json::to_value(&path.id)?);
    Ok(serde_json::from_value::<types::QueueProgress>(json_mut)?)
}

fn progress_queue_item(
    state: State<AppState>,
    json: Json<Value>,
    path: Path<QueueItemRoute>,
) -> actix_web::Result<HttpResponse> {
    let op = build_queue_progress(json, &path).unwrap();
    match add_queue_operation(&QueueOperation::Progress(op)) {
        Ok(_) => get_queue_item(path, state),
        Err(val) => Err(val),
    }
}

fn finish_queue_item(json: Json<Value>, path: Path<QueueItemRoute>) -> actix_web::Result<HttpResponse> {
    let op = build_queue_progress(json, &path).unwrap();
    match add_queue_operation(&QueueOperation::Done(op)) {
        Ok(_) => Ok(HttpResponse::Ok().finish()),
        Err(val) => Err(val),
    }
}

fn make_queue_table(conn: &db::Connection) {
    conn.execute("CREATE TABLE IF NOT EXISTS queues (key VARCHAR(1024) PRIMARY KEY, config JSONB NOT NULL)")
        .unwrap();
}

#[derive(Debug, Clone)]
pub struct AppState {
    clock: clock::SyncClock,
    pool: db::Pool,
}

impl AppState {
    pub fn new(pool: db::Pool) -> Result<AppState> {
        let clock = clock::SyncClock::new();
        let conn = pool.get().unwrap();
        make_queue_table(&conn);
        Ok(AppState { clock, pool })
    }
}

pub fn app_router(state: AppState) -> Result<App<AppState>> {
    Ok(App::with_state(state)
        .resource("/create", |r| r.method(Method::POST).with(create_queue))
        .resource("/event", |r| r.method(Method::POST).with(new_event))
        .resource("/queue/{queue_name}", |r| {
            r.method(Method::GET).with(get_queue_items);
            r.method(Method::POST).with(add_queue_item);
            r.method(Method::DELETE).with(delete_queue);
        })
        .resource("/queue/{queue_name}/{id}", |r| {
            r.method(Method::GET).with(get_queue_item);
            r.method(Method::PUT).with(progress_queue_item);
            r.method(Method::DELETE).with(finish_queue_item);
        }))
}

pub fn register() {
    let client = reqwest::Client::new();
    let mut map = serde_json::Map::new();
    map.insert(
        "url".to_string(),
        serde_json::Value::String(format!("http://{}:{}/event", HOST.deref(), PORT.deref())),
    );
    let res = client
        .post(&format!("{}/register", SERVER_URL.deref()))
        .json(&map)
        .send()
        .expect("Register ok");
    assert_eq!(res.status(), reqwest::StatusCode::CREATED);
}
<commit_msg>Fix lint on pigtail<commit_after>#![allow(clippy::unnecessary_wraps)] // FIXME: Should get redone with better errors
use crate::types::QueueOperation;
use actix_web::{
    http::{header, Method},
    App, HttpResponse, Json, Path, State,
};
use anyhow::Result;
use hybrid_clocks::{Timestamp, WallT};
use lazy_static::lazy_static;
use log::{debug, info};
use potboiler_common::{clock, db, get_raw_timestamp, types::Log};
use serde_derive::Deserialize;
use serde_json::{self, Map, Value};
use std::{env, io::Cursor, ops::Deref};
use time::Duration;
use uuid::{self, Uuid};

mod types;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
    static ref HOST: String = env::var("HOST").unwrap_or_else(|_| "localhost".to_string());
    static ref PORT: u16 = u16::from_str_radix(&env::var("PORT").unwrap_or_else(|_| "8000".to_string()), 10).unwrap();
}

#[derive(Deserialize)]
struct NewLogResponse {
    id: Uuid,
}

fn add_queue_operation(op: &QueueOperation) -> actix_web::Result<NewLogResponse> {
    let client = reqwest::Client::new();
    let mut res = client.post(SERVER_URL.deref()).json(op).send().expect("sender ok");
    assert_eq!(res.status(), reqwest::StatusCode::CREATED);
    Ok(res.json().unwrap())
}

fn create_queue(op: Json<types::QueueCreate>) -> actix_web::Result<HttpResponse> {
    let name = op.name.clone();
    match add_queue_operation(&QueueOperation::Create(op.into_inner())) {
        Ok(_) => {
            let new_url = format!("/queue/{}", &name);
            Ok(HttpResponse::Created().header(header::LOCATION, new_url).finish())
        }
        Err(val) => Err(val),
    }
}

#[derive(Deserialize)]
struct NamedQueueRoute {
    queue_name: String,
}

fn delete_queue(path: Path<NamedQueueRoute>) -> actix_web::Result<HttpResponse> {
    add_queue_operation(&QueueOperation::Delete(path.queue_name.clone()))?;
    Ok(HttpResponse::Ok().finish())
}

fn row_to_state(row: &db::Row) -> Result<types::QueueState> {
    let raw_state: String = row.get("state");
    // FIXME: format! bit is a hacky workaround for https://github.com/serde-rs/serde/issues/251
    Ok(serde_json::from_str(&format!("\"{}\"", raw_state)).unwrap())
}

fn parse_progress<F>(state: &AppState, progress: &types::QueueProgress, should_update: F) -> Result<HttpResponse>
where
    F: Fn(&types::QueueState, &Timestamp<WallT>) -> Option<(Timestamp<WallT>, String)>,
{
    let conn = state.pool.get().unwrap();
    let results = conn.query(&format!(
        "SELECT task_name, state, hlc_tstamp from {} where id='{}'",
        &progress.queue_name, &progress.id
    ))?;
    if results.is_empty() {
        Ok(HttpResponse::NotFound().body(format!("No queue item {} in {}", &progress.id, &progress.queue_name)))
    } else {
        let row = results.get(0);
        let state = row_to_state(&row)?;
        let hlc_tstamp: Vec<u8> = row.get("hlc_tstamp");
        let when = Timestamp::read_bytes(Cursor::new(hlc_tstamp))?;
        if let Some((log_when, status)) = should_update(&state, &when) {
            let raw_timestamp = get_raw_timestamp(&log_when)?;
            conn.execute(&format!(
                "UPDATE {} set hlc_tstamp='{}', worker='{}', state='{}' where id='{}'",
                &progress.queue_name,
                &raw_timestamp.sql(),
                &progress.worker_id,
                &status,
                &progress.id
            ))?;
            Ok(HttpResponse::NoContent().finish())
        } else {
            Ok(HttpResponse::Conflict().body("Out of date change"))
        }
    }
}

fn new_event(log: Json<Log>, state: State<AppState>) -> actix_web::Result<HttpResponse> {
    info!("log: {:?}", log);
    let log_when = log.when;
    state.clock.observe_timestamp(log_when);
    let op = serde_json::from_value::<QueueOperation>(log.data.clone())?;
    info!("op: {:?}", op);
    let conn = state.pool.get().unwrap();
    match op {
        QueueOperation::Create(create) => {
            info!("create: {:?}", create);
            let qc = types::QueueConfig {
                timeout_ms: create.timeout_ms,
            };
            match conn.execute(&format!(
                "INSERT INTO queues (key, config) VALUES('{}', '{}')",
                &create.name,
                &serde_json::to_value(&qc)?
            )) {
                Ok(_) => {
                    conn.execute(&format!(
                        "CREATE TABLE IF NOT EXISTS {} (id UUID PRIMARY KEY, task_name \
                         VARCHAR(2083) NOT NULL, state VARCHAR(8) NOT NULL, info JSONB NOT \
                         NULL, hlc_tstamp BYTEA NOT NULL, worker UUID NULL)",
                        &create.name
                    ))
                    .unwrap();
                }
                Err(db::Error::UniqueViolation) => {}
                Err(err) => Err(err).unwrap(),
            };
        }
        QueueOperation::Add(add) => {
            info!("add: {:?}", add);
            let raw_timestamp = get_raw_timestamp(&log.when)?;
            conn.execute(&format!(
                "INSERT INTO {} (id, task_name, state, info, hlc_tstamp) VALUES('{}', '{}', \
                 '{}', '{}', {})",
                add.queue_name,
                &log.id,
                &add.task_name,
                "pending",
                &serde_json::to_value(&add.info)?,
                &raw_timestamp.sql()
            ))
            .unwrap();
        }
        QueueOperation::Progress(progress) => {
            info!("progress: {:?}", progress);
            return Ok(parse_progress(state.deref(), &progress, |state, when| {
                if state == &types::QueueState::Pending || (state == &types::QueueState::Working && log_when > *when) {
                    Some((log_when, String::from("working")))
                } else {
                    None
                }
            })
            .unwrap());
        }
        QueueOperation::Done(done) => {
            info!("done: {:?}", done);
            return Ok(parse_progress(state.deref(), &done, |state, when| {
                if state != &types::QueueState::Done || (state == &types::QueueState::Done && log_when > *when) {
                    Some((log_when, String::from("done")))
                } else {
                    None
                }
            })
            .unwrap());
        }
        QueueOperation::Delete(queue_name) => {
            //let trans = conn.transaction()?;
            let trans = conn;
            trans.execute(&format!("DROP TABLE IF EXISTS {}", queue_name)).unwrap();
            trans
                .execute(&format!("DELETE FROM queues where key={}", &queue_name))
                .unwrap();
            //trans.commit()?;
        }
    };
    Ok(HttpResponse::NoContent().finish())
}

fn get_queue_items(state: State<AppState>, path: Path<NamedQueueRoute>) -> actix_web::Result<HttpResponse> {
    let conn = state.pool.get().unwrap();
    let config_row = conn
        .query(&format!("select config from queues where key='{}'", &path.queue_name))
        .unwrap();
    if config_row.is_empty() {
        return Ok(HttpResponse::NotFound().body(format!("No queue {}", path.queue_name)));
    }
    let config: types::QueueConfig = serde_json::from_value(config_row.get(0).get("config"))?;
    let results = conn
        .query(&format!(
            "select id, task_name, state, hlc_tstamp from {}",
            &path.queue_name
        ))
        .unwrap();
    let mut queue = Map::new();
    let now = state.clock.get_timestamp().time.as_timespec();
    let max_diff = Duration::milliseconds(config.timeout_ms);
    for row in &results {
        let id: Uuid = row.get("id");
        let mut state = row_to_state(&row).unwrap();
        if state == types::QueueState::Done {
            continue;
        }
        if state == types::QueueState::Working {
            let hlc_tstamp: Vec<u8> = row.get("hlc_tstamp");
            let when = Timestamp::read_bytes(Cursor::new(hlc_tstamp))?;
            let diff = now - when.time.as_timespec();
            if diff > max_diff {
                debug!("{} is out of date, so marking as pending", id);
                state = types::QueueState::Pending;
            }
        }
        let item = types::QueueListItem {
            task_name: row.get("task_name"),
            state,
        };
        queue.insert(id.to_string(), serde_json::to_value(&item)?);
    }
    let value = Value::Object(queue);
    Ok(HttpResponse::Ok().json(value))
}

#[derive(Deserialize)]
struct QueueItemRoute {
    queue_name: String,
    id: String,
}

fn get_queue_item(path: Path<QueueItemRoute>, state: State<AppState>) -> actix_web::Result<HttpResponse> {
    let conn = state.pool.get().unwrap();
    let results = conn
        .query(&format!(
            "select task_name, state, info, worker from {} where id='{}'",
            &path.queue_name, &path.id
        ))
        .unwrap();
    if results.is_empty() {
        Ok(HttpResponse::NotFound().body(format!("No queue item {} in {}", path.id, path.queue_name)))
    } else {
        let row = results.get(0);
        let item = types::QueueItem {
            task_name: row.get("task_name"),
            state: row_to_state(&row).unwrap(),
            info: row.get("info"),
            worker: row.get("worker"),
        };
        Ok(HttpResponse::Ok().json(item))
    }
}

fn add_queue_item(json: Json<Value>, path: Path<NamedQueueRoute>) -> actix_web::Result<HttpResponse> {
    let mut json_mut = json.into_inner();
    let map = json_mut.as_object_mut().unwrap();
    map.insert("queue_name".to_string(), serde_json::to_value(&path.queue_name)?);
    let op = serde_json::from_value::<types::QueueAdd>(json_mut)?;
    match add_queue_operation(&QueueOperation::Add(op)) {
        Ok(val) => {
            let new_url = format!("http://{}:8000/queue/{}/{}", HOST.deref(), &path.queue_name, &val.id);
            Ok(HttpResponse::Created().header(header::LOCATION, new_url).finish())
        }
        Err(val) => Err(val),
    }
}

fn build_queue_progress(json: Json<Value>, path: &Path<QueueItemRoute>) -> Result<types::QueueProgress> {
    let mut json_mut = json.into_inner();
    let map = json_mut.as_object_mut().unwrap();
    map.insert("queue_name".to_string(), serde_json::to_value(&path.queue_name)?);
    map.insert("id".to_string(), serde_json::to_value(&path.id)?);
    Ok(serde_json::from_value::<types::QueueProgress>(json_mut)?)
}

fn progress_queue_item(
    state: State<AppState>,
    json: Json<Value>,
    path: Path<QueueItemRoute>,
) -> actix_web::Result<HttpResponse> {
    let op = build_queue_progress(json, &path).unwrap();
    match add_queue_operation(&QueueOperation::Progress(op)) {
        Ok(_) => get_queue_item(path, state),
        Err(val) => Err(val),
    }
}

fn finish_queue_item(json: Json<Value>, path: Path<QueueItemRoute>) -> actix_web::Result<HttpResponse> {
    let op = build_queue_progress(json, &path).unwrap();
    match add_queue_operation(&QueueOperation::Done(op)) {
        Ok(_) => Ok(HttpResponse::Ok().finish()),
        Err(val) => Err(val),
    }
}

fn make_queue_table(conn: &db::Connection) {
    conn.execute("CREATE TABLE IF NOT EXISTS queues (key VARCHAR(1024) PRIMARY KEY, config JSONB NOT NULL)")
        .unwrap();
}

#[derive(Debug, Clone)]
pub struct AppState {
    clock: clock::SyncClock,
    pool: db::Pool,
}

impl AppState {
    pub fn new(pool: db::Pool) -> Result<AppState> {
        let clock = clock::SyncClock::new();
        let conn = pool.get().unwrap();
        make_queue_table(&conn);
        Ok(AppState { clock, pool })
    }
}

pub fn app_router(state: AppState) -> Result<App<AppState>> {
    Ok(App::with_state(state)
        .resource("/create", |r| r.method(Method::POST).with(create_queue))
        .resource("/event", |r| r.method(Method::POST).with(new_event))
        .resource("/queue/{queue_name}", |r| {
            r.method(Method::GET).with(get_queue_items);
            r.method(Method::POST).with(add_queue_item);
            r.method(Method::DELETE).with(delete_queue);
        })
        .resource("/queue/{queue_name}/{id}", |r| {
            r.method(Method::GET).with(get_queue_item);
            r.method(Method::PUT).with(progress_queue_item);
            r.method(Method::DELETE).with(finish_queue_item);
        }))
}

pub fn register() {
    let client = reqwest::Client::new();
    let mut map = serde_json::Map::new();
    map.insert(
        "url".to_string(),
        serde_json::Value::String(format!("http://{}:{}/event", HOST.deref(), PORT.deref())),
    );
    let res = client
        .post(&format!("{}/register", SERVER_URL.deref()))
        .json(&map)
        .send()
        .expect("Register ok");
    assert_eq!(res.status(), reqwest::StatusCode::CREATED);
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

/* automatically generated by rust-bindgen */

use core::libc::*;

type enum_unnamed1 = c_uint;
static STBI_default: u32 = 0_u32;
static STBI_grey: u32 = 1_u32;
static STBI_grey_alpha: u32 = 2_u32;
static STBI_rgb: u32 = 3_u32;
static STBI_rgb_alpha: u32 = 4_u32;

#[link_args="-L. -lstb-image"]
#[nolink]
mod m {
  extern {
  }
}

#[nolink]
pub mod bindgen {
  use core::libc::*;

  type stbi_uc = c_uchar;
  struct stbi_io_callbacks {
    read: *u8,
    skip: *u8,
    eof: *u8,
  }

  pub extern {

fn stbi_load_from_memory(buffer: *stbi_uc, len: c_int, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_load(filename: *c_char, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_load_from_file(f: *FILE, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_load_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_loadf_from_memory(buffer: *stbi_uc, len: c_int, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_loadf(filename: *c_char, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_loadf_from_file(f: *FILE, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_loadf_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_hdr_to_ldr_gamma(gamma: c_float);

fn stbi_hdr_to_ldr_scale(scale: c_float);

fn stbi_ldr_to_hdr_gamma(gamma: c_float);

fn stbi_ldr_to_hdr_scale(scale: c_float);

fn stbi_is_hdr_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void) -> c_int;

fn stbi_is_hdr_from_memory(buffer: *stbi_uc, len: c_int) -> c_int;

fn stbi_is_hdr(filename: *c_char) -> c_int;

fn stbi_is_hdr_from_file(f: *FILE) -> c_int;

fn stbi_failure_reason() -> *c_char;

fn stbi_image_free(retval_from_stbi_load: *c_void);

fn stbi_info_from_memory(buffer: *stbi_uc, len: c_int, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_info_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_info(filename: *c_char, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_info_from_file(f: *FILE, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_set_unpremultiply_on_load(flag_true_if_should_unpremultiply: c_int);

fn stbi_convert_iphone_png_to_rgb(flag_true_if_should_convert: c_int);

fn stbi_zlib_decode_malloc_guesssize(buffer: *c_char, len: c_int, initial_size: c_int, outlen: *c_int) -> *c_char;

fn stbi_zlib_decode_malloc(buffer: *c_char, len: c_int, outlen: *c_int) -> *c_char;

fn stbi_zlib_decode_buffer(obuffer: *c_char, olen: c_int, ibuffer: *c_char, ilen: c_int) -> c_int;

fn stbi_zlib_decode_noheader_malloc(buffer: *c_char, len: c_int, outlen: *c_int) -> *c_char;

fn stbi_zlib_decode_noheader_buffer(obuffer: *c_char, olen: c_int, ibuffer: *c_char, ilen: c_int) -> c_int;

  }
}
<commit_msg>Fixed linker error with stbi symbols<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

/* automatically generated by rust-bindgen */

use core::libc::*;

type enum_unnamed1 = c_uint;
static STBI_default: u32 = 0_u32;
static STBI_grey: u32 = 1_u32;
static STBI_grey_alpha: u32 = 2_u32;
static STBI_rgb: u32 = 3_u32;
static STBI_rgb_alpha: u32 = 4_u32;

#[link_args="-L. -lstb-image"]
#[nolink]
extern {
}

pub mod bindgen {
  use core::libc::*;

  type stbi_uc = c_uchar;
  struct stbi_io_callbacks {
    read: *u8,
    skip: *u8,
    eof: *u8,
  }

  pub extern {

fn stbi_load_from_memory(buffer: *stbi_uc, len: c_int, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_load(filename: *c_char, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_load_from_file(f: *FILE, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_load_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *stbi_uc;

fn stbi_loadf_from_memory(buffer: *stbi_uc, len: c_int, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_loadf(filename: *c_char, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_loadf_from_file(f: *FILE, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_loadf_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void, x: *mut c_int, y: *mut c_int, comp: *mut c_int, req_comp: c_int) -> *c_float;

fn stbi_hdr_to_ldr_gamma(gamma: c_float);

fn stbi_hdr_to_ldr_scale(scale: c_float);

fn stbi_ldr_to_hdr_gamma(gamma: c_float);

fn stbi_ldr_to_hdr_scale(scale: c_float);

fn stbi_is_hdr_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void) -> c_int;

fn stbi_is_hdr_from_memory(buffer: *stbi_uc, len: c_int) -> c_int;

fn stbi_is_hdr(filename: *c_char) -> c_int;

fn stbi_is_hdr_from_file(f: *FILE) -> c_int;

fn stbi_failure_reason() -> *c_char;

fn stbi_image_free(retval_from_stbi_load: *c_void);

fn stbi_info_from_memory(buffer: *stbi_uc, len: c_int, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_info_from_callbacks(clbk: *stbi_io_callbacks, user: *c_void, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_info(filename: *c_char, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_info_from_file(f: *FILE, x: *c_int, y: *c_int, comp: *c_int) -> c_int;

fn stbi_set_unpremultiply_on_load(flag_true_if_should_unpremultiply: c_int);

fn stbi_convert_iphone_png_to_rgb(flag_true_if_should_convert: c_int);

fn stbi_zlib_decode_malloc_guesssize(buffer: *c_char, len: c_int, initial_size: c_int, outlen: *c_int) -> *c_char;

fn stbi_zlib_decode_malloc(buffer: *c_char, len: c_int, outlen: *c_int) -> *c_char;

fn stbi_zlib_decode_buffer(obuffer: *c_char, olen: c_int, ibuffer: *c_char, ilen: c_int) -> c_int;

fn stbi_zlib_decode_noheader_malloc(buffer: *c_char, len: c_int, outlen: *c_int) -> *c_char;

fn stbi_zlib_decode_noheader_buffer(obuffer: *c_char, olen: c_int, ibuffer: *c_char, ilen: c_int) -> c_int;

  }
}
<|endoftext|>


<commit_before>pub mod symbol_table;
use std::collections::HashMap;

pub trait StaticSymbolTable<'a>
{
	/// Return all symbols in this table with the specified name (in any namespace)
	fn lookup_by_name(&self, name: &str) -> Vec<&Symbol<'a>>;

	/// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol<'a>>;

	/// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
	/// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol<'a>>;

	/// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol<'a>>;

	/// Lookup a structure (class, enum) by its name and namespace.
	/// Duplicate classes in the same namespace are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol<'a>)>;

	/// Adds a symbol to this table and allocates it's location
	/// Returns true if the symbol could be added; false otherwise
	/// Duplicate symbols are not allowed
	/// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass<'a>, namespace: &'a str, name: &'a str, is_static: bool, in_method: bool, is_parameter: bool, local_variable_count: u16, static_variable_count: u16, parameter_offset: u16) -> bool;
}

pub enum SymbolLocation<'a>
{
	/// Indicates that a register has been reserved for a specific use (e.g. by a variable)
	/// tuple: (name)
	Register(&'a str),

	/// Indicates a location in memory (e.g. for static variables)
	/// tuple: (address)
	Memory(MemoryAddress),

	/// Indicates an offset location from a structured entity (e.g. a member variable of a class)
	/// tuple: offset
	InstancedMemory(u16),

	MethodArgument(u16),

	/// Indicates that the symbol should not be accessed, as it represents a structured entity
	Structured
}

pub enum SymbolClass<'a>
{
	/// (variable_type)
	Variable(&'a str),

	/// Function signature (return_type, argument_types) //TODO: support exceptions
	Function(&'a str, &'a Vec<&'a str>, &'a str, usize),

	/// Includes class, enum, and interface
	/// (subtype)
	Structure(&'a str),
}

pub struct Symbol<'a>
{
	/// Namespace of this symbol, without the final "." or the name of this symbol
	pub namespace: &'a str,

	pub is_static: bool,

	/// Identifier for the symbol (e.g. name of variable, function, class, etc. without it's namespace)
	pub name: &'a str,

	/// What this symbol represents (class, enum, variable, function, etc)
	pub symbol_class: SymbolClass<'a>,

	/// Memory location of this symbol. Methods will always map to a SymbolLocation::Memory label with a 0 offset
	pub location: SymbolLocation<'a>,
}

pub struct MemoryAddress
{
	/// Label marking the base address where the symbol is stored
	pub label_name: String,

	/// Offset from the base address to access the symbol. Methods will always have an offset of 0
	pub offset: u16,
}
<commit_msg>Update documentation<commit_after>pub mod symbol_table;
use std::collections::HashMap;

pub trait StaticSymbolTable<'a>
{
	/// Return all symbols in this table with the specified name (in any namespace)
	fn lookup_by_name(&self, name: &str) -> Vec<&Symbol<'a>>;

	/// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol<'a>>;

	/// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
	/// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol<'a>>;

	/// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol<'a>>;

	/// Lookup a structure (class, enum) by its name and namespace.
	/// Duplicate classes in the same namespace are not allowed, so the result will be unique
	/// If no result is found in the direct namespace, the parent namespaces will be searched
	/// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol<'a>)>;

	/// Adds a symbol to this table and allocates it's location
	/// Returns true if the symbol could be added; false otherwise
	/// Duplicate symbols are not allowed
	/// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass<'a>, namespace: &'a str, name: &'a str, is_static: bool, in_method: bool, is_parameter: bool, local_variable_count: u16, static_variable_count: u16, parameter_offset: u16) -> bool;
}

pub enum SymbolLocation<'a>
{
	/// Indicates that a register has been reserved for a specific use (e.g. by a variable)
	/// tuple: (name)
	Register(&'a str),

	/// Indicates a location in memory (e.g. for static variables)
	/// tuple: (address)
	Memory(MemoryAddress),

	/// Indicates an offset location from a structured entity (e.g. a member variable of a class)
	/// tuple: offset
	InstancedMemory(u16),

	MethodArgument(u16),

	/// Indicates that the symbol should not be accessed, as it represents a structured entity
	Structured
}

pub enum SymbolClass<'a>
{
	/// (variable_type)
	Variable(&'a str),

	/// Function signature (return_type, argument_types, static_memory_label, static_memory_size (in words)) //TODO: support exceptions
	Function(&'a str, &'a Vec<&'a str>, &'a str, usize),

	/// Includes class, enum, and interface
	/// (subtype)
	Structure(&'a str),
}

pub struct Symbol<'a>
{
	/// Namespace of this symbol, without the final "." or the name of this symbol
	pub namespace: &'a str,

	pub is_static: bool,

	/// Identifier for the symbol (e.g. name of variable, function, class, etc. without it's namespace)
	pub name: &'a str,

	/// What this symbol represents (class, enum, variable, function, etc)
	pub symbol_class: SymbolClass<'a>,

	/// Memory location of this symbol. Methods will always map to a SymbolLocation::Memory label with a 0 offset
	pub location: SymbolLocation<'a>,
}

pub struct MemoryAddress
{
	/// Label marking the base address where the symbol is stored
	pub label_name: String,

	/// Offset from the base address to access the symbol. Methods will always have an offset of 0
	pub offset: u16,
}
<|endoftext|>


<commit_before>use std::time::Duration;
use std::ops::AddAssign;
use ggez::{Context, GameResult};

mod well;
mod tetromino;
mod shapes;
mod bag;
mod util;
mod ui_element;

use self::well::Well;
use self::tetromino::Piece;
use self::bag::PieceBag;
use self::util::DurationExt;
use self::ui_element::{UIBlockView, UITextView};
use states::{Assets, Transition};
use states::game_over_state::GameOverState;
use event::*;

const BLOCK_SIZE: f32 = 30.0;
const FALL_SPEED: f64 = 0.5;
const INPUT_DELAY_TIME: f64 = 0.06;

#[derive(Copy, Clone, Debug)]
pub struct Position {
    x: i32,
    y: i32,
}

impl Position {
    fn new(x: i32, y: i32) -> Self {
        Self { x: x, y: y }
    }
}

impl AddAssign for Position {
    fn add_assign(&mut self, other: Position) {
        *self = Position {
            x: self.x + other.x,
            y: self.y + other.y,
        };
    }
}

#[derive(Debug, Copy, Clone)]
pub struct InputStateField {
    is_active: bool,
    delay_timer: f64,
}

impl Default for InputStateField {
    fn default() -> Self {
        InputStateField {
            is_active: false,
            delay_timer: INPUT_DELAY_TIME,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct InputState {
    left: InputStateField,
    right: InputStateField,
    soft_drop: InputStateField,
    hard_drop: InputStateField,
    rotate_clockwise: InputStateField,
    rotate_counterclockwise: InputStateField,
    drop: InputStateField,
}

impl Default for InputState {
    fn default() -> Self {

        InputState {
            left: InputStateField::default(),
            right: InputStateField::default(),
            soft_drop: InputStateField::default(),
            hard_drop: InputStateField::default(),
            rotate_clockwise: InputStateField::default(),
            rotate_counterclockwise: InputStateField::default(),
            drop: InputStateField::default(),
        }
    }
}

pub struct PlayState {
    input: InputState,
    prev_input: InputState,

    well: Well,
    bag: PieceBag,
    current_piece: Piece,

    fall_timer: f64,
    score: u32,
    cleared_lines: u32,
    level: u32,
    game_over: bool,

    // ui elements
    ui_lines: UITextView,
    ui_score: UITextView,
    ui_next: UIBlockView,
}

impl PlayState {
    pub fn new(ctx: &mut Context, assets: &Assets) -> GameResult<PlayState> {
        let mut bag = PieceBag::new();
        let first_piece = bag.take_piece();
        let first_type = first_piece.get_type();

        Ok(PlayState {
            input: InputState::default(),
            prev_input: InputState::default(),

            well: Well::new(),
            bag: bag,
            current_piece: first_piece,

            fall_timer: 0.0,
            score: 0,
            cleared_lines: 0,
            level: 0,
            game_over: false,

            ui_lines: UITextView::new(ctx, assets, Position::new(775, 300), "Lines", "0"),
            ui_score: UITextView::new(ctx, assets, Position::new(775, 450), "Score", "0"),
            ui_next: UIBlockView::new(ctx, assets, Position::new(775, 100), "Next", first_type),
        })
    }

    fn handle_user_input(&mut self, dt: Duration) -> GameResult<()> {
        if self.input.left.is_active {
            self.input.left.delay_timer += dt.as_subsec_millis();
            if self.input.left.delay_timer >= INPUT_DELAY_TIME {
                self.move_piece(Position::new(-1, 0));
                self.input.left.delay_timer = 0.0;
            }
        } else if self.input.right.is_active {
            self.input.right.delay_timer += dt.as_subsec_millis();
            if self.input.right.delay_timer >= INPUT_DELAY_TIME {
                self.move_piece(Position::new(1, 0));
                self.input.right.delay_timer = 0.0;
            }
        } else if self.input.soft_drop.is_active {
            self.input.soft_drop.delay_timer += dt.as_subsec_millis();
            if self.input.soft_drop.delay_timer >= INPUT_DELAY_TIME {
                self.move_piece(Position::new(0, 1));
                self.input.soft_drop.delay_timer = 0.0;
            }
        } else if self.input.rotate_clockwise.is_active {
            if self.input.rotate_clockwise.is_active != self.prev_input.rotate_clockwise.is_active {
                self.rotate_piece(1);
            }
        } else if self.input.rotate_counterclockwise.is_active {
            if self.input.rotate_counterclockwise.is_active !=
               self.prev_input.rotate_counterclockwise.is_active {
                self.rotate_piece(-1);
            }
        } else if self.input.hard_drop.is_active {
            if self.input.hard_drop.is_active != self.prev_input.hard_drop.is_active {
                self.current_piece.top_left = self.current_piece.get_shadow_position();
                self.well.land(&self.current_piece);
                self.current_piece = self.bag.take_piece();
            }
        }

        Ok(())
    }

    fn move_piece(&mut self, potential_new_position: Position) {
        self.current_piece.potential_top_left += potential_new_position;
        let current_shape = self.current_piece.get_shape();
        let collision_found = self.well
            .check_for_collisions(&current_shape, &self.current_piece.potential_top_left);

        if collision_found {
            self.current_piece.potential_top_left = self.current_piece.top_left;
        }

        self.current_piece.top_left = self.current_piece.potential_top_left; // advance tetromino
    }

    fn rotate_piece(&mut self, direction: i32) {
        let next_shape = self.current_piece.get_next_shape(direction);
        let collision_found = self.well
            .check_for_collisions(&next_shape, &self.current_piece.top_left);

        if !collision_found {
            self.current_piece.change_shape(direction);
        } else {
            // wall kick attempt!
            // need to do 2 checks:
            // move one piece to the right & perform all above checks
            let mut potential_position = self.current_piece.top_left; // creates a copy of 'Position' struct
            potential_position.x += 1;
            let collision_found = self.well
                .check_for_collisions(&next_shape, &potential_position);

            if !collision_found {
                self.current_piece.top_left = potential_position;
                self.current_piece.potential_top_left = potential_position;
                self.current_piece.change_shape(direction);
            } else {
                let mut potential_position = self.current_piece.top_left;
                potential_position.x -= 1;
                let collision_found = self.well
                    .check_for_collisions(&next_shape, &potential_position);

                if !collision_found {
                    self.current_piece.top_left = potential_position;
                    self.current_piece.potential_top_left = potential_position;
                    self.current_piece.change_shape(direction);
                }
            }
        }

    }

    /// Advance the fall time. If enough time has passed, allow gravity to
    /// affect the current piece.
    /// Returns a `GameResult<false>` if a landing occured.
    /// Returns a `GameResult<true>` if no landing occured and the piece can advance.
    fn handle_gravity(&mut self, dt: Duration) -> GameResult<bool> {
        self.fall_timer += dt.as_subsec_millis();

        if self.fall_timer >= FALL_SPEED {
            let current_shape = self.current_piece.get_shape();
            self.fall_timer = 0.0;
            self.current_piece.potential_top_left.y += 1;

            let did_land = self.well
                .check_for_landing(&current_shape, &self.current_piece.potential_top_left);

            if did_land {
                if self.current_piece.top_left.y < 2 {
                    println!("game over!");
                    self.game_over = true;
                    return Ok(false);
                }

                // game isn't over - take another piece and move to next frame
                self.well.land(&self.current_piece);
                self.current_piece = self.bag.take_piece();
                return Ok(false);
            }

            // piece did not land - advance!
            self.current_piece.top_left = self.current_piece.potential_top_left;
        }

        Ok(true)
    }

    fn handle_shadow_piece(&mut self) -> GameResult<()> {
        let mut shadow_position = self.current_piece.top_left;
        let mut potential_shadow_position = shadow_position;
        loop {
            potential_shadow_position.y += 1;
            let collision_found = self.well
                .check_for_landing(&self.current_piece.get_shape(), &potential_shadow_position);

            if collision_found {
                break;
            }

            shadow_position = potential_shadow_position;
            self.current_piece.set_shadow_position(shadow_position);
        }

        Ok(())
    }

    fn handle_line_clears(&mut self) -> GameResult<()> {
        // check for line clears
        let mut lines_cleared = 0;
        for r in (0..self.well.data.len()).rev() {
            let mut is_row_filled = true;
            for (c, _) in self.well.data[r].iter().enumerate() {
                if self.well.data[r][c] == 0 {
                    is_row_filled = false;
                    break; // no need to continue iterating, line is not clear...
                }
            }

            if is_row_filled {
                // TODO: implement more line clearing algorithms
                // TODO: make the current line clearing algorithm user selectable
                self.well.naive_line_clear(r);
                lines_cleared += 1;
            }
        }

        // add to score
        let level = self.level;
        match lines_cleared {
            1 => self.increase_score(40, level),
            2 => self.increase_score(100, level),
            3 => self.increase_score(300, level),
            4 => self.increase_score(1200, level),
            _ => (),
        }

        self.cleared_lines += lines_cleared;

        Ok(())
    }

    fn increase_score(&mut self, base_score: u32, level: u32) {
        self.score += base_score * (level + 1);
    }
}

impl EventHandler for PlayState {
    fn update(&mut self,
              ctx: &mut Context,
              assets: &Assets,
              dt: Duration)
              -> GameResult<Transition> {
        if self.game_over {
            return Ok(Transition::Push(Box::new(GameOverState::new(ctx,
                                                                   assets,
                                                                   self.score,
                                                                   self.cleared_lines)?)));
        }

        self.handle_user_input(dt)?;

        // handle shadow piece
        // TODO: put behind option
        self.handle_shadow_piece()?;

        // handle gravity - return from update if our current piece landed
        if let Ok(false) = self.handle_gravity(dt) {
            return Ok(Transition::None);
        }

        self.handle_line_clears()?;

        // update ui
        self.ui_next.update(ctx, assets, self.current_piece.get_type());
        self.ui_lines.update(ctx, assets, &self.cleared_lines.to_string());
        self.ui_score.update(ctx, assets, &self.score.to_string());

        self.prev_input = self.input;

        Ok(Transition::None)
    }

    fn draw(&mut self, ctx: &mut Context, assets: &Assets) -> GameResult<()> {
        self.well.draw(ctx, assets.get_image("block")?)?;
        self.current_piece
            .draw_shadow(ctx,
                         assets.get_image("block")?,
                         &self.current_piece.get_shadow_position())?;
        self.current_piece.draw(ctx, assets.get_image("block")?)?;

        self.ui_next.draw(ctx, assets, &self.bag)?;
        self.ui_lines.draw(ctx)?;
        self.ui_score.draw(ctx)?;

        Ok(())
    }

    fn key_down_event(&mut self, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        match keycode {
            Keycode::Left => self.input.left.is_active = true,
            Keycode::Right => self.input.right.is_active = true,
            Keycode::Up => self.input.hard_drop.is_active = true,
            Keycode::Down => self.input.soft_drop.is_active = true,
            Keycode::Z => self.input.rotate_counterclockwise.is_active = true,
            Keycode::X => self.input.rotate_clockwise.is_active = true,
            _ => (),
        }

    }

    fn key_up_event(&mut self, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        match keycode {
            Keycode::Left => {
                self.input.left.is_active = false;
                self.input.left.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Right => {
                self.input.right.is_active = false;
                self.input.right.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Up => {
                self.input.hard_drop.is_active = false;
                self.input.hard_drop.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Down => {
                self.input.soft_drop.is_active = false;
                self.input.soft_drop.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Z => {
                self.input.rotate_counterclockwise.is_active = false;
                self.input.rotate_counterclockwise.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::X => {
                self.input.rotate_clockwise.is_active = false;
                self.input.rotate_clockwise.delay_timer = INPUT_DELAY_TIME;
            }
            _ => (),
        }
    }
}
<commit_msg>tweak input delay a bit<commit_after>use std::time::Duration;
use std::ops::AddAssign;
use ggez::{Context, GameResult};

mod well;
mod tetromino;
mod shapes;
mod bag;
mod util;
mod ui_element;

use self::well::Well;
use self::tetromino::Piece;
use self::bag::PieceBag;
use self::util::DurationExt;
use self::ui_element::{UIBlockView, UITextView};
use states::{Assets, Transition};
use states::game_over_state::GameOverState;
use event::*;

const BLOCK_SIZE: f32 = 30.0;
const FALL_SPEED: f64 = 0.5;
const INPUT_DELAY_TIME: f64 = 0.075;

#[derive(Copy, Clone, Debug)]
pub struct Position {
    x: i32,
    y: i32,
}

impl Position {
    fn new(x: i32, y: i32) -> Self {
        Self { x: x, y: y }
    }
}

impl AddAssign for Position {
    fn add_assign(&mut self, other: Position) {
        *self = Position {
            x: self.x + other.x,
            y: self.y + other.y,
        };
    }
}

#[derive(Debug, Copy, Clone)]
pub struct InputStateField {
    is_active: bool,
    delay_timer: f64,
}

impl Default for InputStateField {
    fn default() -> Self {
        InputStateField {
            is_active: false,
            delay_timer: INPUT_DELAY_TIME,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct InputState {
    left: InputStateField,
    right: InputStateField,
    soft_drop: InputStateField,
    hard_drop: InputStateField,
    rotate_clockwise: InputStateField,
    rotate_counterclockwise: InputStateField,
    drop: InputStateField,
}

impl Default for InputState {
    fn default() -> Self {

        InputState {
            left: InputStateField::default(),
            right: InputStateField::default(),
            soft_drop: InputStateField::default(),
            hard_drop: InputStateField::default(),
            rotate_clockwise: InputStateField::default(),
            rotate_counterclockwise: InputStateField::default(),
            drop: InputStateField::default(),
        }
    }
}

pub struct PlayState {
    input: InputState,
    prev_input: InputState,

    well: Well,
    bag: PieceBag,
    current_piece: Piece,

    fall_timer: f64,
    score: u32,
    cleared_lines: u32,
    level: u32,
    game_over: bool,

    // ui elements
    ui_lines: UITextView,
    ui_score: UITextView,
    ui_next: UIBlockView,
}

impl PlayState {
    pub fn new(ctx: &mut Context, assets: &Assets) -> GameResult<PlayState> {
        let mut bag = PieceBag::new();
        let first_piece = bag.take_piece();
        let first_type = first_piece.get_type();

        Ok(PlayState {
            input: InputState::default(),
            prev_input: InputState::default(),

            well: Well::new(),
            bag: bag,
            current_piece: first_piece,

            fall_timer: 0.0,
            score: 0,
            cleared_lines: 0,
            level: 0,
            game_over: false,

            ui_lines: UITextView::new(ctx, assets, Position::new(775, 300), "Lines", "0"),
            ui_score: UITextView::new(ctx, assets, Position::new(775, 450), "Score", "0"),
            ui_next: UIBlockView::new(ctx, assets, Position::new(775, 100), "Next", first_type),
        })
    }

    fn handle_user_input(&mut self, dt: Duration) -> GameResult<()> {
        if self.input.left.is_active {
            self.input.left.delay_timer += dt.as_subsec_millis();
            if self.input.left.delay_timer >= INPUT_DELAY_TIME {
                self.move_piece(Position::new(-1, 0));
                self.input.left.delay_timer = 0.0;
            }
        } else if self.input.right.is_active {
            self.input.right.delay_timer += dt.as_subsec_millis();
            if self.input.right.delay_timer >= INPUT_DELAY_TIME {
                self.move_piece(Position::new(1, 0));
                self.input.right.delay_timer = 0.0;
            }
        } else if self.input.soft_drop.is_active {
            self.input.soft_drop.delay_timer += dt.as_subsec_millis();
            if self.input.soft_drop.delay_timer >= INPUT_DELAY_TIME {
                self.move_piece(Position::new(0, 1));
                self.input.soft_drop.delay_timer = 0.0;
            }
        } else if self.input.rotate_clockwise.is_active {
            if self.input.rotate_clockwise.is_active != self.prev_input.rotate_clockwise.is_active {
                self.rotate_piece(1);
            }
        } else if self.input.rotate_counterclockwise.is_active {
            if self.input.rotate_counterclockwise.is_active !=
               self.prev_input.rotate_counterclockwise.is_active {
                self.rotate_piece(-1);
            }
        } else if self.input.hard_drop.is_active {
            if self.input.hard_drop.is_active != self.prev_input.hard_drop.is_active {
                self.current_piece.top_left = self.current_piece.get_shadow_position();
                self.well.land(&self.current_piece);
                self.current_piece = self.bag.take_piece();
            }
        }

        Ok(())
    }

    fn move_piece(&mut self, potential_new_position: Position) {
        self.current_piece.potential_top_left += potential_new_position;
        let current_shape = self.current_piece.get_shape();
        let collision_found = self.well
            .check_for_collisions(&current_shape, &self.current_piece.potential_top_left);

        if collision_found {
            self.current_piece.potential_top_left = self.current_piece.top_left;
        }

        self.current_piece.top_left = self.current_piece.potential_top_left; // advance tetromino
    }

    fn rotate_piece(&mut self, direction: i32) {
        let next_shape = self.current_piece.get_next_shape(direction);
        let collision_found = self.well
            .check_for_collisions(&next_shape, &self.current_piece.top_left);

        if !collision_found {
            self.current_piece.change_shape(direction);
        } else {
            // wall kick attempt!
            // need to do 2 checks:
            // move one piece to the right & perform all above checks
            let mut potential_position = self.current_piece.top_left; // creates a copy of 'Position' struct
            potential_position.x += 1;
            let collision_found = self.well
                .check_for_collisions(&next_shape, &potential_position);

            if !collision_found {
                self.current_piece.top_left = potential_position;
                self.current_piece.potential_top_left = potential_position;
                self.current_piece.change_shape(direction);
            } else {
                let mut potential_position = self.current_piece.top_left;
                potential_position.x -= 1;
                let collision_found = self.well
                    .check_for_collisions(&next_shape, &potential_position);

                if !collision_found {
                    self.current_piece.top_left = potential_position;
                    self.current_piece.potential_top_left = potential_position;
                    self.current_piece.change_shape(direction);
                }
            }
        }

    }

    /// Advance the fall time. If enough time has passed, allow gravity to
    /// affect the current piece.
    /// Returns a `GameResult<false>` if a landing occured.
    /// Returns a `GameResult<true>` if no landing occured and the piece can advance.
    fn handle_gravity(&mut self, dt: Duration) -> GameResult<bool> {
        self.fall_timer += dt.as_subsec_millis();

        if self.fall_timer >= FALL_SPEED {
            let current_shape = self.current_piece.get_shape();
            self.fall_timer = 0.0;
            self.current_piece.potential_top_left.y += 1;

            let did_land = self.well
                .check_for_landing(&current_shape, &self.current_piece.potential_top_left);

            if did_land {
                if self.current_piece.top_left.y < 2 {
                    println!("game over!");
                    self.game_over = true;
                    return Ok(false);
                }

                // game isn't over - take another piece and move to next frame
                self.well.land(&self.current_piece);
                self.current_piece = self.bag.take_piece();
                return Ok(false);
            }

            // piece did not land - advance!
            self.current_piece.top_left = self.current_piece.potential_top_left;
        }

        Ok(true)
    }

    fn handle_shadow_piece(&mut self) -> GameResult<()> {
        let mut shadow_position = self.current_piece.top_left;
        let mut potential_shadow_position = shadow_position;
        loop {
            potential_shadow_position.y += 1;
            let collision_found = self.well
                .check_for_landing(&self.current_piece.get_shape(), &potential_shadow_position);

            if collision_found {
                break;
            }

            shadow_position = potential_shadow_position;
            self.current_piece.set_shadow_position(shadow_position);
        }

        Ok(())
    }

    fn handle_line_clears(&mut self) -> GameResult<()> {
        // check for line clears
        let mut lines_cleared = 0;
        for r in (0..self.well.data.len()).rev() {
            let mut is_row_filled = true;
            for (c, _) in self.well.data[r].iter().enumerate() {
                if self.well.data[r][c] == 0 {
                    is_row_filled = false;
                    break; // no need to continue iterating, line is not clear...
                }
            }

            if is_row_filled {
                // TODO: implement more line clearing algorithms
                // TODO: make the current line clearing algorithm user selectable
                self.well.naive_line_clear(r);
                lines_cleared += 1;
            }
        }

        // add to score
        let level = self.level;
        match lines_cleared {
            1 => self.increase_score(40, level),
            2 => self.increase_score(100, level),
            3 => self.increase_score(300, level),
            4 => self.increase_score(1200, level),
            _ => (),
        }

        self.cleared_lines += lines_cleared;

        Ok(())
    }

    fn increase_score(&mut self, base_score: u32, level: u32) {
        self.score += base_score * (level + 1);
    }
}

impl EventHandler for PlayState {
    fn update(&mut self,
              ctx: &mut Context,
              assets: &Assets,
              dt: Duration)
              -> GameResult<Transition> {
        if self.game_over {
            return Ok(Transition::Push(Box::new(GameOverState::new(ctx,
                                                                   assets,
                                                                   self.score,
                                                                   self.cleared_lines)?)));
        }

        self.handle_user_input(dt)?;

        // handle shadow piece
        // TODO: put behind option
        self.handle_shadow_piece()?;

        // handle gravity - return from update if our current piece landed
        if let Ok(false) = self.handle_gravity(dt) {
            return Ok(Transition::None);
        }

        self.handle_line_clears()?;

        // update ui
        self.ui_next.update(ctx, assets, self.current_piece.get_type());
        self.ui_lines.update(ctx, assets, &self.cleared_lines.to_string());
        self.ui_score.update(ctx, assets, &self.score.to_string());

        self.prev_input = self.input;

        Ok(Transition::None)
    }

    fn draw(&mut self, ctx: &mut Context, assets: &Assets) -> GameResult<()> {
        self.well.draw(ctx, assets.get_image("block")?)?;
        self.current_piece
            .draw_shadow(ctx,
                         assets.get_image("block")?,
                         &self.current_piece.get_shadow_position())?;
        self.current_piece.draw(ctx, assets.get_image("block")?)?;

        self.ui_next.draw(ctx, assets, &self.bag)?;
        self.ui_lines.draw(ctx)?;
        self.ui_score.draw(ctx)?;

        Ok(())
    }

    fn key_down_event(&mut self, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        match keycode {
            Keycode::Left => self.input.left.is_active = true,
            Keycode::Right => self.input.right.is_active = true,
            Keycode::Up => self.input.hard_drop.is_active = true,
            Keycode::Down => self.input.soft_drop.is_active = true,
            Keycode::Z => self.input.rotate_counterclockwise.is_active = true,
            Keycode::X => self.input.rotate_clockwise.is_active = true,
            _ => (),
        }

    }

    fn key_up_event(&mut self, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        match keycode {
            Keycode::Left => {
                self.input.left.is_active = false;
                self.input.left.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Right => {
                self.input.right.is_active = false;
                self.input.right.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Up => {
                self.input.hard_drop.is_active = false;
                self.input.hard_drop.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Down => {
                self.input.soft_drop.is_active = false;
                self.input.soft_drop.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Z => {
                self.input.rotate_counterclockwise.is_active = false;
                self.input.rotate_counterclockwise.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::X => {
                self.input.rotate_clockwise.is_active = false;
                self.input.rotate_clockwise.delay_timer = INPUT_DELAY_TIME;
            }
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>use std::str::raw::c_str_to_static_slice;
use libc::{c_char, size_t};

enum Selector { }
pub enum Object { }

pub trait Message { }

impl Message for Object { }

pub struct Sel {
	ptr: *mut Selector,
}

pub struct Class {
	ptr: *mut Object,
}

pub type Imp = extern fn(*mut Object, Sel, ...);

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> Class;
	pub fn class_getName(cls: Class) -> *const c_char;
	pub fn class_addMethod(cls: Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn object_getClass(obj: *mut Object) -> Class;

	pub fn objc_allocateClassPair(superclass: Class, name: *const c_char, extraBytes: size_t) -> Class;
	pub fn objc_disposeClassPair(cls: Class);
	pub fn objc_registerClassPair(cls: Class);

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel {
		Sel { ptr: self.ptr }
	}
}

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}

impl Class {
	pub fn get(name: &str) -> Option<Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.is_nil() {
			None
		} else {
			Some(cls)
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl ToMessage for Class {
	fn as_ptr(&self) -> *mut Object {
		self.ptr
	}
}
<commit_msg>Fixed type of Imp.<commit_after>use std::str::raw::c_str_to_static_slice;
use libc::{c_char, size_t};

enum Selector { }
pub enum Object { }

pub trait Message { }

impl Message for Object { }

pub struct Sel {
	ptr: *mut Selector,
}

pub struct Class {
	ptr: *mut Object,
}

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> Class;
	pub fn class_getName(cls: Class) -> *const c_char;
	pub fn class_addMethod(cls: Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn object_getClass(obj: *mut Object) -> Class;

	pub fn objc_allocateClassPair(superclass: Class, name: *const c_char, extraBytes: size_t) -> Class;
	pub fn objc_disposeClassPair(cls: Class);
	pub fn objc_registerClassPair(cls: Class);

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel {
		Sel { ptr: self.ptr }
	}
}

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}

impl Class {
	pub fn get(name: &str) -> Option<Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.is_nil() {
			None
		} else {
			Some(cls)
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl ToMessage for Class {
	fn as_ptr(&self) -> *mut Object {
		self.ptr
	}
}
<|endoftext|>