<commit_before>// riff-wave -- Basic support for reading and writing wave PCM files.
// Copyright (c) 2016 Kevin Brothaler and the riff-wave project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::cmp;
use std::error;
use std::fmt;
use std::io;
use std::io::{Seek, Write};
use std::result;

use byteorder::{LittleEndian, WriteBytesExt};

use super::PcmFormat;
use super::{FORMAT_UNCOMPRESSED_PCM, MAX_I24_VALUE, MIN_I24_VALUE};

// MARK: Error types

#[derive(Debug)]
pub enum WriteError {
    /// The file format is incorrect or unsupported.
    Format(WriteErrorKind),
    /// An IO error occurred.
    Io(io::Error),
}

/// Represents a result when reading a wave file.
pub type WriteResult<T> = result::Result<T, WriteError>;

impl fmt::Display for WriteError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WriteError::Format(ref err_kind) => write!(f, "Format error: {}", err_kind),
            WriteError::Io(ref err) => write!(f, "IO error: {}", err),
        }
    }
}

/// Represents a file format error, when incorrect parameters have been specified.
#[derive(Debug)]
pub enum WriteErrorKind {
    /// The number of channels is zero, which is invalid.
    NumChannelsIsZero,
    /// The sample rate is zero, which is invalid.
    SampleRateIsZero,
    /// Only 8-bit, 16-bit, 24-bit and 32-bit PCM files are supported.
    UnsupportedBitsPerSample(u16),
}

impl WriteErrorKind {
    fn to_string(&self) -> &str {
        match *self {
            WriteErrorKind::NumChannelsIsZero => "Number of channels is zero",
            WriteErrorKind::SampleRateIsZero => "Sample rate is zero",
            WriteErrorKind::UnsupportedBitsPerSample(_) => "Unsupported bits per sample",
        }
    }
}

