<commit_before>//! Write points to a `Write`.
//!
//! Simple writes can be done with a `Writer`, but if you need to configure your output file, use a
//! `Builder`.

use std::fs::File;
use std::io::{BufWriter, Seek, SeekFrom, Write};
use std::path::Path;

use {Error, Result};
use builder::Builder;
use header::{Header, WriteHeader};
use point::{Point, WritePoint};
use utils::{Bounds, Triple};
use vlr::{Vlr, WriteVlr};

/// Write LAS points to a `Write`.
///
/// This struct implements `Drop`, so the LAS data are finalized (the header is re-written) when
/// the `Writer` goes out of scope. This will panic if there is an error while closing the file, so
/// if you're worried about panics you will need to use `Writer::close` instead.
#[derive(Debug)]
pub struct Writer<W: Seek + Write> {
    bounds: Bounds<f64>,
    closed: bool,
    header: Header,
    point_count: u32,
    point_count_by_return: [u32; 5],
    vlrs: Vec<Vlr>,
    write: W,
}

impl Writer<BufWriter<File>> {
    /// Creates a default `Writer` that will write points out to a file at the path.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Writer;
    /// let writer = Writer::from_path("/dev/null").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Writer<BufWriter<File>>> {
        File::create(path).map_err(Error::from).and_then(|f| Writer::default(BufWriter::new(f)))
    }
}

impl<W: Seek + Write> Writer<W> {
    fn from(builder: &Builder, write: W) -> Writer<W> {
        Writer {
            bounds: Default::default(),
            closed: false,
            header: builder.into(),
            point_count: Default::default(),
            point_count_by_return: Default::default(),
            vlrs: builder.vlrs.clone(),
            write: write,
        }
    }

    /// Creates a new writer from a `Builder` and a `Write`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Writer;
    /// use std::io::Cursor;
    /// use las::Builder;
    /// let writer = Writer::new(&Builder::new(), Cursor::new(Vec::new())).unwrap();
    /// ```
    pub fn new(builder: &Builder, write: W) -> Result<Writer<W>> {
        let mut writer = Writer::from(builder, write);
        try!(writer.write_header());
        Ok(writer)
    }

    /// Creates a new default writer.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// # use las::Writer;
    /// let writer = Writer::default(Cursor::new(Vec::new())).unwrap();
    /// ```
    pub fn default(write: W) -> Result<Writer<W>> {
        Builder::new().writer(write)
    }

    /// Writes a point.
    ///
    /// # Examples
    ///
    ///
    /// ```
    /// use std::io::Cursor;
    /// # use las::Writer;
    /// let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
    /// writer.write(&Default::default()).unwrap();
    /// ```
    pub fn write(&mut self, point: &Point) -> Result<()> {
        if self.closed {
            return Err(Error::ClosedWriter);
        }
        try!(self.write.write_point(point,
                                    self.header.transforms,
                                    self.header.point_format,
                                    self.header.extra_bytes));
        self.point_count += 1;
        if point.return_number.is_valid() {
            self.point_count_by_return[u8::from(point.return_number) as usize - 1] += 1;
        }
        self.bounds.grow(Triple {
            x: point.x,
            y: point.y,
            z: point.z,
        });
        Ok(())
    }

    fn write_header(&mut self) -> Result<()> {
        // TODO test point count by return, offsets, etc
        self.header.point_count = self.point_count;
        self.header.bounds = self.bounds;
        try!(self.write.seek(SeekFrom::Start(0)));
        try!(self.write.write_header(self.header));
        for vlr in &self.vlrs {
            try!(self.write.write_vlr(vlr));
        }
        Ok(())
    }

    /// Closes this writer.
    ///
    /// After the writer is closed, and future calls to `write` will error.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// # use las::Writer;
    /// let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
    /// assert!(writer.write(&Default::default()).is_ok());
    /// writer.close().unwrap();
    /// assert!(writer.write(&Default::default()).is_err());
    /// ```
    pub fn close(&mut self) -> Result<()> {
        if self.closed {
            return Err(Error::ClosedWriter);
        }
        try!(self.write_header());
        self.closed = true;
        Ok(())
    }
}