impl fmt::Display for WriteErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl error::Error for WriteError {
    fn description(&self) -> &str {
        match *self {
            WriteError::Format(ref kind) => kind.to_string(),
            WriteError::Io(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            WriteError::Format(_) => None,
            WriteError::Io(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for WriteError {
    fn from(err: io::Error) -> WriteError {
        WriteError::Io(err)
    }
}

// MARK: Writing functions

trait WriteWaveExt: Write + Seek {
    fn write_standard_wave_header(&mut self, pcm_format: &PcmFormat) -> WriteResult<()> {
        try!(self.write_riff_wave_chunk_header());
        try!(self.write_standard_fmt_subchunk(pcm_format.num_channels,
                                              pcm_format.sample_rate,
                                              pcm_format.bits_per_sample));
        try!(self.write_data_subchunk_header());

        Ok(())
    }

    fn write_riff_wave_chunk_header(&mut self) -> WriteResult<()> {
        try!(self.write_all(b"RIFF"));                      // Identifier
        try!(self.write_u32_l(36));                         // File size (header) minus eight bytes
        try!(self.write_all(b"WAVE"));                      // Identifier

        Ok(())
    }

    fn write_standard_fmt_subchunk(&mut self,
                                   num_channels: u16,
                                   sample_rate: u32,
                                   bits_per_sample: u16)
                                   -> WriteResult<()> {
        if num_channels == 0 {
            return Err(WriteError::Format(WriteErrorKind::NumChannelsIsZero));
        } else if sample_rate == 0 {
            return Err(WriteError::Format(WriteErrorKind::SampleRateIsZero));
        } else if bits_per_sample != 8 && bits_per_sample != 16 && bits_per_sample != 24 &&
           bits_per_sample != 32 {
            return Err(WriteError::Format(WriteErrorKind::UnsupportedBitsPerSample(bits_per_sample)));
        }

        try!(self.write_all(b"fmt "));                      // "fmt " chunk and size
        try!(self.write_u32_l(16));                         // Subchunk size
        try!(self.write_u16_l(FORMAT_UNCOMPRESSED_PCM));    // PCM Format
        try!(self.write_u16_l(num_channels));               // Number of channels
        try!(self.write_u32_l(sample_rate));                // Sample rate

        let bytes_per_sample = bits_per_sample / 8;
        let block_align = num_channels * bytes_per_sample;
        let byte_rate = block_align as u32 * sample_rate;

        try!(self.write_u32_l(byte_rate));                  // Byte rate
        try!(self.write_u16_l(block_align));                // Block align
        try!(self.write_u16_l(bits_per_sample));            // Bits per sample

        Ok(())
    }

    fn write_data_subchunk_header(&mut self) -> WriteResult<()> {
        try!(self.write_all(b"data"));                      // Start of "data" subchunk
        try!(self.write_u32_l(0));                          // Size of data subchunk.

        Ok(())
    }

    fn write_u16_l(&mut self, n: u16) -> io::Result<()> {
        self.write_u16::<LittleEndian>(n)
    }

    fn write_u32_l(&mut self, n: u32) -> io::Result<()> {
        self.write_u32::<LittleEndian>(n)
    }
}

impl<T> WriteWaveExt for T where T: Seek + Write {}

fn clamp(n: i32, min: i32, max: i32) -> i32 {
    cmp::min(cmp::max(n, min), max)
}

/// Helper struct that takes ownership of a writer and can be used to write data
/// to a PCM wave file.
#[derive(Debug)]
pub struct WaveWriter<T>
    where T: Seek + Write
{
    ///  Represents the PCM format for this wave file.
    pub pcm_format: PcmFormat,

    // The underlying writer that we'll use to read data.
    writer: T,
}

// TODO what should we do if an incorrect write_* method is called? Return the error in the result?
impl<T> WaveWriter<T>
    where T: Seek + Write
{
    /// Returns a new wave writer for the given writer.
    pub fn new(num_channels: u16,
               sample_rate: u32,
               bits_per_sample: u16,
               mut writer: T)
               -> WriteResult<WaveWriter<T>> {
        let pcm_format = PcmFormat {
            num_channels: num_channels,
            sample_rate: sample_rate,
            bits_per_sample: bits_per_sample,
        };

        try!(writer.write_standard_wave_header(&pcm_format));

        Ok(WaveWriter {
            pcm_format: pcm_format,
            writer: writer,
        })
    }

    /// Writes a single sample as an unsigned 8-bit value.
    pub fn write_sample_u8(&mut self, sample: u8) -> io::Result<()> {
        self.write_sample(sample, |writer, sample| writer.write_u8(sample))
    }

    /// Writes a single sample as a signed 16-bit value.
    pub fn write_sample_i16(&mut self, sample: i16) -> io::Result<()> {
        self.write_sample(sample,
                          |writer, sample| writer.write_i16::<LittleEndian>(sample))
    }

    /// Writes a single sample as a signed 24-bit value. The value will be truncated
    /// to fit in a 24-bit value.
    pub fn write_sample_i24(&mut self, sample: i32) -> io::Result<()> {
        self.write_sample(sample, |writer, sample| {
            writer.write_int::<LittleEndian>(3,
                clamp(sample, MIN_I24_VALUE, MAX_I24_VALUE) as usize)
        })
    }

    /// Writes a single sample as a signed 32-bit value.
    pub fn write_sample_i32(&mut self, sample: i32) -> io::Result<()> {
        self.write_sample(sample,
                          |writer, sample| writer.write_i32::<LittleEndian>(sample))
    }

    fn write_sample<F, S>(&mut self, sample: S, write_data: F) -> io::Result<()>
        where F: Fn(&mut T, S) -> io::Result<()>
    {
        Ok(try!(write_data(&mut self.writer, sample)))
    }

    /// Consumes this writer, returning the underlying value.
    pub fn into_inner(self) -> T {
        self.writer
    }
}

// MARK: Tests

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use super::super::WaveReader;
    use super::{WriteError, WriteErrorKind, WaveWriter};

    // Validation tests

    #[test]
    fn test_validate_doesnt_accept_zero_channels() {
        let wave_writer = WaveWriter::new(0, 44100, 16, Cursor::new(Vec::new()));
        assert_matches!(Err(WriteError::Format(WriteErrorKind::NumChannelsIsZero)),
                        wave_writer);
    }

    #[test]
    fn test_validate_doesnt_accept_zero_sample_rate() {
        let wave_writer = WaveWriter::new(1, 0, 16, Cursor::new(Vec::new()));
        assert_matches!(Err(WriteError::Format(WriteErrorKind::SampleRateIsZero)),
                        wave_writer);
    }

    #[test]
    fn test_validate_doesnt_accept_invalid_bitrate() {
        let wave_writer = WaveWriter::new(1, 44100, 12, Cursor::new(Vec::new()));
        assert_matches!(Err(WriteError::Format(WriteErrorKind::UnsupportedBitsPerSample(12))),
                        wave_writer);
    }

    #[test]
    fn test_validate_accepts_valid_combination() {
        let wave_writer = WaveWriter::new(1, 44100, 16, Cursor::new(Vec::new()));
        assert_matches!(Ok(_), wave_writer);
    }

    // Header validation tests

    #[test]
    fn test_header_is_acceptable() {
        let data = Vec::new();
        let mut cursor = Cursor::new(data);
        let wave_writer = WaveWriter::new(1, 44100, 16, cursor).unwrap();
        let mut cursor = wave_writer.into_inner();

        cursor.set_position(0);

        let wave_reader = WaveReader::new(cursor).unwrap();

        assert_eq!(1, wave_reader.pcm_format.num_channels);
        assert_eq!(44100, wave_reader.pcm_format.sample_rate);
        assert_eq!(16, wave_reader.pcm_format.bits_per_sample);
    }

    // TODO test header
}
<commit_msg>Add code to sync the header after writing samples.<commit_after>// riff-wave -- Basic support for reading and writing wave PCM files.
// Copyright (c) 2016 Kevin Brothaler and the riff-wave project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::cmp;
use std::error;
use std::fmt;
use std::io;
use std::io::{Seek, SeekFrom, Write};
use std::result;

use byteorder::{LittleEndian, WriteBytesExt};

use super::PcmFormat;
use super::{FORMAT_UNCOMPRESSED_PCM, MAX_I24_VALUE, MIN_I24_VALUE};

// MARK: Error types

#[derive(Debug)]
pub enum WriteError {
    /// The file format is incorrect or unsupported.
    Format(WriteErrorKind),
    /// An IO error occurred.
    Io(io::Error),
}

/// Represents a result when reading a wave file.
pub type WriteResult<T> = result::Result<T, WriteError>;

impl fmt::Display for WriteError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WriteError::Format(ref err_kind) => write!(f, "Format error: {}", err_kind),
            WriteError::Io(ref err) => write!(f, "IO error: {}", err),
        }
    }
}

/// Represents a file format error, when incorrect parameters have been specified.
#[derive(Debug)]
pub enum WriteErrorKind {
    /// The number of channels is zero, which is invalid.
    NumChannelsIsZero,
    /// The sample rate is zero, which is invalid.
    SampleRateIsZero,
    /// Only 8-bit, 16-bit, 24-bit and 32-bit PCM files are supported.
    UnsupportedBitsPerSample(u16),
}

impl WriteErrorKind {
    fn to_string(&self) -> &str {
        match *self {
            WriteErrorKind::NumChannelsIsZero => "Number of channels is zero",
            WriteErrorKind::SampleRateIsZero => "Sample rate is zero",
            WriteErrorKind::UnsupportedBitsPerSample(_) => "Unsupported bits per sample",
        }
    }
}

impl fmt::Display for WriteErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl error::Error for WriteError {
    fn description(&self) -> &str {
        match *self {
            WriteError::Format(ref kind) => kind.to_string(),
            WriteError::Io(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            WriteError::Format(_) => None,
            WriteError::Io(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for WriteError {
    fn from(err: io::Error) -> WriteError {
        WriteError::Io(err)
    }
}

// MARK: Writing functions

trait WriteWaveExt: Write + Seek {
    fn write_standard_wave_header(&mut self, pcm_format: &PcmFormat) -> WriteResult<()> {
        try!(self.write_riff_wave_chunk_header());
        try!(self.write_standard_fmt_subchunk(pcm_format.num_channels,
                                              pcm_format.sample_rate,
                                              pcm_format.bits_per_sample));
        try!(self.write_data_subchunk_header());

        Ok(())
    }

    fn write_riff_wave_chunk_header(&mut self) -> WriteResult<()> {
        try!(self.write_all(b"RIFF"));                      // Identifier
        try!(self.write_u32_l(36));                         // File size (header) minus eight bytes
        try!(self.write_all(b"WAVE"));                      // Identifier

        Ok(())
    }

    fn write_standard_fmt_subchunk(&mut self,
                                   num_channels: u16,
                                   sample_rate: u32,
                                   bits_per_sample: u16)
                                   -> WriteResult<()> {
        if num_channels == 0 {
            return Err(WriteError::Format(WriteErrorKind::NumChannelsIsZero));
        } else if sample_rate == 0 {
            return Err(WriteError::Format(WriteErrorKind::SampleRateIsZero));
        } else if bits_per_sample != 8 && bits_per_sample != 16 && bits_per_sample != 24 &&
           bits_per_sample != 32 {
            return Err(WriteError::Format(WriteErrorKind::UnsupportedBitsPerSample(bits_per_sample)));
        }

        try!(self.write_all(b"fmt "));                      // "fmt " chunk and size
        try!(self.write_u32_l(16));                         // Subchunk size
        try!(self.write_u16_l(FORMAT_UNCOMPRESSED_PCM));    // PCM Format
        try!(self.write_u16_l(num_channels));               // Number of channels
        try!(self.write_u32_l(sample_rate));                // Sample rate

        let bytes_per_sample = bits_per_sample / 8;
        let block_align = num_channels * bytes_per_sample;
        let byte_rate = block_align as u32 * sample_rate;

        try!(self.write_u32_l(byte_rate));                  // Byte rate
        try!(self.write_u16_l(block_align));                // Block align
        try!(self.write_u16_l(bits_per_sample));            // Bits per sample

        Ok(())
    }

    fn write_data_subchunk_header(&mut self) -> WriteResult<()> {
        try!(self.write_all(b"data"));                      // Start of "data" subchunk
        try!(self.write_u32_l(0));                          // Size of data subchunk.

        Ok(())
    }

    fn write_u16_l(&mut self, n: u16) -> io::Result<()> {
        self.write_u16::<LittleEndian>(n)
    }

    fn write_u32_l(&mut self, n: u32) -> io::Result<()> {
        self.write_u32::<LittleEndian>(n)
    }
}

impl<T> WriteWaveExt for T where T: Seek + Write {}

fn clamp(n: i32, min: i32, max: i32) -> i32 {
    cmp::min(cmp::max(n, min), max)
}

/// Helper struct that takes ownership of a writer and can be used to write data
/// to a PCM wave file.
#[derive(Debug)]
pub struct WaveWriter<T>
    where T: Seek + Write
{
    ///  Represents the PCM format for this wave file.
    pub pcm_format: PcmFormat,

    // How many samples have been written.
    written_samples: u32,

    // The underlying writer that we'll use to read data.
    writer: T,
}

// TODO what should we do if an incorrect write_* method is called? Return the error in the result?
impl<T> WaveWriter<T>
    where T: Seek + Write
{
    /// Returns a new wave writer for the given writer.
    pub fn new(num_channels: u16,
               sample_rate: u32,
               bits_per_sample: u16,
               mut writer: T)
               -> WriteResult<WaveWriter<T>> {
        let pcm_format = PcmFormat {
            num_channels: num_channels,
            sample_rate: sample_rate,
            bits_per_sample: bits_per_sample,
        };

        try!(writer.write_standard_wave_header(&pcm_format));

        Ok(WaveWriter {
            pcm_format: pcm_format,
            written_samples: 0,
            writer: writer,
        })
    }

    /// Writes a single sample as an unsigned 8-bit value.
    pub fn write_sample_u8(&mut self, sample: u8) -> io::Result<()> {
        self.write_sample(sample, |writer, sample| writer.write_u8(sample))
    }

    /// Writes a single sample as a signed 16-bit value.
    pub fn write_sample_i16(&mut self, sample: i16) -> io::Result<()> {
        self.write_sample(sample, |writer, sample|
            writer.write_i16::<LittleEndian>(sample))
    }

    /// Writes a single sample as a signed 24-bit value. The value will be truncated
    /// to fit in a 24-bit value.
    pub fn write_sample_i24(&mut self, sample: i32) -> io::Result<()> {
        self.write_sample(sample, |writer, sample| {
            writer.write_int::<LittleEndian>(3,
                clamp(sample, MIN_I24_VALUE, MAX_I24_VALUE) as usize)
        })
    }

    /// Writes a single sample as a signed 32-bit value.
    pub fn write_sample_i32(&mut self, sample: i32) -> io::Result<()> {
        self.write_sample(sample, |writer, sample|
            writer.write_i32::<LittleEndian>(sample))
    }

    fn write_sample<F, S>(&mut self, sample: S, write_data: F) -> io::Result<()>
        where F: Fn(&mut T, S) -> io::Result<()>
    {
        try!(write_data(&mut self.writer, sample));
        self.written_samples = self.written_samples + 1;
        Ok(())
    }

    /// Updates the header at the beginning of the file with the new chunk sizes.
    pub fn sync_header(&mut self) -> io::Result<()> {
        let data_chunk_size = self.written_samples * self.pcm_format.bits_per_sample as u32 / 8;
        let riff_chunk_size = 36 + data_chunk_size;

        // File size minus eight bytes
        try!(self.writer.seek(SeekFrom::Start(4)));
        try!(self.writer.write_u32_l(riff_chunk_size));

        // Data size minus eight bytes
        try!(self.writer.seek(SeekFrom::Start(40)));
        try!(self.writer.write_u32_l(data_chunk_size));

        // Seek back to the end so we can continue writing
        try!(self.writer.seek(SeekFrom::End(0)));

        Ok(())
    }

    /// Consumes this writer, returning the underlying value.
    pub fn into_inner(self) -> T {
        self.writer
    }
}

// MARK: Tests

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use super::super::WaveReader;
    use super::{WriteError, WriteErrorKind, WaveWriter};

    // Validation tests

    #[test]
    fn test_validate_doesnt_accept_zero_channels() {
        let wave_writer = WaveWriter::new(0, 44100, 16, Cursor::new(Vec::new()));
        assert_matches!(Err(WriteError::Format(WriteErrorKind::NumChannelsIsZero)),
                        wave_writer);
    }

    #[test]
    fn test_validate_doesnt_accept_zero_sample_rate() {
        let wave_writer = WaveWriter::new(1, 0, 16, Cursor::new(Vec::new()));
        assert_matches!(Err(WriteError::Format(WriteErrorKind::SampleRateIsZero)),
                        wave_writer);
    }

    #[test]
    fn test_validate_doesnt_accept_invalid_bitrate() {
        let wave_writer = WaveWriter::new(1, 44100, 12, Cursor::new(Vec::new()));
        assert_matches!(Err(WriteError::Format(WriteErrorKind::UnsupportedBitsPerSample(12))),
                        wave_writer);
    }

    #[test]
    fn test_validate_accepts_valid_combination() {
        let wave_writer = WaveWriter::new(1, 44100, 16, Cursor::new(Vec::new()));
        assert_matches!(Ok(_), wave_writer);
    }

    // Header validation tests

    #[test]
    fn test_header_is_acceptable() {
        let data = Vec::new();
        let mut cursor = Cursor::new(data);
        let wave_writer = WaveWriter::new(1, 44100, 16, cursor).unwrap();
        let mut cursor = wave_writer.into_inner();

        cursor.set_position(0);

        let wave_reader = WaveReader::new(cursor).unwrap();

        assert_eq!(1, wave_reader.pcm_format.num_channels);
        assert_eq!(44100, wave_reader.pcm_format.sample_rate);
        assert_eq!(16, wave_reader.pcm_format.bits_per_sample);
    }

    // Write validation tests

    #[test]
    fn test_header_sync_when_no_data_written() {
      let data = Vec::new();
        let mut cursor = Cursor::new(data);
        let mut wave_writer = WaveWriter::new(1, 44100, 16, cursor).unwrap();
        wave_writer.sync_header().unwrap();
        let mut cursor = wave_writer.into_inner();

        cursor.set_position(0);

        let wave_reader = WaveReader::new(cursor).unwrap();
        let cursor = wave_reader.into_inner();
        let data = cursor.into_inner();

        assert_eq!(44, data.len());
        // We're not currently surfacing the chunk/subchunk info in the reader
        // so just access the data directly.

        // Should match 36 in little-endian format.
        assert_eq!(b"\x24\x00\x00\x00", &data[4..8]);

        // Should match 0 in little-endian format.
        assert_eq!(b"\x00\x00\x00\x00", &data[40..44]);
    }

    #[test]
    fn test_header_sync_when_ten_samples_written() {
        let data = Vec::new();
        let mut cursor = Cursor::new(data);
        let mut wave_writer = WaveWriter::new(1, 44100, 16, cursor).unwrap();

        for i in 0..10 {
            wave_writer.write_sample_i16(i as i16).unwrap();
        }

        wave_writer.sync_header().unwrap();

        let mut cursor = wave_writer.into_inner();

        cursor.set_position(0);

        let wave_reader = WaveReader::new(cursor).unwrap();
        let cursor = wave_reader.into_inner();
        let data = cursor.into_inner();

        assert_eq!(64, data.len());
        // We're not currently surfacing the chunk/subchunk info in the reader
        // so just access the data directly.

        // Should match 56 in little-endian format.
        assert_eq!(b"\x38\x00\x00\x00", &data[4..8]);

        // Should match 20 in little-endian format.
        assert_eq!(b"\x14\x00\x00\x00", &data[40..44]);
    }

    // TODO test header
}
<|endoftext|>


<commit_before>extern crate mass_nes;
use self::mass_nes::nes::{Machine, Region, Cartridge, Controller};

use std::fs::File;
use std::path::Path;
use std::convert::AsRef;
use std::sync::Mutex;

#[derive(Debug, Copy, Clone)]
pub enum Condition {
    Equals(u16, u8),
}

pub fn run<T>(rom: T, frames: u32, condition: Condition) where T: AsRef<str> {
    let mut path = Path::new("./tests/").to_path_buf();
    path.push(Path::new(rom.as_ref()));
    let mut file = File::open(path).unwrap();
    let cart = Cartridge::load(&mut file).unwrap();

    let closed = Mutex::new(false);

    let mut machine = Machine::new(Region::Ntsc, cart,
    |_| {
    },
    || {
        *closed.lock().unwrap()
    },
    || {
        Controller {
            a: false,
            b: false,
            select: false,
            start: false,
            up: false,
            down: false,
            left: false,
            right: false,
        }
    },
    |system, state| {
        let mut closed = closed.lock().unwrap();
        let nes_frame = system.debug.frame(state);
        *closed = nes_frame > frames;
        if nes_frame >= frames {
            match condition {
                Condition::Equals(a, v) => {
                    let nes_val = system.debug.peek(system, state, a);
                    assert!(v == nes_val,
                            "Expected '0x{:04X}' to be '0x{:02X}', found '0x{:02X}'.",
                            a, v, nes_val);
                }
            }
        }
    });

    machine.run();
}
<commit_msg>Fix test runner<commit_after>extern crate mass_nes;
use self::mass_nes::nes::{Machine, Region, Cartridge, Controller};

use std::fs::File;
use std::path::Path;
use std::convert::AsRef;
use std::sync::Mutex;

#[derive(Debug, Copy, Clone)]
pub enum Condition {
    Equals(u16, u8),
}

pub fn run<T>(rom: T, frames: u32, condition: Condition) where T: AsRef<str> {
    let mut path = Path::new("./tests/").to_path_buf();
    path.push(Path::new(rom.as_ref()));
    let mut file = File::open(path).unwrap();
    let cart = Cartridge::load(&mut file).unwrap();

    let closed = Mutex::new(false);

    let mut machine = Machine::new(Region::Ntsc, cart,
    |_| {
    },
    |_| {
    },
    || {
        *closed.lock().unwrap()
    },
    || {
        Controller {
            a: false,
            b: false,
            select: false,
            start: false,
            up: false,
            down: false,
            left: false,
            right: false,
        }
    },
    |system, state| {
        let mut closed = closed.lock().unwrap();
        let nes_frame = system.debug.frame(state);
        *closed = nes_frame > frames;
        if nes_frame >= frames {
            match condition {
                Condition::Equals(a, v) => {
                    let nes_val = system.debug.peek(system, state, a);
                    assert!(v == nes_val,
                            "Expected '0x{:04X}' to be '0x{:02X}', found '0x{:02X}'.",
                            a, v, nes_val);
                }
            }
        }
    });

    machine.run();
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;
extern crate rusqlite;
extern crate jmap;
extern crate time;

#[macro_use]
extern crate log;

mod logger;
mod db;
mod util;
mod record;

use std::default::Default;
use std::io::Write;
use std::io::Read;

use std::fs::File;

use hyper::server::{Request, Response};
use hyper::method::Method::{Post, Get};
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use hyper::header;

use rustc_serialize::json::{Json,ToJson};

use jmap::parse::FromJson;
use jmap::method::{RequestBatch, ResponseBatch, ResponseMethod};
use jmap::method::RequestMethod::*;
use jmap::method::ResponseMethod::*;

use util::RequestContext;
use record::RecordHandler;
use db::Db;

macro_rules! make_crud_method_dispatcher {
    ($method: expr, $rmethods: expr, $r: expr,
     $($ty: ty { $get: ident => $rget: ident, $set: ident => $rset: ident, $getup: ident => $rgetup: ident }),*) => {
        $rmethods = match $method {
            $(
                $get(args, id)
                    => vec!($r.get_records(&args).map(|a| $rget(a, id.clone())).unwrap_or_else(|e| ResponseError(e, id.clone()))),
                $set(args, id)
                    => vec!($r.set_records(&args).map(|a| $rset(a, id.clone())).unwrap_or_else(|e| ResponseError(e, id.clone()))),
                $getup(args, id) => $r.get_record_updates(&args).map(|a| {
                    match a {
                        (a, Some(b)) => vec!($rgetup(a, id.clone()), $rget(b, id.clone())),
                        (a, _)       => vec!($rgetup(a, id.clone())),
                    }
                }).unwrap_or_else(|e| vec!(ResponseError(e, id.clone()))),
            )*
            RequestError(args, id) => vec!(ResponseError(args, id)),
        };
    }
}

fn jmap_handler(batch: RequestBatch) -> ResponseBatch {
    let mut rbatch: ResponseBatch = ResponseBatch::default();

    let r = RequestContext {
        userid: 1, // XXX get userid from auth
        db: Db::open().unwrap(),
    };

    for method in batch.0.into_iter() {
        let rmethods: Vec<ResponseMethod>;
        make_crud_method_dispatcher!(method, rmethods, r,
            Calendar {
                GetCalendars       => Calendars,
                SetCalendars       => CalendarsSet,
                GetCalendarUpdates => CalendarUpdates
            },
            CalendarEvent {
                GetCalendarEvents       => CalendarEvents,
                SetCalendarEvents       => CalendarEventsSet,
                GetCalendarEventUpdates => CalendarEventUpdates
            },
            Contact {
                GetContacts       => Contacts,
                SetContacts       => ContactsSet,
                GetContactUpdates => ContactUpdates
            },
            ContactGroup {
                GetContactGroups       => ContactGroups,
                SetContactGroups       => ContactGroupsSet,
                GetContactGroupUpdates => ContactGroupUpdates
            },
            Mailbox {
                GetMailboxes      => Mailboxes,
                SetMailboxes      => MailboxesSet,
                GetMailboxUpdates => MailboxUpdates
            }
        );

        rbatch.0.extend(rmethods.into_iter());
    }

    rbatch
}

fn file_handler(path: &String) -> String {
    let mut f = File::open(String::from("client")+path).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    s
}

fn finish_response(mut res: Response, code: StatusCode, body: Option<&[u8]>) {
    *res.status_mut() = code;

    match res.start()
        .and_then(|mut res|
                  match body {
                      Some(ref b) => {
                          try!(res.write_all(b));
                          Ok(res)
                      }
                      None => {
                          Ok(res)
                      }
                  })
        .and_then(|res| res.end()) {
            Err(e) => error!("response error: {}", e),
            _      => (),
        };
}

fn http_handler(mut req: Request, mut res: Response) {
    res.headers_mut().set(header::Server("salada/0.0.5".to_string()));

    let method = req.method.clone();
    let uri = req.uri.clone();

    match (method, uri) {
        (Post, AbsolutePath(ref path)) if path == "/jmap" => {
            match Json::from_reader(&mut req) {
                Ok(j) => match RequestBatch::from_json(&j) {
                    Ok(b) =>
                        finish_response(res, StatusCode::Ok, Some(jmap_handler(b).to_json().to_string().as_bytes())),
                    Err(e) =>
                        finish_response(res, StatusCode::BadRequest, Some(e.to_string().into_bytes().as_ref())),
                },
                Err(e) =>
                    finish_response(res, StatusCode::BadRequest, Some(e.to_string().into_bytes().as_ref())),
            }
        },

        (_, AbsolutePath(ref path)) if path == "/jmap" => finish_response(res, StatusCode::MethodNotAllowed, None),

        (Get, AbsolutePath(ref path)) => {
            finish_response(res, StatusCode::Ok, Some(file_handler(path).as_ref()))
        },

        _ => finish_response(res, StatusCode::NotFound, None),
    };
}

fn main() {
    logger::init().unwrap();

    info!("Listening on http://127.0.0.1:3000/jmap");
    hyper::Server::http("127.0.0.1:3000").unwrap().handle(http_handler).unwrap();
}
<commit_msg>Return response code and body in same type<commit_after>extern crate hyper;
extern crate rustc_serialize;
extern crate rusqlite;
extern crate jmap;
extern crate time;

#[macro_use]
extern crate log;

mod logger;
mod db;
mod util;
mod record;

use std::default::Default;
use std::io::Write;
use std::io::Read;

use std::fs::File;

use hyper::server::{Request, Response};
use hyper::method::Method::{Post, Get};
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use hyper::header;

use rustc_serialize::json::{Json,ToJson};

use jmap::parse::FromJson;
use jmap::method::{RequestBatch, ResponseBatch, ResponseMethod};
use jmap::method::RequestMethod::*;
use jmap::method::ResponseMethod::*;

use util::RequestContext;
use record::RecordHandler;
use db::Db;

struct StatusBody {
    code: StatusCode,
    body: Option<Vec<u8>>
}
impl StatusBody {
    fn new(code: StatusCode, body: Option<Vec<u8>>) -> StatusBody {
        StatusBody { code: code, body: body }
    }
}

macro_rules! make_crud_method_dispatcher {
    ($method: expr, $rmethods: expr, $r: expr,
     $($ty: ty { $get: ident => $rget: ident, $set: ident => $rset: ident, $getup: ident => $rgetup: ident }),*) => {
        $rmethods = match $method {
            $(
                $get(args, id)
                    => vec!($r.get_records(&args).map(|a| $rget(a, id.clone())).unwrap_or_else(|e| ResponseError(e, id.clone()))),
                $set(args, id)
                    => vec!($r.set_records(&args).map(|a| $rset(a, id.clone())).unwrap_or_else(|e| ResponseError(e, id.clone()))),
                $getup(args, id) => $r.get_record_updates(&args).map(|a| {
                    match a {
                        (a, Some(b)) => vec!($rgetup(a, id.clone()), $rget(b, id.clone())),
                        (a, _)       => vec!($rgetup(a, id.clone())),
                    }
                }).unwrap_or_else(|e| vec!(ResponseError(e, id.clone()))),
            )*
            RequestError(args, id) => vec!(ResponseError(args, id)),
        };
    }
}

fn jmap_handler(mut req: Request) -> StatusBody {

    match Json::from_reader(&mut req) {
        Ok(j) => match RequestBatch::from_json(&j) {
            Ok(b) => {
                let mut rbatch: ResponseBatch = ResponseBatch::default();

                let r = RequestContext {
                    userid: 1, // XXX get userid from auth
                    db: Db::open().unwrap(),
                };

                for method in b.0.into_iter() {
                    let rmethods: Vec<ResponseMethod>;
                    make_crud_method_dispatcher!(method, rmethods, r,
                        Calendar {
                            GetCalendars       => Calendars,
                            SetCalendars       => CalendarsSet,
                            GetCalendarUpdates => CalendarUpdates
                        },
                        CalendarEvent {
                            GetCalendarEvents       => CalendarEvents,
                            SetCalendarEvents       => CalendarEventsSet,
                            GetCalendarEventUpdates => CalendarEventUpdates
                        },
                        Contact {
                            GetContacts       => Contacts,
                            SetContacts       => ContactsSet,
                            GetContactUpdates => ContactUpdates
                        },
                        ContactGroup {
                            GetContactGroups       => ContactGroups,
                            SetContactGroups       => ContactGroupsSet,
                            GetContactGroupUpdates => ContactGroupUpdates
                        },
                        Mailbox {
                            GetMailboxes      => Mailboxes,
                            SetMailboxes      => MailboxesSet,
                            GetMailboxUpdates => MailboxUpdates
                        }
                    );

                    rbatch.0.extend(rmethods.into_iter());
                }

                StatusBody::new(StatusCode::Ok, Some(rbatch.to_json().to_string().into_bytes()))
            },
            Err(e) =>
                StatusBody::new(StatusCode::BadRequest, Some(e.to_string().into_bytes())),
        },
        Err(e) => StatusBody::new(StatusCode::BadRequest, Some(e.to_string().into_bytes())),
    }
}

fn file_handler(path: &String) -> String {
    let mut f = File::open(String::from("client")+path).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    s
}

fn finish_response(mut res: Response, out: StatusBody) {
    *res.status_mut() = out.code;

    match res.start()
        .and_then(|mut res|
                  match out.body {
                      Some(ref b) => {
                          try!(res.write_all(b));
                          Ok(res)
                      }
                      None => {
                          Ok(res)
                      }
                  })
        .and_then(|res| res.end()) {
            Err(e) => error!("response error: {}", e),
            _      => (),
        };
}

fn http_handler(req: Request, mut res: Response) {
    res.headers_mut().set(header::Server("salada/0.0.5".to_string()));

    let method = req.method.clone();
    let uri = req.uri.clone();

    match (method, uri) {
        (Post, AbsolutePath(ref path)) if path == "/jmap" =>
            finish_response(res, jmap_handler(req)),

        (_, AbsolutePath(ref path)) if path == "/jmap" =>
            finish_response(res, StatusBody::new(StatusCode::MethodNotAllowed, None)),

        (Get, AbsolutePath(ref path)) => {
            finish_response(res, StatusBody::new(StatusCode::Ok, Some(file_handler(path).into_bytes())))
        },

        _ => finish_response(res, StatusBody::new(StatusCode::NotFound, None)),
    };
}

fn main() {
    logger::init().unwrap();

    info!("Listening on http://127.0.0.1:3000/jmap");
    hyper::Server::http("127.0.0.1:3000").unwrap().handle(http_handler).unwrap();
}
<|endoftext|>


<commit_before>use std::{ mem, slice, string };
use handle::Owned;
use raw;
use raw::{ IV, UV, NV };
use convert::{ IntoSV, FromSV };

pub struct SV(Owned<raw::SV>);

impl SV {
    method! { simple fn iv() -> IV = sv_iv() }
    method! { simple fn uv() -> UV = sv_uv() }
    method! { simple fn nv() -> NV = sv_nv() }
    method! { simple fn utf8() -> bool = sv_utf8() != 0 }

    pub fn pv(&self) -> Vec<u8> {
        unsafe {
            let mut len = 0;
            let ptr = self.pthx().sv_pv(self.as_ptr(), &mut len);
            slice::from_raw_parts(ptr as *const u8, len as usize).to_owned()
        }
    }

    pub fn str(&self) -> Result<String, string::FromUtf8Error> {
        String::from_utf8(self.pv())
    }

    pub fn into_raw(self) -> *mut raw::SV {
        let raw = self.0.as_ptr();
        mem::forget(self);
        raw
    }

    pub unsafe fn from_raw_owned(pthx: raw::Interpreter, raw: *mut raw::SV) -> SV {
        SV(Owned::from_raw_owned(pthx, raw))
    }

    pub unsafe fn from_raw_borrowed(pthx: raw::Interpreter, raw: *mut raw::SV) -> SV {
        SV(Owned::from_raw_borrowed(pthx, raw))
    }

    fn pthx(&self) -> raw::Interpreter { self.0.pthx() }

    fn as_ptr(&self) -> *mut raw::SV { self.0.as_ptr() }
}

impl FromSV for IV {
    unsafe fn from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> IV {
        pthx.sv_iv(raw)
    }
}

impl FromSV for SV {
    unsafe fn from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> SV {
        SV::from_raw_borrowed(pthx, raw)
    }
}

impl IntoSV for IV {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe { SV::from_raw_owned(pthx, pthx.new_sv_iv(self)) }
    }
}

impl IntoSV for UV {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe { SV::from_raw_owned(pthx, pthx.new_sv_uv(self)) }
    }
}

impl IntoSV for NV {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe { SV::from_raw_owned(pthx, pthx.new_sv_nv(self)) }
    }
}

impl<'a> IntoSV for &'a str {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe {
            let svp = pthx.new_sv_pvn(self.as_ptr() as *const i8,
                                      self.len() as raw::STRLEN,
                                      raw::SVf_UTF8 as raw::U32);
            SV::from_raw_owned(pthx, svp)
        }
    }
}
<commit_msg>Document SV methods.<commit_after>use std::{ mem, slice, string };
use handle::Owned;
use raw;
use raw::{ IV, UV, NV };
use convert::{ IntoSV, FromSV };

pub struct SV(Owned<raw::SV>);

impl SV {
    method! {
        /// Coerce the given SV to an integer and return it.
        ///
        /// Perl macro: [`SvIV`](http://perldoc.perl.org/perlapi.html#SvIV).
        simple fn iv() -> IV = sv_iv()
    }
    method! {
        /// Coerce the given SV to an unsigned integer and return it.
        ///
        /// Perl macro: [`SvUV`](http://perldoc.perl.org/perlapi.html#SvUV).
        simple fn uv() -> UV = sv_uv()
    }
    method! {
        /// Coerce the given SV to a floating point value and return it.
        ///
        /// Perl macro: [`SvNV`](http://perldoc.perl.org/perlapi.html#SvNV).
        simple fn nv() -> NV = sv_nv()
    }
    method! {
        /// Return UTF8 flag on the SV.
        ///
        /// You should use this after a call to `pv()` or `str()`, in case any call to string
        /// overloading updates the internal flag.
        ///
        /// Perl macro: [`SvUTF8`](http://perldoc.perl.org/perlapi.html#SvUTF8).
        simple fn utf8() -> bool = sv_utf8() != 0
    }

    /// Return a copy of string in the SV as vector of bytes.
    ///
    /// Perl macro: [`SvPV`](http://perldoc.perl.org/perlapi.html#SvPV).
    pub fn pv(&self) -> Vec<u8> {
        unsafe {
            let mut len = 0;
            let ptr = self.pthx().sv_pv(self.as_ptr(), &mut len);
            slice::from_raw_parts(ptr as *const u8, len as usize).to_owned()
        }
    }

    /// Return a copy of string in the SV.
    ///
    /// Perl macro: [`SvPV`](http://perldoc.perl.org/perlapi.html#SvPV).
    pub fn str(&self) -> Result<String, string::FromUtf8Error> {
        String::from_utf8(self.pv())
    }

    /// Consume SV and convert into raw pointer.
    ///
    /// Does not decrement reference count. Returned pointer must be correctly disposed of to avoid
    /// memory leaks.
    pub fn into_raw(self) -> *mut raw::SV {
        let raw = self.0.as_ptr();
        mem::forget(self);
        raw
    }

    /// Construct new instance from a raw SV pointer without incrementing reference counter.
    ///
    /// Owned SV pointers are returned by assorted
    /// [`newSV`](http://perldoc.perl.org/perlapi.html#newSV) functions.
    pub unsafe fn from_raw_owned(pthx: raw::Interpreter, raw: *mut raw::SV) -> SV {
        SV(Owned::from_raw_owned(pthx, raw))
    }