impl<W: Seek + Write> Drop for Writer<W> {
    fn drop(&mut self) {
        if !self.closed {
            if let Err(err) = self.close() {
                error!("Error while dropping writer: {}", err);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::io::Cursor;

    use builder::Builder;
    use point::{Classification, Color, NumberOfReturns, Point, ReturnNumber, ScanDirection};
    use reader::Reader;
    use utils::Bounds;
    use version::Version;

    fn point() -> Point {
        Point {
            x: 1.,
            y: 2.,
            z: 3.,
            intensity: 4,
            return_number: ReturnNumber::from(1),
            number_of_returns: NumberOfReturns::from(0b00010000),
            scan_direction: ScanDirection::Positive,
            edge_of_flight_line: false,
            classification: Classification::from(2),
            scan_angle_rank: 2,
            user_data: 3,
            point_source_id: 4,
            gps_time: Some(5.),
            color: Some(Color {
                red: 6,
                green: 7,
                blue: 8,
            }),
            extra_bytes: Vec::new(),
        }
    }

    fn check_point(point: &Point) {
        assert_eq!(1., point.x);
        assert_eq!(2., point.y);
        assert_eq!(3., point.z);
        assert_eq!(4, point.intensity);
        assert_eq!(1, point.return_number);
        assert_eq!(2, point.number_of_returns);
        assert_eq!(ScanDirection::Positive, point.scan_direction);
        assert!(!point.edge_of_flight_line);
        assert_eq!(2, point.classification);
        assert_eq!(2, point.scan_angle_rank);
        assert_eq!(3, point.user_data);
        assert_eq!(4, point.point_source_id);
    }

    fn check_point_0(point: Point) {
        check_point(&point);
        assert!(point.gps_time.is_none());
        assert!(point.color.is_none());
    }

    fn check_point_1(point: Point) {
        check_point(&point);
        assert_eq!(5., point.gps_time.unwrap());
        assert!(point.color.is_none());
    }

    fn check_point_2(point: Point) {
        check_point(&point);
        assert!(point.gps_time.is_none());
        let color = point.color.unwrap();
        assert_eq!(6, color.red);
        assert_eq!(7, color.green);
        assert_eq!(8, color.blue);
    }

    fn check_point_3(point: Point) {
        check_point(&point);
        assert_eq!(5., point.gps_time.unwrap());
        let color = point.color.unwrap();
        assert_eq!(6, color.red);
        assert_eq!(7, color.green);
        assert_eq!(8, color.blue);
    }

    #[test]
    fn write_zero_points() {
        let mut cursor = Cursor::new(Vec::new());
        Writer::default(&mut cursor).unwrap();
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_default() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_0(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_format_1() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.point_format = 1.into();
            let mut writer = builder.writer(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_1(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_format_2() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.point_format = 2.into();
            let mut writer = builder.writer(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_2(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_format_3() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.point_format = 3.into();
            let mut writer = builder.writer(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_3(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn try_write_point_format_1_no_time() {
        let mut builder = Builder::new();
        builder.point_format = 1.into();
        let mut writer = builder.writer(Cursor::new(Vec::new())).unwrap();
        let mut point = point();
        point.gps_time = None;
        assert!(writer.write(&point).is_err());
    }

    #[test]
    fn try_write_point_format_2_no_color() {
        let mut builder = Builder::new();
        builder.point_format = 2.into();
        let mut writer = builder.writer(Cursor::new(Vec::new())).unwrap();
        let mut point = point();
        point.color = None;
        assert!(writer.write(&point).is_err());
    }

    #[test]
    fn write_point_count() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        assert_eq!(1, Reader::new(cursor).unwrap().header.point_count);
    }

    #[test]
    fn write_two_points() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        assert!(reader.read().unwrap().is_some());
        assert!(reader.read().unwrap().is_some());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_bounds() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let reader = Reader::new(cursor).unwrap();
        assert_eq!(Bounds::new(1., 2., 3., 1., 2., 3.), reader.header.bounds);
    }

    #[test]
    fn close_the_writer() {
        let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
        writer.close().is_ok();
        assert!(writer.write(&point()).is_err());
        assert!(writer.close().is_err());
    }

    #[test]
    fn write_version() {
        let mut cursor = Cursor::new(Vec::new());
        let mut builder = Builder::new();
        builder.version = (1, 0).into();
        builder.writer(&mut cursor).unwrap();
        cursor.set_position(0);
        assert_eq!(Version::from((1, 0)),
                   Reader::new(&mut cursor).unwrap().header.version);
    }

    #[test]
    fn write_zero_return_number() {
        let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
        writer.write(&Default::default()).unwrap();
    }

    #[test]
    fn write_extra_bytes() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.extra_bytes = 5;
            let mut writer = builder.writer(&mut cursor).unwrap();
            let mut point = point();
            point.extra_bytes = b"Hello".to_vec();
            writer.write(&point).unwrap();
        }
        cursor.set_position(0);
        let point = Reader::new(cursor).unwrap().read_to_end().unwrap().pop().unwrap();
        assert_eq!(b"Hello", &point.extra_bytes[..]);
    }

    #[test]
    fn writer_from_path() {
        assert!(Writer::from_path("/dev/null").is_ok());
    }

    #[test]
    fn builder_writer_for_path() {
        assert!(Builder::new().writer_from_path("/dev/null").is_ok());
    }

    #[test]
    fn disallow_file_source_id_wipe() {
        let mut builder = Builder::new();
        builder.file_source_id = 1;
        builder.version = (1, 0).into();
        assert!(builder.writer(Cursor::new(Vec::new())).is_err());
    }

    #[test]
    fn disallow_global_encoding_downcast() {
        let mut builder = Builder::new();
        builder.global_encoding = 1.into();
        builder.version = (1, 0).into();
        assert!(builder.writer(Cursor::new(Vec::new())).is_err());
    }
}
<commit_msg>Write point count by return<commit_after>//! Write points to a `Write`.
//!
//! Simple writes can be done with a `Writer`, but if you need to configure your output file, use a
//! `Builder`.

use std::fs::File;
use std::io::{BufWriter, Seek, SeekFrom, Write};
use std::path::Path;

use {Error, Result};
use builder::Builder;
use header::{Header, WriteHeader};
use point::{Point, WritePoint};
use utils::{Bounds, Triple};
use vlr::{Vlr, WriteVlr};

/// Write LAS points to a `Write`.
///
/// This struct implements `Drop`, so the LAS data are finalized (the header is re-written) when
/// the `Writer` goes out of scope. This will panic if there is an error while closing the file, so
/// if you're worried about panics you will need to use `Writer::close` instead.
#[derive(Debug)]
pub struct Writer<W: Seek + Write> {
    bounds: Bounds<f64>,
    closed: bool,
    header: Header,
    point_count: u32,
    point_count_by_return: [u32; 5],
    vlrs: Vec<Vlr>,
    write: W,
}

impl Writer<BufWriter<File>> {
    /// Creates a default `Writer` that will write points out to a file at the path.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Writer;
    /// let writer = Writer::from_path("/dev/null").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Writer<BufWriter<File>>> {
        File::create(path).map_err(Error::from).and_then(|f| Writer::default(BufWriter::new(f)))
    }
}

impl<W: Seek + Write> Writer<W> {
    fn from(builder: &Builder, write: W) -> Writer<W> {
        Writer {
            bounds: Default::default(),
            closed: false,
            header: builder.into(),
            point_count: Default::default(),
            point_count_by_return: Default::default(),
            vlrs: builder.vlrs.clone(),
            write: write,
        }
    }

    /// Creates a new writer from a `Builder` and a `Write`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Writer;
    /// use std::io::Cursor;
    /// use las::Builder;
    /// let writer = Writer::new(&Builder::new(), Cursor::new(Vec::new())).unwrap();
    /// ```
    pub fn new(builder: &Builder, write: W) -> Result<Writer<W>> {
        let mut writer = Writer::from(builder, write);
        try!(writer.write_header());
        Ok(writer)
    }

    /// Creates a new default writer.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// # use las::Writer;
    /// let writer = Writer::default(Cursor::new(Vec::new())).unwrap();
    /// ```
    pub fn default(write: W) -> Result<Writer<W>> {
        Builder::new().writer(write)
    }

    /// Writes a point.
    ///
    /// # Examples
    ///
    ///
    /// ```
    /// use std::io::Cursor;
    /// # use las::Writer;
    /// let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
    /// writer.write(&Default::default()).unwrap();
    /// ```
    pub fn write(&mut self, point: &Point) -> Result<()> {
        if self.closed {
            return Err(Error::ClosedWriter);
        }
        try!(self.write.write_point(point,
                                    self.header.transforms,
                                    self.header.point_format,
                                    self.header.extra_bytes));
        self.point_count += 1;
        if point.return_number.is_valid() {
            self.point_count_by_return[u8::from(point.return_number) as usize - 1] += 1;
        }
        self.bounds.grow(Triple {
            x: point.x,
            y: point.y,
            z: point.z,
        });
        Ok(())
    }

    fn write_header(&mut self) -> Result<()> {
        // TODO test point count by return, offsets, etc
        self.header.point_count = self.point_count;
        self.header.point_count_by_return = self.point_count_by_return;
        self.header.bounds = self.bounds;
        try!(self.write.seek(SeekFrom::Start(0)));
        try!(self.write.write_header(self.header));
        for vlr in &self.vlrs {
            try!(self.write.write_vlr(vlr));
        }
        Ok(())
    }

    /// Closes this writer.
    ///
    /// After the writer is closed, and future calls to `write` will error.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// # use las::Writer;
    /// let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
    /// assert!(writer.write(&Default::default()).is_ok());
    /// writer.close().unwrap();
    /// assert!(writer.write(&Default::default()).is_err());
    /// ```
    pub fn close(&mut self) -> Result<()> {
        if self.closed {
            return Err(Error::ClosedWriter);
        }
        try!(self.write_header());
        self.closed = true;
        Ok(())
    }
}

impl<W: Seek + Write> Drop for Writer<W> {
    fn drop(&mut self) {
        if !self.closed {
            if let Err(err) = self.close() {
                error!("Error while dropping writer: {}", err);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::io::Cursor;

    use builder::Builder;
    use point::{Classification, Color, NumberOfReturns, Point, ReturnNumber, ScanDirection};
    use reader::Reader;
    use utils::Bounds;
    use version::Version;

    fn point() -> Point {
        Point {
            x: 1.,
            y: 2.,
            z: 3.,
            intensity: 4,
            return_number: ReturnNumber::from(1),
            number_of_returns: NumberOfReturns::from(0b00010000),
            scan_direction: ScanDirection::Positive,
            edge_of_flight_line: false,
            classification: Classification::from(2),
            scan_angle_rank: 2,
            user_data: 3,
            point_source_id: 4,
            gps_time: Some(5.),
            color: Some(Color {
                red: 6,
                green: 7,
                blue: 8,
            }),
            extra_bytes: Vec::new(),
        }
    }

    fn check_point(point: &Point) {
        assert_eq!(1., point.x);
        assert_eq!(2., point.y);
        assert_eq!(3., point.z);
        assert_eq!(4, point.intensity);
        assert_eq!(1, point.return_number);
        assert_eq!(2, point.number_of_returns);
        assert_eq!(ScanDirection::Positive, point.scan_direction);
        assert!(!point.edge_of_flight_line);
        assert_eq!(2, point.classification);
        assert_eq!(2, point.scan_angle_rank);
        assert_eq!(3, point.user_data);
        assert_eq!(4, point.point_source_id);
    }

    fn check_point_0(point: Point) {
        check_point(&point);
        assert!(point.gps_time.is_none());
        assert!(point.color.is_none());
    }

    fn check_point_1(point: Point) {
        check_point(&point);
        assert_eq!(5., point.gps_time.unwrap());
        assert!(point.color.is_none());
    }

    fn check_point_2(point: Point) {
        check_point(&point);
        assert!(point.gps_time.is_none());
        let color = point.color.unwrap();
        assert_eq!(6, color.red);
        assert_eq!(7, color.green);
        assert_eq!(8, color.blue);
    }

    fn check_point_3(point: Point) {
        check_point(&point);
        assert_eq!(5., point.gps_time.unwrap());
        let color = point.color.unwrap();
        assert_eq!(6, color.red);
        assert_eq!(7, color.green);
        assert_eq!(8, color.blue);
    }

    #[test]
    fn write_zero_points() {
        let mut cursor = Cursor::new(Vec::new());
        Writer::default(&mut cursor).unwrap();
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_default() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_0(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_format_1() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.point_format = 1.into();
            let mut writer = builder.writer(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_1(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_format_2() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.point_format = 2.into();
            let mut writer = builder.writer(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_2(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_one_point_format_3() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.point_format = 3.into();
            let mut writer = builder.writer(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        check_point_3(reader.read().unwrap().unwrap());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn try_write_point_format_1_no_time() {
        let mut builder = Builder::new();
        builder.point_format = 1.into();
        let mut writer = builder.writer(Cursor::new(Vec::new())).unwrap();
        let mut point = point();
        point.gps_time = None;
        assert!(writer.write(&point).is_err());
    }

    #[test]
    fn try_write_point_format_2_no_color() {
        let mut builder = Builder::new();
        builder.point_format = 2.into();
        let mut writer = builder.writer(Cursor::new(Vec::new())).unwrap();
        let mut point = point();
        point.color = None;
        assert!(writer.write(&point).is_err());
    }

    #[test]
    fn write_point_count() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        assert_eq!(1, Reader::new(cursor).unwrap().header.point_count);
    }

    #[test]
    fn write_two_points() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let mut reader = Reader::new(cursor).unwrap();
        assert!(reader.read().unwrap().is_some());
        assert!(reader.read().unwrap().is_some());
        assert!(reader.read().unwrap().is_none());
    }

    #[test]
    fn write_bounds() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let reader = Reader::new(cursor).unwrap();
        assert_eq!(Bounds::new(1., 2., 3., 1., 2., 3.), reader.header.bounds);
    }

    #[test]
    fn close_the_writer() {
        let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
        writer.close().is_ok();
        assert!(writer.write(&point()).is_err());
        assert!(writer.close().is_err());
    }

    #[test]
    fn write_version() {
        let mut cursor = Cursor::new(Vec::new());
        let mut builder = Builder::new();
        builder.version = (1, 0).into();
        builder.writer(&mut cursor).unwrap();
        cursor.set_position(0);
        assert_eq!(Version::from((1, 0)),
                   Reader::new(&mut cursor).unwrap().header.version);
    }

    #[test]
    fn write_zero_return_number() {
        let mut writer = Writer::default(Cursor::new(Vec::new())).unwrap();
        writer.write(&Default::default()).unwrap();
    }

    #[test]
    fn write_extra_bytes() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut builder = Builder::new();
            builder.extra_bytes = 5;
            let mut writer = builder.writer(&mut cursor).unwrap();
            let mut point = point();
            point.extra_bytes = b"Hello".to_vec();
            writer.write(&point).unwrap();
        }
        cursor.set_position(0);
        let point = Reader::new(cursor).unwrap().read_to_end().unwrap().pop().unwrap();
        assert_eq!(b"Hello", &point.extra_bytes[..]);
    }

    #[test]
    fn writer_from_path() {
        assert!(Writer::from_path("/dev/null").is_ok());
    }

    #[test]
    fn builder_writer_for_path() {
        assert!(Builder::new().writer_from_path("/dev/null").is_ok());
    }

    #[test]
    fn disallow_file_source_id_wipe() {
        let mut builder = Builder::new();
        builder.file_source_id = 1;
        builder.version = (1, 0).into();
        assert!(builder.writer(Cursor::new(Vec::new())).is_err());
    }

    #[test]
    fn disallow_global_encoding_downcast() {
        let mut builder = Builder::new();
        builder.global_encoding = 1.into();
        builder.version = (1, 0).into();
        assert!(builder.writer(Cursor::new(Vec::new())).is_err());
    }

    #[test]
    fn point_count_by_return_type() {
        let mut cursor = Cursor::new(Vec::new());
        {
            let mut writer = Writer::default(&mut cursor).unwrap();
            writer.write(&point()).unwrap();
        }
        cursor.set_position(0);
        let reader = Reader::new(cursor).unwrap();
        assert_eq!([1, 0, 0, 0, 0], reader.header.point_count_by_return);
    }
}
<|endoftext|>


<commit_before>//! The `event` module contains traits and structs to actually run your game mainloop
//! and handle top-level state, as well as handle input events such as keyboard
//! and mouse.
//!
//! If you don't want to do this, you can write your own mainloop and
//! get the necessary event machinery by calling
//! `context.sdl_context.event_pump()` on your `Context`.  You can
//! then call whatever SDL event methods you want on that.  This is
//! not particularly elegant and is not guaranteed to be stable across
//! different versions of ggez (for instance, we may someday get rid of SDL2),
//! but trying to wrap it
//! up more conveniently really ends up with the exact same interface.
//!
//! See the `eventloop` example for an implementation.

use sdl2;

/// A key code.
pub use sdl2::keyboard::Keycode;

/// A struct that holds the state of modifier buttons such as ctrl or shift.
pub use sdl2::keyboard::Mod;
/// A mouse button press.
pub use sdl2::mouse::MouseButton;
/// A struct containing the mouse state at a given instant.
pub use sdl2::mouse::MouseState;

/// A controller button.
pub use sdl2::controller::Button;
/// A controller axis.
pub use sdl2::controller::Axis;

/// The event iterator
pub use sdl2::event::EventPollIterator;
pub use sdl2::event::Event;

use sdl2::event::Event::*;
use sdl2::event;
use sdl2::mouse;
use sdl2::keyboard;

use context::Context;
use GameResult;

pub use sdl2::keyboard::{CAPSMOD, LALTMOD, LCTRLMOD, LGUIMOD, LSHIFTMOD, MODEMOD, NOMOD, NUMMOD,
                         RALTMOD, RCTRLMOD, RESERVEDMOD, RGUIMOD, RSHIFTMOD};

/// A trait defining event callbacks; your primary interface with
/// `ggez`'s event loop.  Have a type implement this trait and
/// override at least the update() and draw() methods, then pass it to
/// `event::run()` to run the game's mainloop.
///
/// The default event handlers do nothing, apart from
/// `key_down_event()`, which will by default exit the game if escape
/// is pressed.  Just override the methods you want to do things with.
pub trait EventHandler {
    /// Called upon each physics update to the game.
    /// This should be where the game's logic takes place.
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()>;

    /// Called to do the drawing of your game.
    /// You probably want to start this with
    /// `graphics::clear()` and end it with
    /// `graphics::present()` and `timer::sleep_until_next_frame()`
    fn draw(&mut self, _ctx: &mut Context) -> GameResult<()>;

    /// A mouse button was pressed
    fn mouse_button_down_event(
        &mut self,
        _ctx: &mut Context,
        _button: mouse::MouseButton,
        _x: i32,
        _y: i32,
    ) {
    }

    /// A mouse button was released
    fn mouse_button_up_event(
        &mut self,
        _ctx: &mut Context,
        _button: mouse::MouseButton,
        _x: i32,
        _y: i32,
    ) {
    }

    /// The mouse was moved; it provides both absolute x and y coordinates in the window,
    /// and relative x and y coordinates compared to its last position.
    fn mouse_motion_event(
        &mut self,
        _ctx: &mut Context,
        _state: mouse::MouseState,
        _x: i32,
        _y: i32,
        _xrel: i32,
        _yrel: i32,
    ) {
    }

    /// The mousewheel was clicked.
    fn mouse_wheel_event(&mut self, _ctx: &mut Context, _x: i32, _y: i32) {}

    /// A keyboard button was pressed.
    fn key_down_event(&mut self, ctx: &mut Context, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        if keycode == keyboard::Keycode::Escape {
            ctx.quit().expect("Should never fail");
        }
    }

    /// A keyboard button was released.
    fn key_up_event(&mut self, _ctx: &mut Context, _keycode: Keycode, _keymod: Mod, _repeat: bool) {
    }

    /// A controller button was pressed; instance_id identifies which controller.
    fn controller_button_down_event(
        &mut self,
        _ctx: &mut Context,
        _btn: Button,
        _instance_id: i32,
    ) {
    }
    /// A controller button was released.
    fn controller_button_up_event(&mut self, _ctx: &mut Context, _btn: Button, _instance_id: i32) {}
    /// A controller axis moved.
    fn controller_axis_event(
        &mut self,
        _ctx: &mut Context,
        _axis: Axis,
        _value: i16,
        _instance_id: i32,
    ) {
    }

    /// Called when the window is shown or hidden.
    fn focus_event(&mut self, _ctx: &mut Context, _gained: bool) {}

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit.
    fn quit_event(&mut self, _ctx: &mut Context) -> bool {
        println!("quit_event() callback called, quitting...");
        false
    }

    /// Called when the user resizes the window.
    /// Is not called when you resize it yourself with
    /// `graphics::set_mode()` though.
    fn resize_event(&mut self, _ctx: &mut Context, _width: u32, _height: u32) {}
}

/// A handle to access the OS's event pump.
pub struct Events(sdl2::EventPump);

use std::fmt;
impl fmt::Debug for Events {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Events: {:p}>", self)
    }
}

impl Events {
    /// Create a new Events object.
    pub fn new(ctx: &Context) -> GameResult<Events> {
        let e = ctx.sdl_context.event_pump()?;
        Ok(Events(e))
    }

    /// Get an iterator for all events.
    pub fn poll(&mut self) -> EventPollIterator {
        self.0.poll_iter()
    }
}

/// Runs the game's main loop, calling event callbacks on the given state
/// object as events occur.
///
/// It does not try to do any type of framerate limiting.  See the
/// documentation for the `timer` module for more info.
pub fn run<S>(ctx: &mut Context, state: &mut S) -> GameResult<()>
where
    S: EventHandler,
{
    let mut event_pump = ctx.sdl_context.event_pump()?;

    let mut continuing = true;
    while continuing {
        ctx.timer_context.tick();

        for event in event_pump.poll_iter() {
            match event {
                Quit { .. } => {
                    continuing = state.quit_event(ctx);
                    // println!("Quit event: {:?}", t);
                }
                KeyDown {
                    keycode,
                    keymod,
                    repeat,
                    ..
                } => {
                    if let Some(key) = keycode {
                        state.key_down_event(ctx, key, keymod, repeat)
                    }
                }
                KeyUp {
                    keycode,
                    keymod,
                    repeat,
                    ..
                } => {
                    if let Some(key) = keycode {
                        state.key_up_event(ctx, key, keymod, repeat)
                    }
                }
                MouseButtonDown {
                    mouse_btn, x, y, ..
                } => state.mouse_button_down_event(ctx, mouse_btn, x, y),
                MouseButtonUp {
                    mouse_btn, x, y, ..
                } => state.mouse_button_up_event(ctx, mouse_btn, x, y),
                MouseMotion {
                    mousestate,
                    x,
                    y,
                    xrel,
                    yrel,
                    ..
                } => state.mouse_motion_event(ctx, mousestate, x, y, xrel, yrel),
                MouseWheel { x, y, .. } => state.mouse_wheel_event(ctx, x, y),
                ControllerButtonDown { button, which, .. } => {
                    state.controller_button_down_event(ctx, button, which)
                }
                ControllerButtonUp { button, which, .. } => {
                    state.controller_button_up_event(ctx, button, which)
                }
                ControllerAxisMotion {
                    axis, value, which, ..
                } => state.controller_axis_event(ctx, axis, value, which),
                Window {
                    win_event: event::WindowEvent::FocusGained,
                    ..
                } => state.focus_event(ctx, true),
                Window {
                    win_event: event::WindowEvent::FocusLost,
                    ..
                } => state.focus_event(ctx, false),
                Window {
                    win_event: event::WindowEvent::Resized(w, h),
                    ..
                } => {
                    ctx.gfx_context.resize_viewport();
                    state.resize_event(ctx, w as u32, h as u32);
                }
                _ => {}
            }
        }
        state.update(ctx)?;
        state.draw(ctx)?;
    }

    Ok(())
}
<commit_msg>remove timer::sleep_until_next_frame from docs<commit_after>//! The `event` module contains traits and structs to actually run your game mainloop
//! and handle top-level state, as well as handle input events such as keyboard
//! and mouse.
//!
//! If you don't want to do this, you can write your own mainloop and
//! get the necessary event machinery by calling
//! `context.sdl_context.event_pump()` on your `Context`.  You can
//! then call whatever SDL event methods you want on that.  This is
//! not particularly elegant and is not guaranteed to be stable across
//! different versions of ggez (for instance, we may someday get rid of SDL2),
//! but trying to wrap it
//! up more conveniently really ends up with the exact same interface.
//!
//! See the `eventloop` example for an implementation.

use sdl2;

/// A key code.
pub use sdl2::keyboard::Keycode;

/// A struct that holds the state of modifier buttons such as ctrl or shift.
pub use sdl2::keyboard::Mod;
/// A mouse button press.
pub use sdl2::mouse::MouseButton;
/// A struct containing the mouse state at a given instant.
pub use sdl2::mouse::MouseState;

/// A controller button.
pub use sdl2::controller::Button;
/// A controller axis.
pub use sdl2::controller::Axis;

/// The event iterator
pub use sdl2::event::EventPollIterator;
pub use sdl2::event::Event;

use sdl2::event::Event::*;
use sdl2::event;
use sdl2::mouse;
use sdl2::keyboard;

use context::Context;
use GameResult;

pub use sdl2::keyboard::{CAPSMOD, LALTMOD, LCTRLMOD, LGUIMOD, LSHIFTMOD, MODEMOD, NOMOD, NUMMOD,
                         RALTMOD, RCTRLMOD, RESERVEDMOD, RGUIMOD, RSHIFTMOD};

/// A trait defining event callbacks; your primary interface with
/// `ggez`'s event loop.  Have a type implement this trait and
/// override at least the update() and draw() methods, then pass it to
/// `event::run()` to run the game's mainloop.
///
/// The default event handlers do nothing, apart from
/// `key_down_event()`, which will by default exit the game if escape
/// is pressed.  Just override the methods you want to do things with.
pub trait EventHandler {
    /// Called upon each physics update to the game.
    /// This should be where the game's logic takes place.
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()>;

    /// Called to do the drawing of your game.
    /// You probably want to start this with
    /// `graphics::clear()` and end it with
    /// `graphics::present()` and `timer::yield_now()`
    fn draw(&mut self, _ctx: &mut Context) -> GameResult<()>;

    /// A mouse button was pressed
    fn mouse_button_down_event(
        &mut self,
        _ctx: &mut Context,
        _button: mouse::MouseButton,
        _x: i32,
        _y: i32,
    ) {
    }

    /// A mouse button was released
    fn mouse_button_up_event(
        &mut self,
        _ctx: &mut Context,
        _button: mouse::MouseButton,
        _x: i32,
        _y: i32,
    ) {
    }

    /// The mouse was moved; it provides both absolute x and y coordinates in the window,
    /// and relative x and y coordinates compared to its last position.
    fn mouse_motion_event(
        &mut self,
        _ctx: &mut Context,
        _state: mouse::MouseState,
        _x: i32,
        _y: i32,
        _xrel: i32,
        _yrel: i32,
    ) {
    }

    /// The mousewheel was clicked.
    fn mouse_wheel_event(&mut self, _ctx: &mut Context, _x: i32, _y: i32) {}

    /// A keyboard button was pressed.
    fn key_down_event(&mut self, ctx: &mut Context, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        if keycode == keyboard::Keycode::Escape {
            ctx.quit().expect("Should never fail");
        }
    }

    /// A keyboard button was released.
    fn key_up_event(&mut self, _ctx: &mut Context, _keycode: Keycode, _keymod: Mod, _repeat: bool) {
    }

    /// A controller button was pressed; instance_id identifies which controller.
    fn controller_button_down_event(
        &mut self,
        _ctx: &mut Context,
        _btn: Button,
        _instance_id: i32,
    ) {
    }
    /// A controller button was released.
    fn controller_button_up_event(&mut self, _ctx: &mut Context, _btn: Button, _instance_id: i32) {}
    /// A controller axis moved.
    fn controller_axis_event(
        &mut self,
        _ctx: &mut Context,
        _axis: Axis,
        _value: i16,
        _instance_id: i32,
    ) {
    }

    /// Called when the window is shown or hidden.
    fn focus_event(&mut self, _ctx: &mut Context, _gained: bool) {}

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit.
    fn quit_event(&mut self, _ctx: &mut Context) -> bool {
        println!("quit_event() callback called, quitting...");
        false
    }

    /// Called when the user resizes the window.
    /// Is not called when you resize it yourself with
    /// `graphics::set_mode()` though.
    fn resize_event(&mut self, _ctx: &mut Context, _width: u32, _height: u32) {}
}

/// A handle to access the OS's event pump.
pub struct Events(sdl2::EventPump);

use std::fmt;
impl fmt::Debug for Events {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Events: {:p}>", self)
    }
}

impl Events {
    /// Create a new Events object.
    pub fn new(ctx: &Context) -> GameResult<Events> {
        let e = ctx.sdl_context.event_pump()?;
        Ok(Events(e))
    }

    /// Get an iterator for all events.
    pub fn poll(&mut self) -> EventPollIterator {
        self.0.poll_iter()
    }
}

/// Runs the game's main loop, calling event callbacks on the given state
/// object as events occur.
///
/// It does not try to do any type of framerate limiting.  See the
/// documentation for the `timer` module for more info.
pub fn run<S>(ctx: &mut Context, state: &mut S) -> GameResult<()>
where
    S: EventHandler,
{
    let mut event_pump = ctx.sdl_context.event_pump()?;

    let mut continuing = true;
    while continuing {
        ctx.timer_context.tick();

        for event in event_pump.poll_iter() {
            match event {
                Quit { .. } => {
                    continuing = state.quit_event(ctx);
                    // println!("Quit event: {:?}", t);
                }
                KeyDown {
                    keycode,
                    keymod,
                    repeat,
                    ..
                } => {
                    if let Some(key) = keycode {
                        state.key_down_event(ctx, key, keymod, repeat)
                    }
                }
                KeyUp {
                    keycode,
                    keymod,
                    repeat,
                    ..
                } => {
                    if let Some(key) = keycode {
                        state.key_up_event(ctx, key, keymod, repeat)
                    }
                }
                MouseButtonDown {
                    mouse_btn, x, y, ..
                } => state.mouse_button_down_event(ctx, mouse_btn, x, y),
                MouseButtonUp {
                    mouse_btn, x, y, ..
                } => state.mouse_button_up_event(ctx, mouse_btn, x, y),
                MouseMotion {
                    mousestate,
                    x,
                    y,
                    xrel,
                    yrel,
                    ..
                } => state.mouse_motion_event(ctx, mousestate, x, y, xrel, yrel),
                MouseWheel { x, y, .. } => state.mouse_wheel_event(ctx, x, y),
                ControllerButtonDown { button, which, .. } => {
                    state.controller_button_down_event(ctx, button, which)
                }
                ControllerButtonUp { button, which, .. } => {
                    state.controller_button_up_event(ctx, button, which)
                }
                ControllerAxisMotion {
                    axis, value, which, ..
                } => state.controller_axis_event(ctx, axis, value, which),
                Window {
                    win_event: event::WindowEvent::FocusGained,
                    ..
                } => state.focus_event(ctx, true),
                Window {
                    win_event: event::WindowEvent::FocusLost,
                    ..
                } => state.focus_event(ctx, false),
                Window {
                    win_event: event::WindowEvent::Resized(w, h),
                    ..
                } => {
                    ctx.gfx_context.resize_viewport();
                    state.resize_event(ctx, w as u32, h as u32);
                }
                _ => {}
            }
        }
        state.update(ctx)?;
        state.draw(ctx)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>
use self::super::Github;
use rustc_serialize::json;
use std::io::Result;
use rep::{Gist, GistFork, GistReq};

/// reference to gists associated with a github user
pub struct UserGists<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
}

impl<'a> UserGists<'a> {

  pub fn new(github: &'a Github<'a>, owner: &'static str) -> UserGists<'a> {
    UserGists { github: github, owner: owner }
  }

  pub fn list(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &format!("/users/{}/gists", self.owner)
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
  }
}

pub struct Gists<'a> {
  github: &'a Github<'a>
}

impl<'a> Gists<'a> {
  pub fn new(github: &'a Github<'a>) -> Gists<'a> {
    Gists { github: github }
  }

  fn path(&self, more: &str) -> String {
    format!("/gists{}", more)
  }

  pub fn star(&self, id: &'static str) -> Result<()> {
    let body = try!(
      self.github.put(
        &self.path(
          &format!("/{}/star", id)
        ),
        &[]
      )
    );
    Ok(())
  }

  pub fn unstar(&self, id: &'static str) -> Result<()> {
    let body = try!(
      self.github.delete(
        &self.path(
          &format!("/{}/star", id)
        )
      )
    );
    Ok(())
  }

  pub fn fork(&self, id: &'static str) -> Result<Gist> {
    let body = try!(
      self.github.post(
        &self.path(
          &format!("/{}/forks", id)
        ),
        &[]
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }

  pub fn forks(&self, id: &'static str) -> Result<Vec<GistFork>> {
    let body = try!(
      self.github.get(
        &self.path(
          &format!("/{}/forks", id)
        )
      )
        );
    Ok(json::decode::<Vec<GistFork>>(&body).unwrap())
  }

  pub fn delete(&self, id: &'static str) -> Result<()> {
    let body = try!(
      self.github.delete(
        &self.path(
          &format!("/{}", id)
        )
      )
    );
    Ok(())
  }

  pub fn get(&self, id: &'static str) -> Result<Gist> {
    let body = try!(
      self.github.get(
        &self.path(
          &format!("/{}", id)
        )
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }

  pub fn getrev(&self, id: &'static str, sha: &'static str) -> Result<Gist> {
    let body = try!(
      self.github.get(
        &self.path(
          &format!("/{}/{}", id, sha)
        )
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }

  pub fn list(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &self.path("")
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
  }

  pub fn public(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &self.path("/public")
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
    }

  pub fn starred(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &self.path("/starred")
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
  }

  pub fn create(&self, gist: &GistReq) -> Result<Gist> {
    let data = json::encode(&gist).unwrap();
    let body = try!(
      self.github.post(
        &self.path(""),
        data.as_bytes()
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }
}
<commit_msg>simplify gists<commit_after>
use self::super::Github;
use rustc_serialize::json;
use std::io::Result;
use rep::{Gist, GistFork, GistReq};

/// reference to gists associated with a github user
pub struct UserGists<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
}

impl<'a> UserGists<'a> {

  pub fn new(github: &'a Github<'a>, owner: &'static str) -> UserGists<'a> {
    UserGists { github: github, owner: owner }
  }

  pub fn list(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &format!("/users/{}/gists", self.owner)
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
  }
}

pub struct Gists<'a> {
  github: &'a Github<'a>
}

impl<'a> Gists<'a> {
  pub fn new(github: &'a Github<'a>) -> Gists<'a> {
    Gists { github: github }
  }

  fn path(&self, more: &str) -> String {
    format!("/gists{}", more)
  }

  pub fn star(&self, id: &'static str) -> Result<()> {
    self.github.put(
      &self.path(
        &format!("/{}/star", id)
      ),
      &[]
    ).map(|_| ())
  }

  pub fn unstar(&self, id: &'static str) -> Result<()> {
    self.github.delete(
      &self.path(
        &format!("/{}/star", id)
      )
    ).map(|_| ())
  }

  pub fn fork(&self, id: &'static str) -> Result<Gist> {
    let body = try!(
      self.github.post(
        &self.path(
          &format!("/{}/forks", id)
        ),
        &[]
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }

  pub fn forks(&self, id: &'static str) -> Result<Vec<GistFork>> {
    let body = try!(
      self.github.get(
        &self.path(
          &format!("/{}/forks", id)
        )
      )
        );
    Ok(json::decode::<Vec<GistFork>>(&body).unwrap())
  }

  pub fn delete(&self, id: &'static str) -> Result<()> {
    self.github.delete(
      &self.path(
        &format!("/{}", id)
      )
    ).map(|_| ())
  }

  pub fn get(&self, id: &'static str) -> Result<Gist> {
    let body = try!(
      self.github.get(
        &self.path(
          &format!("/{}", id)
        )
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }

  pub fn getrev(&self, id: &'static str, sha: &'static str) -> Result<Gist> {
    let body = try!(
      self.github.get(
        &self.path(
          &format!("/{}/{}", id, sha)
        )
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }

  pub fn list(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &self.path("")
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
  }

  pub fn public(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &self.path("/public")
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
    }

  pub fn starred(&self) -> Result<Vec<Gist>> {
    let body = try!(
      self.github.get(
        &self.path("/starred")
      )
    );
    Ok(json::decode::<Vec<Gist>>(&body).unwrap())
  }

  pub fn create(&self, gist: &GistReq) -> Result<Gist> {
    let data = json::encode(&gist).unwrap();
    let body = try!(
      self.github.post(
        &self.path(""),
        data.as_bytes()
      )
    );
    Ok(json::decode::<Gist>(&body).unwrap())
  }
}
<|endoftext|>


<commit_before>//! Data Change Notification Callbacks
#![allow(non_camel_case_types)]

use std::mem;
use std::ptr;
use std::os::raw::{c_int, c_char, c_void};

use ffi;

use {Connection, InnerConnection};

// Commit And Rollback Notification Callbacks
// http://sqlite.org/c3ref/commit_hook.html
/*
void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
*/

/// Authorizer Action Codes
pub enum Action {
    UNKNOWN = -1,
    SQLITE_CREATE_INDEX = ffi::SQLITE_CREATE_INDEX as isize,
    SQLITE_CREATE_TABLE = ffi::SQLITE_CREATE_TABLE as isize,
    SQLITE_CREATE_TEMP_INDEX = ffi::SQLITE_CREATE_TEMP_INDEX as isize,
    SQLITE_CREATE_TEMP_TABLE = ffi::SQLITE_CREATE_TEMP_TABLE as isize,
    SQLITE_CREATE_TEMP_TRIGGER = ffi::SQLITE_CREATE_TEMP_TRIGGER as isize,
    SQLITE_CREATE_TEMP_VIEW = ffi::SQLITE_CREATE_TEMP_VIEW as isize,
    SQLITE_CREATE_TRIGGER = ffi::SQLITE_CREATE_TRIGGER as isize,
    SQLITE_CREATE_VIEW = ffi::SQLITE_CREATE_VIEW as isize,
    SQLITE_DELETE = ffi::SQLITE_DELETE as isize,
    SQLITE_DROP_INDEX = ffi::SQLITE_DROP_INDEX as isize,
    SQLITE_DROP_TABLE = ffi::SQLITE_DROP_TABLE as isize,
    SQLITE_DROP_TEMP_INDEX = ffi::SQLITE_DROP_TEMP_INDEX as isize,
    SQLITE_DROP_TEMP_TABLE = ffi::SQLITE_DROP_TEMP_TABLE as isize,
    SQLITE_DROP_TEMP_TRIGGER = ffi::SQLITE_DROP_TEMP_TRIGGER as isize,
    SQLITE_DROP_TEMP_VIEW = ffi::SQLITE_DROP_TEMP_VIEW as isize,
    SQLITE_DROP_TRIGGER = ffi::SQLITE_DROP_TRIGGER as isize,
    SQLITE_DROP_VIEW = ffi::SQLITE_DROP_VIEW as isize,
    SQLITE_INSERT = ffi::SQLITE_INSERT as isize,
    SQLITE_PRAGMA = ffi::SQLITE_PRAGMA as isize,
    SQLITE_READ = ffi::SQLITE_READ as isize,
    SQLITE_SELECT = ffi::SQLITE_SELECT as isize,
    SQLITE_TRANSACTION = ffi::SQLITE_TRANSACTION as isize,
    SQLITE_UPDATE = ffi::SQLITE_UPDATE as isize,
    SQLITE_ATTACH = ffi::SQLITE_ATTACH as isize,
    SQLITE_DETACH = ffi::SQLITE_DETACH as isize,
    SQLITE_ALTER_TABLE = ffi::SQLITE_ALTER_TABLE as isize,
    SQLITE_REINDEX = ffi::SQLITE_REINDEX as isize,
    SQLITE_ANALYZE = ffi::SQLITE_ANALYZE as isize,
    SQLITE_CREATE_VTABLE = ffi::SQLITE_CREATE_VTABLE as isize,
    SQLITE_DROP_VTABLE = ffi::SQLITE_DROP_VTABLE as isize,
    SQLITE_FUNCTION = ffi::SQLITE_FUNCTION as isize,
    SQLITE_SAVEPOINT = ffi::SQLITE_SAVEPOINT as isize,
    SQLITE_COPY = ffi::SQLITE_COPY as isize,
    SQLITE_RECURSIVE = ffi::SQLITE_RECURSIVE as isize,
}

impl From<i32> for Action {
    fn from(code: i32) -> Action {
        match code {
            ffi::SQLITE_CREATE_INDEX => Action::SQLITE_CREATE_INDEX,
            ffi::SQLITE_CREATE_TABLE => Action::SQLITE_CREATE_TABLE,
            ffi::SQLITE_CREATE_TEMP_INDEX => Action::SQLITE_CREATE_TEMP_INDEX,
            ffi::SQLITE_CREATE_TEMP_TABLE => Action::SQLITE_CREATE_TEMP_TABLE,
            ffi::SQLITE_CREATE_TEMP_TRIGGER => Action::SQLITE_CREATE_TEMP_TRIGGER,
            ffi::SQLITE_CREATE_TEMP_VIEW => Action::SQLITE_CREATE_TEMP_VIEW,
            ffi::SQLITE_CREATE_TRIGGER => Action::SQLITE_CREATE_TRIGGER,
            ffi::SQLITE_CREATE_VIEW => Action::SQLITE_CREATE_VIEW,
            ffi::SQLITE_DELETE => Action::SQLITE_DELETE,
            ffi::SQLITE_DROP_INDEX => Action::SQLITE_DROP_INDEX,
            ffi::SQLITE_DROP_TABLE => Action::SQLITE_DROP_TABLE,
            ffi::SQLITE_DROP_TEMP_INDEX => Action::SQLITE_DROP_TEMP_INDEX,
            ffi::SQLITE_DROP_TEMP_TABLE => Action::SQLITE_DROP_TEMP_TABLE,
            ffi::SQLITE_DROP_TEMP_TRIGGER => Action::SQLITE_DROP_TEMP_TRIGGER,
            ffi::SQLITE_DROP_TEMP_VIEW => Action::SQLITE_DROP_TEMP_VIEW,
            ffi::SQLITE_DROP_TRIGGER => Action::SQLITE_DROP_TRIGGER,
            ffi::SQLITE_DROP_VIEW => Action::SQLITE_DROP_VIEW,
            ffi::SQLITE_INSERT => Action::SQLITE_INSERT,
            ffi::SQLITE_PRAGMA => Action::SQLITE_PRAGMA,
            ffi::SQLITE_READ => Action::SQLITE_READ,
            ffi::SQLITE_SELECT => Action::SQLITE_SELECT,
            ffi::SQLITE_TRANSACTION => Action::SQLITE_TRANSACTION,
            ffi::SQLITE_UPDATE => Action::SQLITE_UPDATE,
            ffi::SQLITE_ATTACH => Action::SQLITE_ATTACH,
            ffi::SQLITE_DETACH => Action::SQLITE_DETACH,
            ffi::SQLITE_ALTER_TABLE => Action::SQLITE_ALTER_TABLE,
            ffi::SQLITE_REINDEX => Action::SQLITE_REINDEX,
            ffi::SQLITE_ANALYZE => Action::SQLITE_ANALYZE,
            ffi::SQLITE_CREATE_VTABLE => Action::SQLITE_CREATE_VTABLE,
            ffi::SQLITE_DROP_VTABLE => Action::SQLITE_DROP_VTABLE,
            ffi::SQLITE_FUNCTION => Action::SQLITE_FUNCTION,
            ffi::SQLITE_SAVEPOINT => Action::SQLITE_SAVEPOINT,
            ffi::SQLITE_COPY => Action::SQLITE_COPY,
            ffi::SQLITE_RECURSIVE => Action::SQLITE_RECURSIVE,
            _ => Action::UNKNOWN,
        }
    }
}

impl Connection {
    pub fn update_hook<F>(&self, hook: Option<F>)
        where F: FnMut(Action, &str, &str, i64)
    {
        self.db.borrow_mut().update_hook(hook);
    }
}

impl InnerConnection {
    // TODO self.update_hook(None) must be called in InnerConnection#close to free any hook
    fn update_hook<F>(&mut self, hook: Option<F>)
        where F: FnMut(Action, &str, &str, i64)
    {
        unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void,
                                                   action_code: c_int,
                                                   db_str: *const c_char,
                                                   tbl_str: *const c_char,
                                                   row_id: i64)
            where F: FnMut(Action, &str, &str, i64)
        {
            use std::ffi::CStr;
            use std::str;

            let boxed_hook: *mut F = mem::transmute(p_arg);
            assert!(!boxed_hook.is_null(),
                    "Internal error - null function pointer");

            let action = Action::from(action_code);
            let db_name = {
                let c_slice = CStr::from_ptr(db_str).to_bytes();
                str::from_utf8_unchecked(c_slice)
            };
            let tbl_name = {
                let c_slice = CStr::from_ptr(tbl_str).to_bytes();
                str::from_utf8_unchecked(c_slice)
            };

            (*boxed_hook)(action, db_name, tbl_name, row_id);
        }

        let previous_hook = if let Some(hook) = hook {
            let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
            unsafe {
                ffi::sqlite3_update_hook(self.db(),
                                         Some(call_boxed_closure::<F>),
                                         mem::transmute(boxed_hook))
            }
        } else {
            unsafe { ffi::sqlite3_update_hook(self.db(), None, ptr::null_mut()) }
        };
        // TODO Validate: what happens if the previous hook has been set from C ?
        if !previous_hook.is_null() {
            // free_boxed_value
            unsafe {
                let _: Box<F> = Box::from_raw(mem::transmute(previous_hook));
            }
        }
    }
}
<commit_msg>SQLITE_RECURSIVE is not available with SQLite 3.6.8<commit_after>//! Data Change Notification Callbacks
#![allow(non_camel_case_types)]

use std::mem;
use std::ptr;
use std::os::raw::{c_int, c_char, c_void};

use ffi;

use {Connection, InnerConnection};

// Commit And Rollback Notification Callbacks
// http://sqlite.org/c3ref/commit_hook.html
/*
void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
*/

/// Authorizer Action Codes
pub enum Action {
    UNKNOWN = -1,
    SQLITE_CREATE_INDEX = ffi::SQLITE_CREATE_INDEX as isize,
    SQLITE_CREATE_TABLE = ffi::SQLITE_CREATE_TABLE as isize,
    SQLITE_CREATE_TEMP_INDEX = ffi::SQLITE_CREATE_TEMP_INDEX as isize,
    SQLITE_CREATE_TEMP_TABLE = ffi::SQLITE_CREATE_TEMP_TABLE as isize,
    SQLITE_CREATE_TEMP_TRIGGER = ffi::SQLITE_CREATE_TEMP_TRIGGER as isize,
    SQLITE_CREATE_TEMP_VIEW = ffi::SQLITE_CREATE_TEMP_VIEW as isize,
    SQLITE_CREATE_TRIGGER = ffi::SQLITE_CREATE_TRIGGER as isize,
    SQLITE_CREATE_VIEW = ffi::SQLITE_CREATE_VIEW as isize,
    SQLITE_DELETE = ffi::SQLITE_DELETE as isize,
    SQLITE_DROP_INDEX = ffi::SQLITE_DROP_INDEX as isize,
    SQLITE_DROP_TABLE = ffi::SQLITE_DROP_TABLE as isize,
    SQLITE_DROP_TEMP_INDEX = ffi::SQLITE_DROP_TEMP_INDEX as isize,
    SQLITE_DROP_TEMP_TABLE = ffi::SQLITE_DROP_TEMP_TABLE as isize,
    SQLITE_DROP_TEMP_TRIGGER = ffi::SQLITE_DROP_TEMP_TRIGGER as isize,
    SQLITE_DROP_TEMP_VIEW = ffi::SQLITE_DROP_TEMP_VIEW as isize,
    SQLITE_DROP_TRIGGER = ffi::SQLITE_DROP_TRIGGER as isize,
    SQLITE_DROP_VIEW = ffi::SQLITE_DROP_VIEW as isize,
    SQLITE_INSERT = ffi::SQLITE_INSERT as isize,
    SQLITE_PRAGMA = ffi::SQLITE_PRAGMA as isize,
    SQLITE_READ = ffi::SQLITE_READ as isize,
    SQLITE_SELECT = ffi::SQLITE_SELECT as isize,
    SQLITE_TRANSACTION = ffi::SQLITE_TRANSACTION as isize,
    SQLITE_UPDATE = ffi::SQLITE_UPDATE as isize,
    SQLITE_ATTACH = ffi::SQLITE_ATTACH as isize,
    SQLITE_DETACH = ffi::SQLITE_DETACH as isize,
    SQLITE_ALTER_TABLE = ffi::SQLITE_ALTER_TABLE as isize,
    SQLITE_REINDEX = ffi::SQLITE_REINDEX as isize,
    SQLITE_ANALYZE = ffi::SQLITE_ANALYZE as isize,
    SQLITE_CREATE_VTABLE = ffi::SQLITE_CREATE_VTABLE as isize,
    SQLITE_DROP_VTABLE = ffi::SQLITE_DROP_VTABLE as isize,
    SQLITE_FUNCTION = ffi::SQLITE_FUNCTION as isize,
    SQLITE_SAVEPOINT = ffi::SQLITE_SAVEPOINT as isize,
    SQLITE_COPY = ffi::SQLITE_COPY as isize,
    SQLITE_RECURSIVE = 33,
}

impl From<i32> for Action {
    fn from(code: i32) -> Action {
        match code {
            ffi::SQLITE_CREATE_INDEX => Action::SQLITE_CREATE_INDEX,
            ffi::SQLITE_CREATE_TABLE => Action::SQLITE_CREATE_TABLE,
            ffi::SQLITE_CREATE_TEMP_INDEX => Action::SQLITE_CREATE_TEMP_INDEX,
            ffi::SQLITE_CREATE_TEMP_TABLE => Action::SQLITE_CREATE_TEMP_TABLE,
            ffi::SQLITE_CREATE_TEMP_TRIGGER => Action::SQLITE_CREATE_TEMP_TRIGGER,
            ffi::SQLITE_CREATE_TEMP_VIEW => Action::SQLITE_CREATE_TEMP_VIEW,
            ffi::SQLITE_CREATE_TRIGGER => Action::SQLITE_CREATE_TRIGGER,
            ffi::SQLITE_CREATE_VIEW => Action::SQLITE_CREATE_VIEW,
            ffi::SQLITE_DELETE => Action::SQLITE_DELETE,
            ffi::SQLITE_DROP_INDEX => Action::SQLITE_DROP_INDEX,
            ffi::SQLITE_DROP_TABLE => Action::SQLITE_DROP_TABLE,
            ffi::SQLITE_DROP_TEMP_INDEX => Action::SQLITE_DROP_TEMP_INDEX,
            ffi::SQLITE_DROP_TEMP_TABLE => Action::SQLITE_DROP_TEMP_TABLE,
            ffi::SQLITE_DROP_TEMP_TRIGGER => Action::SQLITE_DROP_TEMP_TRIGGER,
            ffi::SQLITE_DROP_TEMP_VIEW => Action::SQLITE_DROP_TEMP_VIEW,
            ffi::SQLITE_DROP_TRIGGER => Action::SQLITE_DROP_TRIGGER,
            ffi::SQLITE_DROP_VIEW => Action::SQLITE_DROP_VIEW,
            ffi::SQLITE_INSERT => Action::SQLITE_INSERT,
            ffi::SQLITE_PRAGMA => Action::SQLITE_PRAGMA,
            ffi::SQLITE_READ => Action::SQLITE_READ,
            ffi::SQLITE_SELECT => Action::SQLITE_SELECT,
            ffi::SQLITE_TRANSACTION => Action::SQLITE_TRANSACTION,
            ffi::SQLITE_UPDATE => Action::SQLITE_UPDATE,
            ffi::SQLITE_ATTACH => Action::SQLITE_ATTACH,
            ffi::SQLITE_DETACH => Action::SQLITE_DETACH,
            ffi::SQLITE_ALTER_TABLE => Action::SQLITE_ALTER_TABLE,
            ffi::SQLITE_REINDEX => Action::SQLITE_REINDEX,
            ffi::SQLITE_ANALYZE => Action::SQLITE_ANALYZE,
            ffi::SQLITE_CREATE_VTABLE => Action::SQLITE_CREATE_VTABLE,
            ffi::SQLITE_DROP_VTABLE => Action::SQLITE_DROP_VTABLE,
            ffi::SQLITE_FUNCTION => Action::SQLITE_FUNCTION,
            ffi::SQLITE_SAVEPOINT => Action::SQLITE_SAVEPOINT,
            ffi::SQLITE_COPY => Action::SQLITE_COPY,
            ffi::SQLITE_RECURSIVE => Action::SQLITE_RECURSIVE,
            _ => Action::UNKNOWN,
        }
    }
}

impl Connection {
    pub fn update_hook<F>(&self, hook: Option<F>)
        where F: FnMut(Action, &str, &str, i64)
    {
        self.db.borrow_mut().update_hook(hook);
    }
}

impl InnerConnection {
    // TODO self.update_hook(None) must be called in InnerConnection#close to free any hook
    fn update_hook<F>(&mut self, hook: Option<F>)
        where F: FnMut(Action, &str, &str, i64)
    {
        unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void,
                                                   action_code: c_int,
                                                   db_str: *const c_char,
                                                   tbl_str: *const c_char,
                                                   row_id: i64)
            where F: FnMut(Action, &str, &str, i64)
        {
            use std::ffi::CStr;
            use std::str;

            let boxed_hook: *mut F = mem::transmute(p_arg);
            assert!(!boxed_hook.is_null(),
                    "Internal error - null function pointer");

            let action = Action::from(action_code);
            let db_name = {
                let c_slice = CStr::from_ptr(db_str).to_bytes();
                str::from_utf8_unchecked(c_slice)
            };
            let tbl_name = {
                let c_slice = CStr::from_ptr(tbl_str).to_bytes();
                str::from_utf8_unchecked(c_slice)
            };

            (*boxed_hook)(action, db_name, tbl_name, row_id);
        }

        let previous_hook = if let Some(hook) = hook {
            let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
            unsafe {
                ffi::sqlite3_update_hook(self.db(),
                                         Some(call_boxed_closure::<F>),
                                         mem::transmute(boxed_hook))
            }
        } else {
            unsafe { ffi::sqlite3_update_hook(self.db(), None, ptr::null_mut()) }
        };
        // TODO Validate: what happens if the previous hook has been set from C ?
        if !previous_hook.is_null() {
            // free_boxed_value
            unsafe {
                let _: Box<F> = Box::from_raw(mem::transmute(previous_hook));
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;
use rand::Rng;
use std::cmp::{Ord, Ordering};
use std::fmt;

// comands in config-file start with
pub const CONF_LINE_IDENTIFIER__CONTROL: char = ':';

// the default can be changed by one of the following switches
const DEFAULT_REWARDING_SCHEME: RewardingScheme = RewardingScheme::UnhideWhenGuessedChar;

// Keyword to switch rewarding scheme
// :traditional-rewarding
const UNHIDE_WHEN_LOST_LIVE_IDENTIFIER: &'static str = "traditional-rewarding";

// Keyword to switch rewarding scheme
// :success-rewarding
const UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER: &'static str = "success-rewarding";

// images in config file start with
pub const CONF_LINE_IDENTIFIER__IMAGE: char = '|';

const BIG_IMAGE: usize = 100; // sort algorithm <-> random algorithm

// first char of image lines must be '|'
const DEFAULT_IMAGES: &'static [&'static str] = &[
    "
|    ,,,,,
|   (o   o)
|    /. .\\
|   (_____)
|     : :
|    ##O##'
|  ,,,: :,,,
| _)\\ : : /(____
|{  \\     /  ___}
| \\/)     ((/
|  (_______)
|    :   :
|    :   :
|   / \\ / \\
|   \"\"\" \"\"\"
",
    "
|    |\\_|X|_/|
|   /         \\
| =(  O     O  )=
|  -\\    o    /-
|   / .-----. \\
| /_ | o   o |_ \\
|(U  |       |  U)
|   _|_     _|_
|  (   )---(   )
",
    "
|        _.---._    /\\\\
|     ./'       \"--`\\//
|   ./              o \\
|  /./\\  )______   \\__ \\
| ./  / /\\ \\   | \\ \\  \\ \\
|    / /  \\ \\  | |\\ \\  \\7
|     \"     \"    \"  \"        VK
",
    "
|       ,.
|      (_|,.
|     ,' /, )_______   _
|  __j o``-'        `.'-)'
| (\")                 \\'
|  `-j                |
|    `-._(           /
|       |_\\  |--^.  /
|      /_]'|_| /_)_/
|         /_]'  /_]'
# Author: hjw
",
    "
|        _
|       [ ]
|      (   )
|       |>|
|    __/===\\__
|   //| o=o |\\\\
| <]  | o=o |  [>
|     \\=====/
|    / / | \\ \\
|   <_________>
",
    "
|                          (_)(_)
|                          /     \\
|                         /       |
|                        /   \\  * |
|          ________     /    /\\__/
|  _      /        \\   /    /
| / \\    /  ____    \\_/    /
|//\\ \\  /  /    \\         /
|V  \\ \\/  /      \\       /
|    \\___/        \\_____/
",
    "
|         .-.
|        (. .)__,')
|        / V      )
|  ()    \\  (   \\/
|<)-`\\()  `._`._ \\
|  <).>=====<<==`'====
|   C-'`(>
# Author: hjw
",
    "
| >(. )
|  |  (     /)
|  |   \\___/ )
|  (   ----- )  >@)_//   >@)_//  >@)_//  >@)_//
|   \\_______/    (__)     (__)    (__)    (__)
|~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
",
    "
|           __
|           /(`o
|     ,-,  //  \\\\
|    (,,,) ||   V
|   (,,,,)\\//
|   (,,,/w)-'
|   \\,,/w)
|   `V/uu
|     / |
|     | |
|     o o
|     \\ |
|\\,/  ,\\|,.  \\,/
",
    "
|o
| \\_/\\o
|( Oo)                    \\|/
|(_=-)  .===O-  ~~Z~A~P~~ -O-
|/   \\_/U'                /|\\
|||  |_/
|\\\\  |
|{K ||
| | PP
| | ||
| (__\\\\
# Author: ac
",
    "
|      ______
|     /     /\\
|    /     /  \\
|   /_____/----\\_    (
|  \"     \"          ).
| _ ___          o (:') o
|(@))_))        o ~/~~\\~ o
|                o  o  o
",
    "
|                             _______     |\\
|                            |License|    | \\
|  _____                     | ~~*~~ |    |  \\
| |     |  (((        .--.   |_______|    |
| |DrJRO| ~OvO~ __   (////)               |
| |     | ( _ )|==|   \\__/                |
| |o    |  \\_/ |_(|  /    \\   _______     |
| |     | //|\\\\   \\\\//|  |\\\\  |__o__|     |
| |   __|//\\_/\\\\ __\\/ |__|//  |__o__|     |
| |  |==\"\"//=\\\\\"\"====|||||)   |__o__|     |
|_|__||_|_||_||_____||||||____|__o__|_____|
|    ||  (_) (_)    ||||||                \\
|    []             [(_)(_)
",
];

#[derive(PartialOrd, Eq, PartialEq, Debug, Copy, Clone)] //omitting Ord
pub struct ImageChar {
    pub point: (u8, u8),
    pub char_: char,
}

impl fmt::Display for ImageChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.char_)
    }
}

// Ord enables us to v.sort()
impl Ord for ImageChar {
    fn cmp(&self, other: &Self) -> Ordering {
        fn weight(ic: &ImageChar) -> isize {
            let &ImageChar {
                point: (x, y),
                char_: _,
            } = ic;
            // points near the lower left corner are light
            x as isize - y as isize
        }
        weight(&self).cmp(&weight(&other))
    }
}

#[derive(Debug, PartialEq)]
pub enum RewardingScheme {
    UnhideWhenLostLife,
    UnhideWhenGuessedChar,
}

#[derive(Debug)]
pub struct Image {
    pub ichars: Vec<ImageChar>,
    pub offset: (usize, usize),
    pub dimension: (u8, u8),
    pub visible_points: usize,
    pub rewarding_scheme: RewardingScheme,
}

impl fmt::Display for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut s = String::new();
        for ic in self.ichars.iter().take(self.visible_points) {
            let &ImageChar {
                point: (x, y),
                char_: c,
            } = ic;
            s = s
                + "\x1b["
                + &(y as usize + 1 + self.offset.1).to_string()
                + ";"
                + &(x as usize + 1 + self.offset.0).to_string()
                + "f"
                + &c.to_string();
        }
        // after printing the image s, bring the cursor below
        write!(
            f,
            "{}\x1b[{};0f",
            s,
            (self.dimension.1 as usize + 1 + self.offset.1).to_string()
        )
    }
}

impl Image {
    pub fn new(string: &str, offset: (usize, usize)) -> Image {
        let mut v: Vec<ImageChar> = Vec::new();

        let mut rewarding_scheme: RewardingScheme = DEFAULT_REWARDING_SCHEME;
        for (y, line) in string
            // split in lines
            .lines()
            // interpret identifier line
            .filter_map(|l| {
                if l.starts_with(CONF_LINE_IDENTIFIER__CONTROL) {
                    if l[1..].trim().contains(UNHIDE_WHEN_LOST_LIVE_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenLostLife;
                    }
                    if l[1..].trim().contains(UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenGuessedChar;
                    }
                    None
                } else {
                    Some(l)
                }
            })
            // consider only lines starting with '|'
            .filter(|&l| l.starts_with(CONF_LINE_IDENTIFIER__IMAGE))
            .enumerate()
        //.inspect(|&(n,l)| println!("line {:?}: {:?} ", n,l))
        {
            let mut ii: Vec<_> = line
                .char_indices()
                // skip first char '|'
                .skip(1)
                // consider only chars != ' '
                .filter(|&(_, c)| c != ' ')
                // save in ImageChar object
                .map(|(x, c)| ImageChar {
                    point: (x as u8, y as u8),
                    char_: c,
                })
                .collect();
            v.append(&mut ii);
        }

        // find dimensions
        let mut xmax = 0;
        let mut ymax = 0;
        for i in v.iter() {
            let &ImageChar {
                point: (x, y),
                char_: _,
            } = i;
            if x > xmax {
                xmax = x
            };
            if y > ymax {
                ymax = y
            };
        }

        // order points
        let v_len = v.len();
        if v_len <= BIG_IMAGE {
            v.sort(); // Sort algorithm, see "impl Ord for ImageChar"
        } else {
            rand::thread_rng().shuffle(&mut v); // points appear randomly.
        }

        if v.len() == 0 {
            Image::new(rand::thread_rng().choose(&DEFAULT_IMAGES).unwrap(), offset)
        } else {
            Image {
                ichars: v,
                offset: offset,
                dimension: (xmax, ymax),
                visible_points: v_len,
                rewarding_scheme: rewarding_scheme,
            }
        }
    }

    pub fn disclose(&mut self, lives_frac: (usize, usize), guessed_chars_frac: (usize, usize)) {
        let l = self.ichars.len();

        let as_points = |(n, d)| (3 * l * (d - n) as usize / d as usize + 1 * l) / 4;

        self.visible_points = match self.rewarding_scheme {
            RewardingScheme::UnhideWhenGuessedChar => as_points(guessed_chars_frac),
            RewardingScheme::UnhideWhenLostLife => as_points(lives_frac),
        };
    }
}
<commit_msg>separate the discriminating character by an underscore<commit_after>extern crate rand;
use rand::Rng;
use std::cmp::{Ord, Ordering};
use std::fmt;

// comands in config-file start with
pub const CONF_LINE_IDENTIFIER__CONTROL: char = ':';

// the default can be changed by one of the following switches
const DEFAULT_REWARDING_SCHEME: RewardingScheme = RewardingScheme::UnhideWhenGuessedChar;

// Keyword to switch rewarding scheme
// :traditional-rewarding
const UNHIDE_WHEN_LOST_LIVE_IDENTIFIER: &'static str = "traditional-rewarding";

// Keyword to switch rewarding scheme
// :success-rewarding
const UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER: &'static str = "success-rewarding";

// images in config file start with
pub const CONF_LINE_IDENTIFIER__IMAGE: char = '|';

const BIG_IMAGE: usize = 100; // sort algorithm <-> random algorithm

// first char of image lines must be '|'
const DEFAULT_IMAGES: &'static [&'static str] = &[
    "
|    ,,,,,
|   (o   o)
|    /. .\\
|   (_____)
|     : :
|    ##O##'
|  ,,,: :,,,
| _)\\ : : /(____
|{  \\     /  ___}
| \\/)     ((/
|  (_______)
|    :   :
|    :   :
|   / \\ / \\
|   \"\"\" \"\"\"
",
    "
|    |\\_|X|_/|
|   /         \\
| =(  O     O  )=
|  -\\    o    /-
|   / .-----. \\
| /_ | o   o |_ \\
|(U  |       |  U)
|   _|_     _|_
|  (   )---(   )
",
    "
|        _.---._    /\\\\
|     ./'       \"--`\\//
|   ./              o \\
|  /./\\  )______   \\__ \\
| ./  / /\\ \\   | \\ \\  \\ \\
|    / /  \\ \\  | |\\ \\  \\7
|     \"     \"    \"  \"        VK
",
    "
|       ,.
|      (_|,.
|     ,' /, )_______   _
|  __j o``-'        `.'-)'
| (\")                 \\'
|  `-j                |
|    `-._(           /
|       |_\\  |--^.  /
|      /_]'|_| /_)_/
|         /_]'  /_]'
# Author: hjw
",
    "
|        _
|       [ ]
|      (   )
|       |>|
|    __/===\\__
|   //| o=o |\\\\
| <]  | o=o |  [>
|     \\=====/
|    / / | \\ \\
|   <_________>
",
    "
|                          (_)(_)
|                          /     \\
|                         /       |
|                        /   \\  * |
|          ________     /    /\\__/
|  _      /        \\   /    /
| / \\    /  ____    \\_/    /
|//\\ \\  /  /    \\         /
|V  \\ \\/  /      \\       /
|    \\___/        \\_____/
",
    "
|         .-.
|        (. .)__,')
|        / V      )
|  ()    \\  (   \\/
|<)-`\\()  `._`._ \\
|  <).>=====<<==`'====
|   C-'`(>
# Author: hjw
",
    "
| >(. )
|  |  (     /)
|  |   \\___/ )
|  (   ----- )  >@)_//   >@)_//  >@)_//  >@)_//
|   \\_______/    (__)     (__)    (__)    (__)
|~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
",
    "
|           __
|           /(`o
|     ,-,  //  \\\\
|    (,,,) ||   V
|   (,,,,)\\//
|   (,,,/w)-'
|   \\,,/w)
|   `V/uu
|     / |
|     | |
|     o o
|     \\ |
|\\,/  ,\\|,.  \\,/
",
    "
|o
| \\_/\\o
|( Oo)                    \\|/
|(_=-)  .===O-  ~~Z~A~P~~ -O-
|/   \\_/U'                /|\\
|||  |_/
|\\\\  |
|{K ||
| | PP
| | ||
| (__\\\\
# Author: ac
",
    "
|      ______
|     /     /\\
|    /     /  \\
|   /_____/----\\_    (
|  \"     \"          ).
| _ ___          o (:') o
|(@))_))        o ~/~~\\~ o
|                o  o  o
",
    "
|                             _______     |\\
|                            |License|    | \\
|  _____                     | ~~*~~ |    |  \\
| |     |  (((        .--.   |_______|    |
| |DrJRO| ~OvO~ __   (////)               |
| |     | ( _ )|==|   \\__/                |
| |o    |  \\_/ |_(|  /    \\   _______     |
| |     | //|\\\\   \\\\//|  |\\\\  |__o__|     |
| |   __|//\\_/\\\\ __\\/ |__|//  |__o__|     |
| |  |==\"\"//=\\\\\"\"====|||||)   |__o__|     |
|_|__||_|_||_||_____||||||____|__o__|_____|
|    ||  (_) (_)    ||||||                \\
|    []             [(_)(_)
",
];

#[derive(PartialOrd, Eq, PartialEq, Debug, Copy, Clone)] //omitting Ord
pub struct ImageChar {
    pub point: (u8, u8),
    pub char_: char,
}

impl fmt::Display for ImageChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.char_)
    }
}

// Ord enables us to v.sort()
impl Ord for ImageChar {
    fn cmp(&self, other: &Self) -> Ordering {
        fn weight(ic: &ImageChar) -> isize {
            let &ImageChar {
                point: (x, y),
                char_: _,
            } = ic;
            // points near the lower left corner are light
            x as isize - y as isize
        }
        weight(&self).cmp(&weight(&other))
    }
}

#[derive(Debug, PartialEq)]
pub enum RewardingScheme {
    UnhideWhenLostLife,
    UnhideWhenGuessedChar,
}

#[derive(Debug)]
pub struct Image {
    pub ichars: Vec<ImageChar>,
    pub offset: (usize, usize),
    pub dimension: (u8, u8),
    pub visible_points: usize,
    pub rewarding_scheme: RewardingScheme,
}

impl fmt::Display for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut s = String::new();
        for ic in self.ichars.iter().take(self.visible_points) {
            let &ImageChar {
                point: (x, y),
                char_: c,
            } = ic;
            s = s
                + "\x1b["
                + &(y as usize + 1 + self.offset.1).to_string()
                + ";"
                + &(x as usize + 1 + self.offset.0).to_string()
                + "f"
                + &c.to_string();
        }
        // after printing the image s, bring the cursor below
        write!(
            f,
            "{}\x1b[{};0f",
            s,
            (self.dimension.1 as usize + 1 + self.offset.1).to_string()
        )
    }
}

impl Image {
    pub fn new(string: &str, offset: (usize, usize)) -> Image {
        let mut v: Vec<ImageChar> = Vec::new();

        let mut rewarding_scheme: RewardingScheme = DEFAULT_REWARDING_SCHEME;
        for (y, line) in string
            // split in lines
            .lines()
            // interpret identifier line
            .filter_map(|l| {
                if l.starts_with(CONF_LINE_IDENTIFIER__CONTROL) {
                    if l[1..].trim().contains(UNHIDE_WHEN_LOST_LIVE_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenLostLife;
                    }
                    if l[1..].trim().contains(UNHIDE_WHEN_GUESSED_CHAR_IDENTIFIER) {
                        rewarding_scheme = RewardingScheme::UnhideWhenGuessedChar;
                    }
                    None
                } else {
                    Some(l)
                }
            })
            // consider only lines starting with '|'
            .filter(|&l| l.starts_with(CONF_LINE_IDENTIFIER__IMAGE))
            .enumerate()
        //.inspect(|&(n,l)| println!("line {:?}: {:?} ", n,l))
        {
            let mut ii: Vec<_> = line
                .char_indices()
                // skip first char '|'
                .skip(1)
                // consider only chars != ' '
                .filter(|&(_, c)| c != ' ')
                // save in ImageChar object
                .map(|(x, c)| ImageChar {
                    point: (x as u8, y as u8),
                    char_: c,
                })
                .collect();
            v.append(&mut ii);
        }

        // find dimensions
        let mut x_max = 0;
        let mut y_max = 0;
        for i in v.iter() {
            let &ImageChar {
                point: (x, y),
                char_: _,
            } = i;
            if x > x_max {
                x_max = x
            };
            if y > y_max {
                y_max = y
            };
        }

        // order points
        let v_len = v.len();
        if v_len <= BIG_IMAGE {
            v.sort(); // Sort algorithm, see "impl Ord for ImageChar"
        } else {
            rand::thread_rng().shuffle(&mut v); // points appear randomly.
        }

        if v.len() == 0 {
            Image::new(rand::thread_rng().choose(&DEFAULT_IMAGES).unwrap(), offset)
        } else {
            Image {
                ichars: v,
                offset: offset,
                dimension: (x_max,y_maxx),
                visible_points: v_len,
                rewarding_scheme: rewarding_scheme,
            }
        }
    }

    pub fn disclose(&mut self, lives_frac: (usize, usize), guessed_chars_frac: (usize, usize)) {
        let l = self.ichars.len();

        let as_points = |(n, d)| (3 * l * (d - n) as usize / d as usize + 1 * l) / 4;

        self.visible_points = match self.rewarding_scheme {
            RewardingScheme::UnhideWhenGuessedChar => as_points(guessed_chars_frac),
            RewardingScheme::UnhideWhenLostLife => as_points(lives_frac),
        };
    }
}
<|endoftext|>


<commit_before>use scanner::Scanner;
use regex::Regex;

const COMPARISON           : &'static str = r"^(==|!=|<>|<=?|>=?|contains)";
const SINGLE_STRING_LITERAL: &'static str = r"^'[^']*'";
const DOUBLE_STRING_LITERAL: &'static str = r#"^"[^"]*""#;
const NUMBER_LITERAL:        &'static str = r"^-?\d+(\.\d+)?";
const IDENTIFIER:            &'static str = r"^[a-zA-Z_][\w-]*\??";
const RANGE_OP:              &'static str = r"^\.\.";

#[derive(Debug, PartialEq)]
pub enum Token {
    Comparison(String),
    EndOfString,
    Identifier(String),
    Number(String),
    Range,
    String(String),
}

pub struct Tokens<'t> {
    scanner: &'t Scanner<'t>
}

impl<'t> Tokens<'t> {
    fn token_for(&self, pattern: &Regex, value: &str) -> Token {
        match pattern.as_str() {
            COMPARISON => Token::Comparison(value.to_string()),
            SINGLE_STRING_LITERAL => Token::String(value.to_string()),
            DOUBLE_STRING_LITERAL => Token::String(value.to_string()),
            NUMBER_LITERAL => Token::Number(value.to_string()),
            IDENTIFIER => Token::Identifier(value.to_string()),
            RANGE_OP => Token::Range,
            _ => Token::EndOfString
        }
    }
}

impl<'t> Iterator for Tokens<'t> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        let matchers = vec![
            Regex::new(COMPARISON).unwrap(),
            Regex::new(SINGLE_STRING_LITERAL).unwrap(),
            Regex::new(DOUBLE_STRING_LITERAL).unwrap(),
            Regex::new(NUMBER_LITERAL).unwrap(),
            Regex::new(IDENTIFIER).unwrap(),
            Regex::new(RANGE_OP).unwrap()
        ];

        match matchers.iter().find(|&m| self.scanner.check(m)) {
            None => None,
            Some(regex) => {
                let value = self.scanner.scan(&regex).unwrap();
                Some(self.token_for(&regex, &value))
            }
        }
    }
}

pub struct Lexer<'t> {
    scanner: Scanner<'t>
}

impl<'t> Lexer<'t> {
    pub fn new<'a>(source: &'a str) -> Lexer<'a> {
        Lexer { scanner: Scanner::new(source) }
    }

    pub fn tokens(&self) -> Tokens {
        Tokens { scanner: &self.scanner }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_creates_a_new_instance() {
        let lexer = Lexer::new("doSomthing | filter");
        assert_eq!("doSomthing | filter", lexer.scanner.rest().unwrap());
    }

    #[test]
    fn tokens_when_given_a_blank_string() {
        let lexer              = Lexer::new("");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_when_given_a_whitespace_only_string() {
        let lexer              = Lexer::new("  \t \n\r ");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_parses_identifiers() {
        let lexer              = Lexer::new("high five?");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(Token::Identifier("high".to_string()), tokens[0]);
        assert_eq!(Token::Identifier("five?".to_string()), tokens[1]);
    }

    #[test]
    fn tokens_knows_that_identifiers_dont_start_with_numbers() {
        let lexer              = Lexer::new("2foo 5.0bar");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(4, tokens.len());
        assert_eq!(Token::Number("2".to_string()), tokens[0]);
        assert_eq!(Token::Identifier("foo".to_string()), tokens[1]);
        assert_eq!(Token::Number("5.0".to_string()), tokens[2]);
        assert_eq!(Token::Identifier("bar".to_string()), tokens[3]);
    }

    #[test]
    fn tokens_parses_string_literals() {
        let lexer              = Lexer::new(r#" 'this is a test""' "wat 'lol'" "#);
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(Token::String(r#"'this is a test""'"#.to_string()), tokens[0]);
        assert_eq!(Token::String(r#""wat 'lol'""#.to_string()), tokens[1]);
    }

    #[test]
    fn tokens_parses_integers() {
        let lexer              = Lexer::new("hi 50");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(Token::Identifier("hi".to_string()), tokens[0]);
        assert_eq!(Token::Number("50".to_string()), tokens[1]);
    }

    #[test]
    fn tokens_parses_floats() {
        let lexer              = Lexer::new("hi 5.0");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(Token::Identifier("hi".to_string()), tokens[0]);
        assert_eq!(Token::Number("5.0".to_string()), tokens[1]);
    }

    #[test]
    fn tokens_parses_comparisons() {
        let lexer              = Lexer::new("== <> contains");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(3, tokens.len());
        assert_eq!(Token::Comparison("==".to_string()), tokens[0]);
        assert_eq!(Token::Comparison("<>".to_string()), tokens[1]);
        assert_eq!(Token::Comparison("contains".to_string()), tokens[2]);
    }

    #[test]
    fn tokens_parses_range_operator() {
        let lexer              = Lexer::new("1..10");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(3, tokens.len());
        assert_eq!(Token::Number("1".to_string()), tokens[0]);
        assert_eq!(Token::Range, tokens[1]);
        assert_eq!(Token::Number("10".to_string()), tokens[2]);
    }
}
<commit_msg>using a token! marco to accept &str and String for enum<commit_after>use scanner::Scanner;
use regex::Regex;

const COMPARISON           : &'static str = r"^(==|!=|<>|<=?|>=?|contains)";
const SINGLE_STRING_LITERAL: &'static str = r"^'[^']*'";
const DOUBLE_STRING_LITERAL: &'static str = r#"^"[^"]*""#;
const NUMBER_LITERAL:        &'static str = r"^-?\d+(\.\d+)?";
const IDENTIFIER:            &'static str = r"^[a-zA-Z_][\w-]*\??";
const RANGE_OP:              &'static str = r"^\.\.";

#[derive(Debug, PartialEq)]
pub enum Token {
    Comparison(String),
    Identifier(String),
    Number(String),
    String(String),
    Range,
    EndOfString,
}

macro_rules! token {
    (Comparison => $e:expr) => (Token::Comparison(String::from($e)));
    (Identifier => $e:expr) => (Token::Identifier(String::from($e)));
    (Number => $e:expr) => (Token::Number(String::from($e)));
    (String => $e:expr) => (Token::String(String::from($e)));
    (Range) => (Token::Range);
    (EndOfString) => (Token::EndOfString);
}

pub struct Tokens<'t> {
    scanner: &'t Scanner<'t>
}

impl<'t> Tokens<'t> {
    fn token_for(&self, pattern: &Regex, value: &str) -> Token {
        match pattern.as_str() {
            COMPARISON            => token!(Comparison => value),
            SINGLE_STRING_LITERAL => token!(String => value),
            DOUBLE_STRING_LITERAL => token!(String => value),
            NUMBER_LITERAL        => token!(Number => value),
            IDENTIFIER            => token!(Identifier => value),
            RANGE_OP              => token!(Range),
            _                     => token!(EndOfString)
        }
    }
}

impl<'t> Iterator for Tokens<'t> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        let matchers = vec![
            Regex::new(COMPARISON).unwrap(),
            Regex::new(SINGLE_STRING_LITERAL).unwrap(),
            Regex::new(DOUBLE_STRING_LITERAL).unwrap(),
            Regex::new(NUMBER_LITERAL).unwrap(),
            Regex::new(IDENTIFIER).unwrap(),
            Regex::new(RANGE_OP).unwrap()
        ];

        match matchers.iter().find(|&m| self.scanner.check(m)) {
            None => None,
            Some(regex) => {
                let value = self.scanner.scan(&regex).unwrap();
                Some(self.token_for(&regex, &value))
            }
        }
    }
}

pub struct Lexer<'t> {
    scanner: Scanner<'t>
}

impl<'t> Lexer<'t> {
    pub fn new<'a>(source: &'a str) -> Lexer<'a> {
        Lexer { scanner: Scanner::new(source) }
    }

    pub fn tokens(&self) -> Tokens {
        Tokens { scanner: &self.scanner }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_creates_a_new_instance() {
        let lexer = Lexer::new("doSomthing | filter");
        assert_eq!("doSomthing | filter", lexer.scanner.rest().unwrap());
    }

    #[test]
    fn tokens_when_given_a_blank_string() {
        let lexer              = Lexer::new("");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_when_given_a_whitespace_only_string() {
        let lexer              = Lexer::new("  \t \n\r ");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(0, tokens.len());
    }

    #[test]
    fn tokens_parses_identifiers() {
        let lexer              = Lexer::new("high five?");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(token!(Identifier => "high"), tokens[0]);
        assert_eq!(token!(Identifier => "five?"), tokens[1]);
    }

    #[test]
    fn tokens_knows_that_identifiers_dont_start_with_numbers() {
        let lexer              = Lexer::new("2foo 5.0bar");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(4, tokens.len());
        assert_eq!(token!(Number => "2"), tokens[0]);
        assert_eq!(token!(Identifier => "foo"), tokens[1]);
        assert_eq!(token!(Number => "5.0"), tokens[2]);
        assert_eq!(token!(Identifier => "bar"), tokens[3]);
    }

    #[test]
    fn tokens_parses_string_literals() {
        let lexer              = Lexer::new(r#" 'this is a test""' "wat 'lol'" "#);
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(token!(String => r#"'this is a test""'"#), tokens[0]);
        assert_eq!(token!(String => r#""wat 'lol'""#), tokens[1]);
    }

    #[test]
    fn tokens_parses_integers() {
        let lexer              = Lexer::new("hi 50");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(token!(Identifier => "hi"), tokens[0]);
        assert_eq!(token!(Number => "50"), tokens[1]);
    }

    #[test]
    fn tokens_parses_floats() {
        let lexer              = Lexer::new("hi 5.0");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(2, tokens.len());
        assert_eq!(token!(Identifier => "hi"), tokens[0]);
        assert_eq!(token!(Number => "5.0"), tokens[1]);
    }

    #[test]
    fn tokens_parses_comparisons() {
        let lexer              = Lexer::new("== <> contains");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(3, tokens.len());
        assert_eq!(token!(Comparison => "=="), tokens[0]);
        assert_eq!(token!(Comparison => "<>"), tokens[1]);
        assert_eq!(token!(Comparison => "contains"), tokens[2]);
    }

    #[test]
    fn tokens_parses_range_operator() {
        let lexer              = Lexer::new("1..10");
        let tokens: Vec<Token> = lexer.tokens().collect();

        assert_eq!(3, tokens.len());
        assert_eq!(token!(Number => "1"), tokens[0]);
        assert_eq!(token!(Range), tokens[1]);
        assert_eq!(token!(Number => "10"), tokens[2]);
    }
}
<|endoftext|>