    /// Construct new instance from a raw SV pointer and increment reference counter.
    ///
    /// Borrowed SV pointers exist on stack and are returned by functions like
    /// ['av_fetch`](http://perldoc.perl.org/perlapi.html#av_fetch) or
    /// ['hv_fetch'](http://perldoc.perl.org/perlapi.html#av_fetch).
    pub unsafe fn from_raw_borrowed(pthx: raw::Interpreter, raw: *mut raw::SV) -> SV {
        SV(Owned::from_raw_borrowed(pthx, raw))
    }

    fn pthx(&self) -> raw::Interpreter { self.0.pthx() }

    fn as_ptr(&self) -> *mut raw::SV { self.0.as_ptr() }
}

impl FromSV for IV {
    unsafe fn from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> IV {
        pthx.sv_iv(raw)
    }
}

impl FromSV for SV {
    unsafe fn from_sv(pthx: raw::Interpreter, raw: *mut raw::SV) -> SV {
        SV::from_raw_borrowed(pthx, raw)
    }
}

impl IntoSV for IV {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe { SV::from_raw_owned(pthx, pthx.new_sv_iv(self)) }
    }
}

impl IntoSV for UV {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe { SV::from_raw_owned(pthx, pthx.new_sv_uv(self)) }
    }
}

impl IntoSV for NV {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe { SV::from_raw_owned(pthx, pthx.new_sv_nv(self)) }
    }
}

impl<'a> IntoSV for &'a str {
    fn into_sv(self, pthx: raw::Interpreter) -> SV {
        unsafe {
            let svp = pthx.new_sv_pvn(self.as_ptr() as *const i8,
                                      self.len() as raw::STRLEN,
                                      raw::SVf_UTF8 as raw::U32);
            SV::from_raw_owned(pthx, svp)
        }
    }
}
<|endoftext|>


<commit_before>use nom::{alphanumeric, multispace, space};
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;

use caseless_tag::*;
use parser::ConditionExpression;
use parser::{fieldlist, unsigned_number, statement_terminator};

use condition::*;

#[derive(Clone, Debug, PartialEq)]
pub struct GroupByClause {
    columns: Vec<String>,
    having: String, // XXX(malte): should this be an arbitrary expr?
}

#[derive(Clone, Debug, PartialEq)]
pub struct LimitClause {
    limit: u64,
    offset: u64,
}

#[derive(Clone, Debug, PartialEq)]
enum OrderType {
    //   OrderAscending,
    //   OrderDescending,
}

#[derive(Clone, Debug, PartialEq)]
pub struct OrderClause {
    order_type: OrderType,
    order_cols: Vec<String>, // TODO(malte): can this be an arbitrary expr?
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct SelectStatement {
    table: String,
    distinct: bool,
    fields: Vec<String>,
    where_clause: Option<ConditionExpression>,
    group_by: Option<GroupByClause>,
    order: Option<OrderClause>,
    limit: Option<LimitClause>,
}

/// Parse LIMIT clause
named!(limit_clause<&[u8], LimitClause>,
    chain!(
        space ~
        caseless_tag!("limit") ~
        space ~
        limit_val: unsigned_number ~
        space? ~
        offset_val: opt!(chain!(
                caseless_tag!("offset") ~
                space ~
                val: unsigned_number,
                || { val }
            )
        ),
    || {
        LimitClause {
            limit: limit_val,
            offset: match offset_val {
                None => 0,
                Some(v) => v,
            },
        }
    })
);

/// Parse WHERE clause of a selection
named!(where_clause<&[u8], ConditionExpression>,
    dbg_dmp!(chain!(
        space ~
        caseless_tag!("where") ~
        space ~
        cond: condition_expr,
        || { cond }
    ))
);

named!(table_reference<&[u8], &str>,
    chain!(
        table: map_res!(alphanumeric, str::from_utf8) ~
        opt!(
            chain!(
                space ~
                caseless_tag!("as") ~
                space ~
                alias: map_res!(alphanumeric, str::from_utf8),
                || { println!("got alias: {} -> {}", table, alias); alias }
            )
        ),
        || { table }
    )
);

/// Parse rule for a SQL selection query.
/// TODO(malte): support nested queries as selection targets
named!(pub selection<&[u8], SelectStatement>,
    dbg_dmp!(chain!(
        caseless_tag!("select") ~
        space ~
        distinct: opt!(caseless_tag!("distinct")) ~
        space? ~
        fields: fieldlist ~
        delimited!(multispace, caseless_tag!("from"), multispace) ~
        table: table_reference ~
        cond: opt!(where_clause) ~
        limit: opt!(limit_clause) ~
        statement_terminator,
        || {
            SelectStatement {
                table: String::from(table),
                distinct: distinct.is_some(),
                fields: fields.iter().map(|s| String::from(*s)).collect(),
                where_clause: cond,
                group_by: None,
                order: None,
                limit: limit,
            }
        }
    ))
);

mod tests {
    use super::*;
    use parser::{ConditionBase, ConditionExpression, ConditionTree};

    #[test]
    fn simple_select() {
        let qstring = "SELECT id, name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn select_all() {
        let qstring = "SELECT * FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["ALL".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn spaces_optional() {
        let qstring = "SELECT id,name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn case_sensitivity() {
        let qstring_lc = "select id, name from users;";
        let qstring_uc = "SELECT id, name FROM users;";

        assert_eq!(selection(qstring_lc.as_bytes()).unwrap(),
                   selection(qstring_uc.as_bytes()).unwrap());
    }

    #[test]
    fn termination() {
        let qstring_sem = "select id, name from users;";
        let qstring_linebreak = "select id, name from users\n";

        assert_eq!(selection(qstring_sem.as_bytes()).unwrap(),
                   selection(qstring_linebreak.as_bytes()).unwrap());
    }

    #[test]
    fn where_clause() {
        let qstring = "select * from ContactInfo where email=?;";

        let res = selection(qstring.as_bytes());

        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("email"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("ContactInfo"),
                       fields: vec!["ALL".into()],
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn limit_clause() {
        let qstring1 = "select * from users limit 10\n";
        let qstring2 = "select * from users limit 10 offset 10\n";

        let expected_lim1 = LimitClause {
            limit: 10,
            offset: 0,
        };
        let expected_lim2 = LimitClause {
            limit: 10,
            offset: 10,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        assert_eq!(res1.unwrap().1.limit, Some(expected_lim1));
        assert_eq!(res2.unwrap().1.limit, Some(expected_lim2));
    }

    #[test]
    fn table_alias() {
        let qstring1 = "select * from PaperTag as t;";
        // let qstring2 = "select * from PaperTag t;";

        let res1 = selection(qstring1.as_bytes());
        assert_eq!(res1.clone().unwrap().1,
                   SelectStatement {
                       table: String::from("PaperTag"),
                       fields: vec!["ALL".into()],
                       ..Default::default()
                   });
        // let res2 = selection(qstring2.as_bytes());
        // assert_eq!(res1.unwrap().1, res2.unwrap().1);
    }

    #[test]
    fn distinct() {
        let qstring = "select distinct tag from PaperTag where paperId=?;";

        let res = selection(qstring.as_bytes());
        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("PaperTag"),
                       distinct: true,
                       fields: vec!["tag".into()],
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn simple_condition_expr() {
        let qstring = "select infoJson from PaperStorage where paperId=? and paperStorageId=?;";

        let res = selection(qstring.as_bytes());

        let left_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        })));
        let right_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("paperStorageId"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        })));
        let expected_where_cond = Some(ConditionExpression::LogicalOp(ConditionTree {
            left: left_comp,
            right: right_comp,
            operator: String::from("and"),
        }));
        println!("res: {:#?}", res);
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("PaperStorage"),
                       fields: vec!["infoJson".into()],
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }
}
<commit_msg>Config-gate tests correctly<commit_after>use nom::{alphanumeric, multispace, space};
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;

use caseless_tag::*;
use parser::ConditionExpression;
use parser::{fieldlist, unsigned_number, statement_terminator};

use condition::*;

#[derive(Clone, Debug, PartialEq)]
pub struct GroupByClause {
    columns: Vec<String>,
    having: String, // XXX(malte): should this be an arbitrary expr?
}

#[derive(Clone, Debug, PartialEq)]
pub struct LimitClause {
    limit: u64,
    offset: u64,
}

#[derive(Clone, Debug, PartialEq)]
enum OrderType {
    //   OrderAscending,
    //   OrderDescending,
}

#[derive(Clone, Debug, PartialEq)]
pub struct OrderClause {
    order_type: OrderType,
    order_cols: Vec<String>, // TODO(malte): can this be an arbitrary expr?
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct SelectStatement {
    table: String,
    distinct: bool,
    fields: Vec<String>,
    where_clause: Option<ConditionExpression>,
    group_by: Option<GroupByClause>,
    order: Option<OrderClause>,
    limit: Option<LimitClause>,
}

/// Parse LIMIT clause
named!(limit_clause<&[u8], LimitClause>,
    chain!(
        space ~
        caseless_tag!("limit") ~
        space ~
        limit_val: unsigned_number ~
        space? ~
        offset_val: opt!(chain!(
                caseless_tag!("offset") ~
                space ~
                val: unsigned_number,
                || { val }
            )
        ),
    || {
        LimitClause {
            limit: limit_val,
            offset: match offset_val {
                None => 0,
                Some(v) => v,
            },
        }
    })
);

/// Parse WHERE clause of a selection
named!(where_clause<&[u8], ConditionExpression>,
    dbg_dmp!(chain!(
        space ~
        caseless_tag!("where") ~
        space ~
        cond: condition_expr,
        || { cond }
    ))
);

named!(table_reference<&[u8], &str>,
    chain!(
        table: map_res!(alphanumeric, str::from_utf8) ~
        opt!(
            chain!(
                space ~
                caseless_tag!("as") ~
                space ~
                alias: map_res!(alphanumeric, str::from_utf8),
                || { println!("got alias: {} -> {}", table, alias); alias }
            )
        ),
        || { table }
    )
);

/// Parse rule for a SQL selection query.
/// TODO(malte): support nested queries as selection targets
named!(pub selection<&[u8], SelectStatement>,
    dbg_dmp!(chain!(
        caseless_tag!("select") ~
        space ~
        distinct: opt!(caseless_tag!("distinct")) ~
        space? ~
        fields: fieldlist ~
        delimited!(multispace, caseless_tag!("from"), multispace) ~
        table: table_reference ~
        cond: opt!(where_clause) ~
        limit: opt!(limit_clause) ~
        statement_terminator,
        || {
            SelectStatement {
                table: String::from(table),
                distinct: distinct.is_some(),
                fields: fields.iter().map(|s| String::from(*s)).collect(),
                where_clause: cond,
                group_by: None,
                order: None,
                limit: limit,
            }
        }
    ))
);

#[cfg(test)]
mod tests {
    use super::*;
    use parser::{ConditionBase, ConditionExpression, ConditionTree};

    #[test]
    fn simple_select() {
        let qstring = "SELECT id, name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn select_all() {
        let qstring = "SELECT * FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["ALL".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn spaces_optional() {
        let qstring = "SELECT id,name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("users"),
                       fields: vec!["id".into(), "name".into()],
                       ..Default::default()
                   });
    }

    #[test]
    fn case_sensitivity() {
        let qstring_lc = "select id, name from users;";
        let qstring_uc = "SELECT id, name FROM users;";

        assert_eq!(selection(qstring_lc.as_bytes()).unwrap(),
                   selection(qstring_uc.as_bytes()).unwrap());
    }

    #[test]
    fn termination() {
        let qstring_sem = "select id, name from users;";
        let qstring_linebreak = "select id, name from users\n";

        assert_eq!(selection(qstring_sem.as_bytes()).unwrap(),
                   selection(qstring_linebreak.as_bytes()).unwrap());
    }

    #[test]
    fn where_clause() {
        let qstring = "select * from ContactInfo where email=?;";

        let res = selection(qstring.as_bytes());

        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("email"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("ContactInfo"),
                       fields: vec!["ALL".into()],
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn limit_clause() {
        let qstring1 = "select * from users limit 10\n";
        let qstring2 = "select * from users limit 10 offset 10\n";

        let expected_lim1 = LimitClause {
            limit: 10,
            offset: 0,
        };
        let expected_lim2 = LimitClause {
            limit: 10,
            offset: 10,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        assert_eq!(res1.unwrap().1.limit, Some(expected_lim1));
        assert_eq!(res2.unwrap().1.limit, Some(expected_lim2));
    }

    #[test]
    fn table_alias() {
        let qstring1 = "select * from PaperTag as t;";
        // let qstring2 = "select * from PaperTag t;";

        let res1 = selection(qstring1.as_bytes());
        assert_eq!(res1.clone().unwrap().1,
                   SelectStatement {
                       table: String::from("PaperTag"),
                       fields: vec!["ALL".into()],
                       ..Default::default()
                   });
        // let res2 = selection(qstring2.as_bytes());
        // assert_eq!(res1.unwrap().1, res2.unwrap().1);
    }

    #[test]
    fn distinct() {
        let qstring = "select distinct tag from PaperTag where paperId=?;";

        let res = selection(qstring.as_bytes());
        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("PaperTag"),
                       distinct: true,
                       fields: vec!["tag".into()],
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn simple_condition_expr() {
        let qstring = "select infoJson from PaperStorage where paperId=? and paperStorageId=?;";

        let res = selection(qstring.as_bytes());

        let left_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        })));
        let right_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Expr(ConditionBase::Field(String::from("paperStorageId"))))),
            right: Some(Box::new(ConditionExpression::Expr(ConditionBase::Placeholder))),
            operator: String::from("="),
        })));
        let expected_where_cond = Some(ConditionExpression::LogicalOp(ConditionTree {
            left: left_comp,
            right: right_comp,
            operator: String::from("and"),
        }));
        println!("res: {:#?}", res);
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       table: String::from("PaperStorage"),
                       fields: vec!["infoJson".into()],
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }
}
<|endoftext|>


<commit_before>use message;

pub struct Server {
    id: i32,
}

static CONNACK_OK : [u8; 4] = [32, 2, 0, 0];
static PING_RESP : [u8; 2] = [0xd0, 0];

impl Server {
    pub fn new() -> Self {
        Server { id: 4 }
    }

    pub fn new_message<C: Client>(&mut self, client: &mut C, bytes: &[u8]) {
        let message_type = message::message_type(bytes);
        match message_type {
            message::MqttType::Connect => {
                client.send(&CONNACK_OK);
            },
            message::MqttType::PingReq => {
                client.send(&PING_RESP);
            },
            _ => panic!("Unknown message type")
        }
    }

    pub fn new_bytes<C: Client>(&mut self, client: &mut C, bytes: &[u8]) {
        let mut slice = bytes;

        while slice.len() > 0 {
            let remaining_len = message::remaining_length(bytes);
            let total_len = remaining_len + 2;

            if slice.len() >= total_len {
                let msg = &slice[0 .. total_len];
                slice = &slice[total_len ..];
                self.new_message(client, msg);
            } else {
                slice = &[];
            }
        }
    }
}

struct Stream {
    buffer: Vec<u8>,
    bytes_start: usize,
}

impl Stream {
    pub fn new() -> Self {
        Stream { buffer: vec![0; 1024], bytes_start: 0 }
    }

    pub fn buffer(&mut self) -> &mut Vec<u8> {
        &mut self.buffer
    }

    pub fn handle_messages<C: Client>(&mut self, bytes_read: usize, server: &mut Server, client: &mut C) {
        let mut slice = &self.buffer[self.bytes_start .. self.bytes_start + bytes_read];
        let mut total_len = 2;
        while slice.len() >= total_len {
            total_len = message::remaining_length(slice) + 2; //2 for fixed header size
            let msg = &slice[0 .. total_len];
            slice = &slice[total_len..];
            server.new_message(client, msg);
        }
    }
}

pub trait Client {
    fn send(&mut self, bytes: &[u8]);
}

struct TestClient {
    msgs: Vec<Vec<u8>>,
}

impl TestClient {
    fn new() -> Self {
        TestClient { msgs: vec!() }
    }

    fn last_msg(&self) -> &[u8] {
        self.msgs.last().unwrap()
    }

    fn read(&self, buffer: &mut [u8], bytes: &[u8]) -> usize {
        for i in 0..bytes.len() {
            buffer[i] = bytes[i];
        }
        bytes.len()
    }
}

impl Client for TestClient {
    fn send(&mut self, bytes: &[u8]) {
        self.msgs.push(bytes.to_vec());
    }
}

#[test]
fn test_connect() {
    let connect_bytes = &[
        0x10u8, 0x2a, // fixed header
        0x00, 0x06, 'M' as u8, 'Q' as u8, 'I' as u8, 's' as u8, 'd' as u8, 'p' as u8,
        0x03, // protocol version
        0xcc, // connection flags 1100111x user, pw, !wr, w(01), w, !c, x
        0x00, 0x0a, // keepalive of 100
        0x00, 0x03, 'c' as u8, 'i' as u8, 'd' as u8, // client ID
        0x00, 0x04, 'w' as u8, 'i' as u8, 'l' as u8, 'l' as u8, // will topic
        0x00, 0x04, 'w' as u8, 'm' as u8, 's' as u8, 'g' as u8, // will msg
        0x00, 0x07, 'g' as u8, 'l' as u8, 'i' as u8, 'f' as u8, 't' as u8, 'e' as u8, 'l' as u8, // username
        0x00, 0x02, 'p' as u8, 'w' as u8, // password
        ][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();

    server.new_message(&mut client, connect_bytes);
    assert_eq!(client.last_msg(), &CONNACK_OK);
}

#[test]
fn test_ping() {
    let ping_bytes =  &[0xc0u8, 0][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();

    server.new_message(&mut client, ping_bytes);

    assert_eq!(client.last_msg(), &PING_RESP);
}

#[test]
fn test_pings_all_at_once() {
    let ping_bytes = &[0xc0u8, 0, 0xc0, 0, 0xc0, 0, 0xc0, 0][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();
    let mut stream = Stream::new();

    let bytes_read = client.read(stream.buffer(), ping_bytes);
    stream.handle_messages(bytes_read, &mut server, &mut client);

    assert_eq!(client.msgs.len(), 4);
    for msg in client.msgs {
        assert_eq!(msg, &PING_RESP);
    }
}

#[test]
fn test_pings_multiple_time_unbroken() {
    let ping_bytes = &[0xc0u8, 0, 0xc0, 0][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();
    let mut stream = Stream::new();

    let bytes_read = client.read(stream.buffer(), ping_bytes);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 2);

    let bytes_read = client.read(stream.buffer(), ping_bytes);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 4);

    for msg in client.msgs {
        assert_eq!(msg, &PING_RESP);
    }
}
<commit_msg>Failing UT for MQTT messages that are broken up<commit_after>use message;

pub struct Server {
    id: i32,
}

static CONNACK_OK : [u8; 4] = [32, 2, 0, 0];
static PING_RESP : [u8; 2] = [0xd0, 0];

impl Server {
    pub fn new() -> Self {
        Server { id: 4 }
    }

    pub fn new_message<C: Client>(&mut self, client: &mut C, bytes: &[u8]) {
        let message_type = message::message_type(bytes);
        match message_type {
            message::MqttType::Connect => {
                client.send(&CONNACK_OK);
            },
            message::MqttType::PingReq => {
                client.send(&PING_RESP);
            },
            _ => panic!("Unknown message type")
        }
    }

    pub fn new_bytes<C: Client>(&mut self, client: &mut C, bytes: &[u8]) {
        let mut slice = bytes;

        while slice.len() > 0 {
            let remaining_len = message::remaining_length(bytes);
            let total_len = remaining_len + 2;

            if slice.len() >= total_len {
                let msg = &slice[0 .. total_len];
                slice = &slice[total_len ..];
                self.new_message(client, msg);
            } else {
                slice = &[];
            }
        }
    }
}

struct Stream {
    buffer: Vec<u8>,
    bytes_start: usize,
}

impl Stream {
    pub fn new() -> Self {
        Stream { buffer: vec![0; 1024], bytes_start: 0 }
    }

    pub fn buffer(&mut self) -> &mut Vec<u8> {
        &mut self.buffer
    }

    pub fn handle_messages<C: Client>(&mut self, bytes_read: usize, server: &mut Server, client: &mut C) {
        let mut slice = &self.buffer[self.bytes_start .. self.bytes_start + bytes_read];
        let mut total_len = 2;
        while slice.len() >= total_len {
            total_len = message::remaining_length(slice) + 2; //2 for fixed header size
            let msg = &slice[0 .. total_len];
            slice = &slice[total_len..];
            server.new_message(client, msg);
        }
    }
}

pub trait Client {
    fn send(&mut self, bytes: &[u8]);
}

struct TestClient {
    msgs: Vec<Vec<u8>>,
}

impl TestClient {
    fn new() -> Self {
        TestClient { msgs: vec!() }
    }

    fn last_msg(&self) -> &[u8] {
        self.msgs.last().unwrap()
    }

    fn read(&self, buffer: &mut [u8], bytes: &[u8]) -> usize {
        for i in 0..bytes.len() {
            buffer[i] = bytes[i];
        }
        bytes.len()
    }
}

impl Client for TestClient {
    fn send(&mut self, bytes: &[u8]) {
        self.msgs.push(bytes.to_vec());
    }
}

#[test]
fn test_connect() {
    let connect_bytes = &[
        0x10u8, 0x2a, // fixed header
        0x00, 0x06, 'M' as u8, 'Q' as u8, 'I' as u8, 's' as u8, 'd' as u8, 'p' as u8,
        0x03, // protocol version
        0xcc, // connection flags 1100111x user, pw, !wr, w(01), w, !c, x
        0x00, 0x0a, // keepalive of 100
        0x00, 0x03, 'c' as u8, 'i' as u8, 'd' as u8, // client ID
        0x00, 0x04, 'w' as u8, 'i' as u8, 'l' as u8, 'l' as u8, // will topic
        0x00, 0x04, 'w' as u8, 'm' as u8, 's' as u8, 'g' as u8, // will msg
        0x00, 0x07, 'g' as u8, 'l' as u8, 'i' as u8, 'f' as u8, 't' as u8, 'e' as u8, 'l' as u8, // username
        0x00, 0x02, 'p' as u8, 'w' as u8, // password
        ][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();

    server.new_message(&mut client, connect_bytes);
    assert_eq!(client.last_msg(), &CONNACK_OK);
}

#[test]
fn test_ping() {
    let ping_bytes =  &[0xc0u8, 0][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();

    server.new_message(&mut client, ping_bytes);

    assert_eq!(client.last_msg(), &PING_RESP);
}

#[test]
fn test_pings_all_at_once() {
    let ping_bytes = &[0xc0u8, 0, 0xc0, 0, 0xc0, 0, 0xc0, 0][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();
    let mut stream = Stream::new();

    let bytes_read = client.read(stream.buffer(), ping_bytes);
    stream.handle_messages(bytes_read, &mut server, &mut client);

    assert_eq!(client.msgs.len(), 4);
    for msg in client.msgs {
        assert_eq!(msg, &PING_RESP);
    }
}

#[test]
fn test_pings_multiple_time_unbroken() {
    let ping_bytes = &[0xc0u8, 0, 0xc0, 0][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();
    let mut stream = Stream::new();

    let bytes_read = client.read(stream.buffer(), ping_bytes);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 2);

    let bytes_read = client.read(stream.buffer(), ping_bytes);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 4);

    for msg in client.msgs {
        assert_eq!(msg, &PING_RESP);
    }
}

#[test]
fn test_pings_broken() {
    let ping_fst = &[0xc0u8][0..];
    let ping_snd = &[0u8][0..];

    let mut server = Server::new();
    let mut client = TestClient::new();
    let mut stream = Stream::new();

    let bytes_read = client.read(stream.buffer(), ping_fst);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 0);

    let bytes_read = client.read(stream.buffer(), ping_snd);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 1);

    let bytes_read = client.read(stream.buffer(), ping_fst);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 1);

    let bytes_read = client.read(stream.buffer(), ping_snd);
    stream.handle_messages(bytes_read, &mut server, &mut client);
    assert_eq!(client.msgs.len(), 2);

    for msg in client.msgs {
        assert_eq!(msg, &PING_RESP);
    }
}
<|endoftext|>


<commit_before>use grain_capnp::{PowerboxCapability, UiView, UiSession};
use web_session_capnp::{WebSession};

use collections::hashmap::HashMap;
use capnp::capability::{ClientHook, FromServer};
use capnp::AnyPointer;
use capnp_rpc::rpc::{RpcConnectionState, SturdyRefRestorer};
use capnp_rpc::capability::{LocalClient};

use sqlite3;

pub struct UiViewImpl;

impl PowerboxCapability::Server for UiViewImpl {
    fn get_powerbox_info(&mut self, context : PowerboxCapability::GetPowerboxInfoContext) {
        context.done()
    }
}

impl UiView::Server for UiViewImpl {
    fn get_view_info(&mut self, context : UiView::GetViewInfoContext) {
        context.done()
    }

    fn new_session(&mut self, mut context : UiView::NewSessionContext) {
        println!("asked for a new session!");
        let (_, results) = context.get();

        let client : WebSession::Client = FromServer::new(None::<LocalClient>, ~WebSessionImpl::new());
        // we need to do this dance to upcast.
        results.set_session(UiSession::Client { client : client.client});

        context.done()
    }
}

pub struct WebSessionImpl {
    db : sqlite3::Database,
}

impl WebSessionImpl {
    pub fn new() -> WebSessionImpl {
        WebSessionImpl {
            db : sqlite3::open("/var/data.db").unwrap(),
        }
    }
}

impl UiSession::Server for WebSessionImpl {

}

static main_css : &'static str =
    "body { font-family: Helvetica, Sans, Arial;
            font-size: medium;
             margin-left: auto;
             margin-right: auto;
             width: 600px;
             text-align: center;
     }
    .word {
        text-align: center;
        font-size: 500%;
     }
     ";

static header : &'static str =
  r#"<title> acronymy </title><link rel="stylesheet" type="text/css" href="main.css" >
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
  "#;

fn html_body(body :&str) -> ~str {
    format!("{}{}", header, body)
}

struct Path {
    path : ~str,
    query : HashMap<~str, ~str>,
}

impl Path {
    fn new() -> Path {
        Path { path : ~"", query : HashMap::new() }
    }
}

fn parse_path(path : &str) -> Path {
    let mut result = Path::new();

    let v : ~[&str] = path.splitn('?', 2).collect();
    if v.len() == 0 {
        return result;
    }
    result.path = v[0].into_owned();
    if v.len() == 1 {
        return result;
    }
    for attr in v[1].split('&') {
        let a : ~[&str] = attr.splitn('=', 2).collect();
        if a.len() == 2 {
            result.query.insert(a[0].into_owned(),
                                a[1].into_owned());
        }
    }
    return result;
}

impl WebSessionImpl {
    fn is_word(&mut self, word : &str) -> sqlite3::SqliteResult<bool> {
        let cursor = try!(self.db.prepare(
            format!("SELECT * FROM Words WHERE Word = \"{}\";", word),
            &None));
        println!("got the cursor");
        return Ok(try!(cursor.step_row()).is_some());
    }
}

impl WebSession::Server for WebSessionImpl {
    fn get(&mut self, mut context : WebSession::GetContext) {
        println!("GET");
        let (params, results) = context.get();
        let raw_path = params.get_path();
        let content = results.init_content();
        content.set_mime_type("text/html");

        let path = parse_path(raw_path);
        println!("path = {}", raw_path);
        println!("{}, {}", path.path, path.query);

        if raw_path == "main.css" {
            content.get_body().set_bytes(main_css.as_bytes())
        } else if path.path.as_slice() == "define" {
            let word : ~str = path.query.get(&~"word").clone();

            // TODO check that `word` is actually a word.
            match self.is_word(word) {
                Err(e) => fail!("is_word error: {}, {:?}", e, self.db.get_errmsg()),
                Ok(false) => {
                    content.get_body().set_bytes(
                        html_body(
                            "
 that's not a word

                           <form action=\"define\" method=\"get\">
                           <input name=\"word\"/><button>go</button></form>").as_bytes());
                    return context.done();
                }
                Ok(true) => {}
            }

            content.get_body().set_bytes(
                html_body(
                    format!(
                        "
{word}

                     <form action=\"define\" method=\"get\">
                     <input name=\"word\" value=\"{word}\" type=\"hidden\"/>
                     <input name=\"definition\"/><button>define</button></form>",
                        word=word)).as_bytes());

        } else {
            content.get_body().set_bytes(
                html_body(
                    "<form action=\"define\" method=\"get\">
                     <input name=\"word\"/><button>go</button></form>").as_bytes());
        }
        context.done()
    }
    fn post(&mut self, context : WebSession::PostContext) {
        println!("POST");
        context.done()
    }
    fn put(&mut self, context : WebSession::PutContext) {
        println!("PUT");
        context.done()
    }
    fn delete(&mut self, context : WebSession::DeleteContext) {
        println!("DELETE");
        context.done()
    }
    fn open_web_socket(&mut self, context : WebSession::OpenWebSocketContext) {
        println!("OPEN WEB SOCKET");
        context.done()
    }
}

pub struct FdStream {
    inner : ~::std::rt::rtio::RtioFileStream:Send,
}

impl FdStream {
    pub fn new(fd : ::libc::c_int) -> ::std::io::IoResult<FdStream> {
        ::std::rt::rtio::LocalIo::maybe_raise(|io| {
            Ok (FdStream { inner : io.fs_from_raw_fd(fd, ::std::rt::rtio::DontClose) })
        })
    }
}

impl Reader for FdStream {
    fn read(&mut self, buf : &mut [u8]) -> ::std::io::IoResult<uint> {
        self.inner.read(buf).map(|i| i as uint)
    }
}

impl Writer for FdStream {
    fn write(&mut self, buf : &[u8]) -> ::std::io::IoResult<()> {
        self.inner.write(buf)
    }
}

pub struct Restorer;

impl SturdyRefRestorer for Restorer {
    fn restore(&self, obj_id : AnyPointer::Reader) -> Option<~ClientHook:Send> {
        if obj_id.is_null() {
            let client : UiView::Client = FromServer::new(None::<LocalClient>, ~UiViewImpl);
            Some(client.client.hook)
        } else {
            None
        }
    }
}

pub fn main() -> ::std::io::IoResult<()> {

    let args = ::std::os::args();

    if args.len() == 4 && args[1].as_slice() == "--init" {
        println!("initializing...");
        let initdb_path = ::std::path::Path::new(args[2].as_slice());
        let proddb_path = ::std::path::Path::new(args[3].as_slice());
        println!("copying database from {} to {}", args[2], args[3]);
        try!(::std::io::fs::copy(&initdb_path, &proddb_path));
        println!("success!");
    }

    let ifs = try!(FdStream::new(3));
    let ofs = try!(FdStream::new(3));

    let connection_state = RpcConnectionState::new();
    connection_state.run(ifs, ofs, Restorer);

    Ok(())
}

<commit_msg>write to the database<commit_after>use grain_capnp::{PowerboxCapability, UiView, UiSession};
use web_session_capnp::{WebSession};

use collections::hashmap::HashMap;
use capnp::capability::{ClientHook, FromServer};
use capnp::AnyPointer;
use capnp_rpc::rpc::{RpcConnectionState, SturdyRefRestorer};
use capnp_rpc::capability::{LocalClient};

use sqlite3;

pub struct UiViewImpl;

impl PowerboxCapability::Server for UiViewImpl {
    fn get_powerbox_info(&mut self, context : PowerboxCapability::GetPowerboxInfoContext) {
        context.done()
    }
}

impl UiView::Server for UiViewImpl {
    fn get_view_info(&mut self, context : UiView::GetViewInfoContext) {
        context.done()
    }

    fn new_session(&mut self, mut context : UiView::NewSessionContext) {
        println!("asked for a new session!");
        let (_, results) = context.get();

        let client : WebSession::Client = FromServer::new(None::<LocalClient>, ~WebSessionImpl::new());
        // we need to do this dance to upcast.
        results.set_session(UiSession::Client { client : client.client});

        context.done()
    }
}

pub struct WebSessionImpl {
    db : sqlite3::Database,
}

impl WebSessionImpl {
    pub fn new() -> WebSessionImpl {
        WebSessionImpl {
            db : sqlite3::open("/var/data.db").unwrap(),
        }
    }
}

impl UiSession::Server for WebSessionImpl {

}

static main_css : &'static str =
    "body { font-family: Helvetica, Sans, Arial;
            font-size: medium;
             margin-left: auto;
             margin-right: auto;
             width: 600px;
             text-align: center;
     }
    .word {
        text-align: center;
        font-size: 500%;
     }
     ";

static header : &'static str =
  r#"<title> acronymy </title><link rel="stylesheet" type="text/css" href="main.css" >
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
  "#;

fn html_body(body :&str) -> ~str {
    format!("{}{}", header, body)
}

struct Path {
    path : ~str,
    query : HashMap<~str, ~str>,
}

impl Path {
    fn new() -> Path {
        Path { path : ~"", query : HashMap::new() }
    }
}

fn parse_path(path : &str) -> Path {
    let mut result = Path::new();

    let v : ~[&str] = path.splitn('?', 2).collect();
    if v.len() == 0 {
        return result;
    }
    result.path = v[0].into_owned();
    if v.len() == 1 {
        return result;
    }
    for attr in v[1].split('&') {
        let a : ~[&str] = attr.splitn('=', 2).collect();
        if a.len() == 2 {
            result.query.insert(a[0].into_owned(),
                                a[1].into_owned());
        }
    }
    return result;
}

impl WebSessionImpl {

    fn checked<T>(&self, result : sqlite3::SqliteResult<T>) -> T {
        match result {
            Err(e) => {fail!("database error: {}, {:?}", e, self.db.get_errmsg()) }
            Ok(v) => { return v; }
        }
    }

    fn is_word(&self, word : &str) -> sqlite3::SqliteResult<bool> {

        // TODO sanitize `word`
        let cursor = try!(self.db.prepare(
            format!("SELECT * FROM Words WHERE Word = \"{}\";", word),
            &None));
        println!("got the cursor");
        return Ok(try!(cursor.step_row()).is_some());
    }

    fn validate_def(&self, word : &str, definition : &[&str]) -> sqlite3::SqliteResult<bool> {

        if definition.len() != word.len() { return Ok(false); }

        let mut idx = 0;
        for &d in definition.iter() {
            if !(try!(self.is_word(d)) && d.len() > 0 && d[0] == word[idx]) {
                return Ok(false);
            }

            idx += 1;
        }

        return Ok(true);
    }

    fn write_def(&self, word : &str, definition : &[&str]) -> sqlite3::SqliteResult<()> {
        let mut query = StrBuf::new();
        query.push_str(format!("BEGIN; DELETE FROM Definitions WHERE Definee =\"{}\"; ", word));
        query.push_str("INSERT INTO Definitions(Definee, Idx, Definer) VALUES");
        let mut idx = 0;
        for &d in definition.iter() {
            if idx != 0 { query.push_str(","); }
            query.push_str(format!("(\"{}\", {}, \"{}\")", word, idx, d));
            idx += 1;
        }
        query.push_str("; COMMMIT;");

        println!("query: {}", query);

        try!(self.db.exec(query.as_slice()));

        return Ok(());
    }

    fn get_def(&self, word : &str) -> sqlite3::SqliteResult<~str> {

        let cursor = try!(self.db.prepare(
            format!("SELECT * FROM Definitions WHERE Definee = \"{}\";", word),
            &None));

        let mut map = HashMap::<int, ~str>::new();

        loop {
            match try!(cursor.step_row()) {
                None => break,
                Some(row) => {
                    let definer = match row.get(&~"Definer") { &sqlite3::Text(ref t) => t.clone(), _ => fail!(), };
                    let idx = match row.get(&~"Idx") { &sqlite3::Integer(ref i) => i.clone(), _ => fail!(), };

                    map.insert(idx, definer);
                }
            }
        }

        if map.len() != word.len() {
            return Ok(~"
this word has no definition yet
");
        } else {

            let mut result = StrBuf::new();
            result.push_str("
");
            for idx in range::<int>(0, word.len() as int) {
                let definer : &str = map.get(&idx).as_slice();
                result.push_str(format!(" {word} ", word=definer));
            }
            result.push_str("
");
            return Ok(result.into_owned());
        }
    }

}

impl WebSession::Server for WebSessionImpl {
    fn get(&mut self, mut context : WebSession::GetContext) {
        println!("GET");
        let (params, results) = context.get();
        let raw_path = params.get_path();
        let content = results.init_content();
        content.set_mime_type("text/html");

        let path = parse_path(raw_path);
        println!("path = {}", raw_path);

        if raw_path == "main.css" {
            content.get_body().set_bytes(main_css.as_bytes())
        } else if path.path.as_slice() == "define" {
            let word : ~str = path.query.get(&~"word").clone();

            if !self.checked(self.is_word(word)) {
                content.get_body().set_bytes(
                    html_body(
                        "
 that's not a word

                           <form action=\"define\" method=\"get\">
                           <input name=\"word\"/><button>go</button></form>").as_bytes());
                return context.done();
            } else {

            }

            match path.query.find(&~"definition") {
                None => {
                    let def_div = self.checked(self.get_def(word));

                    content.get_body().set_bytes(
                        html_body(
                            format!(
                                "
{word}

                                 {def}
                                 <form action=\"define\" method=\"get\">
                                 <input name=\"word\" value=\"{word}\" type=\"hidden\"/>
                                 <input name=\"definition\"/><button>define</button></form>",
                                word=word, def=def_div)).as_bytes());

                }
                Some(def_query) => {

                    let definition : ~[&str] = def_query.split('+').collect();

                    if self.checked(self.validate_def(word, definition)) {

                        self.write_def(word, definition);

                        let def_div = self.checked(self.get_def(word));

                        content.get_body().set_bytes(
                        html_body(
                            format!(
                                "
{word}

                                 {def}
                                 <form action=\"define\" method=\"get\">
                                 <input name=\"word\" value=\"{word}\" type=\"hidden\"/>
                                 <input name=\"definition\"/><button>define</button></form>",
                                word=word, def=def_div)).as_bytes());

                    } else {

                        let def_div = self.checked(self.get_def(word));

                        content.get_body().set_bytes(
                        html_body(
                            format!(
                                "
{word}

                                 {def}

invalid definition

                                 <form action=\"define\" method=\"get\">
                                 <input name=\"word\" value=\"{word}\" type=\"hidden\"/>
                                 <input name=\"definition\"/><button>define</button></form>",
                                word=word, def=def_div)).as_bytes());

                    }
                }
            }

        } else {
            content.get_body().set_bytes(
                html_body(
                    "<form action=\"define\" method=\"get\">
                     <input name=\"word\"/><button>go</button></form>").as_bytes());
        }
        context.done()
    }
    fn post(&mut self, context : WebSession::PostContext) {
        println!("POST");
        context.done()
    }
    fn put(&mut self, context : WebSession::PutContext) {
        println!("PUT");
        context.done()
    }
    fn delete(&mut self, context : WebSession::DeleteContext) {
        println!("DELETE");
        context.done()
    }
    fn open_web_socket(&mut self, context : WebSession::OpenWebSocketContext) {
        println!("OPEN WEB SOCKET");
        context.done()
    }
}

pub struct FdStream {
    inner : ~::std::rt::rtio::RtioFileStream:Send,
}

impl FdStream {
    pub fn new(fd : ::libc::c_int) -> ::std::io::IoResult<FdStream> {
        ::std::rt::rtio::LocalIo::maybe_raise(|io| {
            Ok (FdStream { inner : io.fs_from_raw_fd(fd, ::std::rt::rtio::DontClose) })
        })
    }
}

impl Reader for FdStream {
    fn read(&mut self, buf : &mut [u8]) -> ::std::io::IoResult<uint> {
        self.inner.read(buf).map(|i| i as uint)
    }
}

impl Writer for FdStream {
    fn write(&mut self, buf : &[u8]) -> ::std::io::IoResult<()> {
        self.inner.write(buf)
    }
}

pub struct Restorer;

impl SturdyRefRestorer for Restorer {
    fn restore(&self, obj_id : AnyPointer::Reader) -> Option<~ClientHook:Send> {
        if obj_id.is_null() {
            let client : UiView::Client = FromServer::new(None::<LocalClient>, ~UiViewImpl);
            Some(client.client.hook)
        } else {
            None
        }
    }
}

pub fn main() -> ::std::io::IoResult<()> {

    let args = ::std::os::args();

    if args.len() == 4 && args[1].as_slice() == "--init" {
        println!("initializing...");
        let initdb_path = ::std::path::Path::new(args[2].as_slice());
        let proddb_path = ::std::path::Path::new(args[3].as_slice());
        println!("copying database from {} to {}", args[2], args[3]);
        try!(::std::io::fs::copy(&initdb_path, &proddb_path));
        println!("success!");
    }

    let ifs = try!(FdStream::new(3));
    let ofs = try!(FdStream::new(3));

    let connection_state = RpcConnectionState::new();
    connection_state.run(ifs, ofs, Restorer);

    Ok(())
}

<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::str::FromStr;
use std::convert::From;
use time::Duration;

use error::{Error, ProtoError, ParseError};

#[derive(Debug, PartialEq, Clone)]
pub struct Status {
    pub volume: i8,
    pub repeat: bool,
    pub random: bool,
    pub single: bool,
    pub consume: bool,
    pub queue_version: usize,
    pub queue_len: usize,
    pub state: State,
    //song: Option<MpdQueuePlace>,
    //nextsong: Option<MpdQueuePlace>,
    //play_time: Option<Duration>,
    //total_time: Option<Duration>,
    //elapsed: Option<Duration>,
    //duration: Option<Duration>,
    pub bitrate: Option<usize>,
    pub crossfade: Option<u64>,
    pub mixrampdb: f32,
    pub mixrampdelay: Option<Duration>,
    pub audio: Option<AudioFormat>,
    pub updating_db: Option<usize>,
    pub error: Option<String>
}

impl Status {
    pub fn from_map(map: BTreeMap<String, String>) -> Result<Status, Error> {
        Ok(Status {
            volume: get_field!(map, "volume"),

            repeat: get_field!(map, bool "repeat"),
            random: get_field!(map, bool "random"),
            single: get_field!(map, bool "single"),
            consume: get_field!(map, bool "consume"),

            queue_version: get_field!(map, "playlist"),
            queue_len: get_field!(map, "playlistlength"),
            state: get_field!(map, "state"),
            //song: Option<MpdQueuePlace>,
            //nextsong: Option<MpdQueuePlace>,
            //play_time: Option<Duration>,
            //total_time: Option<Duration>,
            //elapsed: Option<Duration>,
            //duration: Option<Duration>,
            bitrate: get_field!(map, opt "bitrate"),
            crossfade: get_field!(map, opt "xfade"),
            mixrampdb: 0.0, //get_field!(map, "mixrampdb"),
            mixrampdelay: None, //get_field!(map, opt "mixrampdelay").map(|v: f64| Duration::milliseconds((v * 1000.0) as i64)),
            audio: get_field!(map, opt "audio"),
            updating_db: get_field!(map, opt "updating_db"),
            error: map.get("error").map(|v| v.to_owned()),
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct AudioFormat {
    pub rate: u16,
    pub bits: u8,
    pub chans: u8
}

impl FromStr for AudioFormat {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<AudioFormat, ParseError> {
        let mut it = s.split(':');
        Ok(AudioFormat {
            rate: try!(it.next().ok_or(ParseError::NoRate).and_then(|v| v.parse().map_err(ParseError::BadRate))),
            bits: try!(it.next().ok_or(ParseError::NoBits).and_then(|v| if &*v == "f" { Ok(0) } else { v.parse().map_err(ParseError::BadBits) })),
            chans: try!(it.next().ok_or(ParseError::NoChans).and_then(|v| v.parse().map_err(ParseError::BadChans))),
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum State {
    Stop,
    Play,
    Pause
}

impl FromStr for State {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<State, ParseError> {
        match s {
            "stop" => Ok(State::Stop),
            "play" => Ok(State::Play),
            "pause" => Ok(State::Pause),
            _ => Err(ParseError::BadState(s.to_owned())),
        }
    }
}
<commit_msg>update integer type usage<commit_after>use std::collections::BTreeMap;
use std::str::FromStr;
use std::convert::From;
use time::Duration;

use error::{Error, ProtoError, ParseError};

#[derive(Debug, PartialEq, Clone)]
pub struct Status {
    pub volume: i8,
    pub repeat: bool,
    pub random: bool,
    pub single: bool,
    pub consume: bool,
    pub queue_version: u32,
    pub queue_len: u32,
    pub state: State,
    //song: Option<MpdQueuePlace>,
    //nextsong: Option<MpdQueuePlace>,
    //play_time: Option<Duration>,
    //total_time: Option<Duration>,
    //elapsed: Option<Duration>,
    //duration: Option<Duration>,
    pub bitrate: Option<u32>,
    pub crossfade: Option<u64>,
    pub mixrampdb: f32,
    pub mixrampdelay: Option<Duration>,
    pub audio: Option<AudioFormat>,
    pub updating_db: Option<u32>,
    pub error: Option<String>
}

impl Status {
    pub fn from_map(map: BTreeMap<String, String>) -> Result<Status, Error> {
        Ok(Status {
            volume: get_field!(map, "volume"),

            repeat: get_field!(map, bool "repeat"),
            random: get_field!(map, bool "random"),
            single: get_field!(map, bool "single"),
            consume: get_field!(map, bool "consume"),

            queue_version: get_field!(map, "playlist"),
            queue_len: get_field!(map, "playlistlength"),
            state: get_field!(map, "state"),
            //song: Option<MpdQueuePlace>,
            //nextsong: Option<MpdQueuePlace>,
            //play_time: Option<Duration>,
            //total_time: Option<Duration>,
            //elapsed: Option<Duration>,
            //duration: Option<Duration>,
            bitrate: get_field!(map, opt "bitrate"),
            crossfade: get_field!(map, opt "xfade"),
            mixrampdb: 0.0, //get_field!(map, "mixrampdb"),
            mixrampdelay: None, //get_field!(map, opt "mixrampdelay").map(|v: f64| Duration::milliseconds((v * 1000.0) as i64)),
            audio: get_field!(map, opt "audio"),
            updating_db: get_field!(map, opt "updating_db"),
            error: map.get("error").map(|v| v.to_owned()),
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct AudioFormat {
    pub rate: u32,
    pub bits: u8,
    pub chans: u8
}

impl FromStr for AudioFormat {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<AudioFormat, ParseError> {
        let mut it = s.split(':');
        Ok(AudioFormat {
            rate: try!(it.next().ok_or(ParseError::NoRate).and_then(|v| v.parse().map_err(ParseError::BadRate))),
            bits: try!(it.next().ok_or(ParseError::NoBits).and_then(|v| if &*v == "f" { Ok(0) } else { v.parse().map_err(ParseError::BadBits) })),
            chans: try!(it.next().ok_or(ParseError::NoChans).and_then(|v| v.parse().map_err(ParseError::BadChans))),
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum State {
    Stop,
    Play,
    Pause
}

impl FromStr for State {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<State, ParseError> {
        match s {
            "stop" => Ok(State::Stop),
            "play" => Ok(State::Play),
            "pause" => Ok(State::Pause),
            _ => Err(ParseError::BadState(s.to_owned())),
        }
    }
}
<|endoftext|>