<commit_before>use std::old_io::{File,Writer,IoError, Truncate, Write};

use SMF;
use ::{Event,AbsoluteEvent,MetaCommand,SMFFormat};

/// An SMFWriter is used to write an SMF to a file.  It can be either
/// constructed empty and have tracks added, or created from an
/// existing rimd::SMF.
///
/// # Writing an existing SMF to a file
/// ```
/// use rimd::{SMF,SMFWriter};
/// // Create smf
/// // let writer = SMFWriter::from_smf(smf);
/// // let result = writer.write_to_file(Path::new("/path/to/file.smf"));
/// // handle result
pub struct SMFWriter {
    format: u16,
    ticks: i16,
    tracks: Vec<Vec<u8>>,
}

impl SMFWriter {

    /// Create a new SMFWriter with the given number of units per
    /// beat.  The SMFWriter will initially have no tracks.
    pub fn new_with_division(ticks: i16) -> SMFWriter {
        SMFWriter {
            format: 1,
            ticks: ticks,
            tracks: Vec::new(),
        }
    }

    /// Create a new SMFWriter with the given format and number of
    /// units per beat.  The SMFWriter will initially have no tracks.
    pub fn new_with_division_and_format(format: SMFFormat, ticks: i16) -> SMFWriter {
        SMFWriter {
            format: format as u16,
            ticks: ticks,
            tracks: Vec::new(),
        }
    }

    /// Create a writer that has all the tracks from the given SMF already added
    pub fn from_smf(smf: SMF) -> SMFWriter {
        let mut writer = SMFWriter::new_with_division_and_format
            (smf.format, smf.division);

        let mut length = 0;
        let mut saw_eot = false;

        for track in smf.tracks.iter() {
            let mut vec = Vec::new();
            writer.start_track_header(&mut vec);

            for event in track.events.iter() {
                length += SMFWriter::write_vtime(event.vtime as u64,&mut vec).unwrap(); // TODO: Handle error
                writer.write_event(&mut vec, &(event.event) , &mut length, &mut saw_eot);
            }

            writer.finish_track_write(&mut vec, &mut length, saw_eot);
            writer.tracks.push(vec);
        }

        writer
    }

    // Write a variable length value.  Return number of bytes written.
    fn write_vtime(val: u64, writer: &mut Writer) -> Result<u32,IoError> {
        let mut storage = Vec::new();
        let mut cur = val;
        let mut written = 0;
        let cont_mask = 0x80 as u8;
        let val_mask = 0x7F as u64;
        loop {
            let mut to_write = (cur & val_mask) as u8;
            cur = cur >> 7;
            if written != 0 {
                // we're writing a continuation byte, so set the bit
                to_write |= cont_mask;
            }
            storage.push(to_write);
            written += 1;
            if cur == 0 { break; }
        }
        for b in storage.iter().rev() {
            try!(writer.write_u8(*b));
        }
        Ok(written)
    }

    fn start_track_header(&self, vec: &mut Vec<u8>) {
        vec.push(0x4D);
        vec.push(0x54);
        vec.push(0x72);
        vec.push(0x6B);
        // reserve space for track len
        vec.push(0);
        vec.push(0);
        vec.push(0);
        vec.push(0);
    }

    fn write_event(&self, vec: &mut Vec<u8>, event: &Event, length: &mut u32, saw_eot: &mut bool) {
        match event {
            &Event::Midi(ref midi) => {
                vec.push_all(midi.data.as_slice());
                *length += midi.data.len() as u32;
            }
            &Event::Meta(ref meta) => {
                vec.push(0xff); // indicate we're writing a meta event
                vec.push(meta.command as u8);
                // +2 on next line for the 0xff and the command byte we just wrote
                *length += SMFWriter::write_vtime(meta.length,vec).unwrap() + 2;
                vec.push_all(meta.data.as_slice());
                *length += meta.data.len() as u32;
                if meta.command == MetaCommand::EndOfTrack {
                    *saw_eot = true;
                }
            }
        }
    }

    fn finish_track_write(&self, vec: &mut Vec<u8>, length: &mut u32, saw_eot: bool) {
        if !saw_eot {
            // no end of track marker in passed data, add one
            *length += SMFWriter::write_vtime(0,vec).unwrap() + 1;
            vec.push(MetaCommand::EndOfTrack as u8);
            *length += SMFWriter::write_vtime(0,vec).unwrap() + 1; // write length of meta command: 0
        }

        // write in the length in the space we reserved
        for i in (0..4) {
            let lbyte = (*length & 0xFF) as u8;
            // 7-i because smf is big endian and we want to put this in bytes 4-7
            vec[7-i] = lbyte;
            *length = (*length)>>8;
        }
    }

    /// Add any sequence of AbsoluteEvents as a track to this writer
    pub fn add_track<'a,I>(&mut self, track: I) where I: Iterator<Item=&'a AbsoluteEvent> {
        let mut vec = Vec::new();

        self.start_track_header(&mut vec);

        let mut length = 0;
        let mut cur_time: u64 = 0;
        let mut saw_eot = false;

        for ev in track {
            let vtime = ev.get_time() - cur_time;
            cur_time = vtime;
            length += SMFWriter::write_vtime(vtime as u64,&mut vec).unwrap(); // TODO: Handle error
            self.write_event(&mut vec, ev.get_event(), &mut length, &mut saw_eot);
        }

        self.finish_track_write(&mut vec, &mut length, saw_eot);

        self.tracks.push(vec);
    }

    // actual writing stuff below

    fn write_header(&self, writer: &mut Writer) -> Result<(),IoError> {
        try!(writer.write_all(&[0x4D,0x54,0x68,0x64]));
        try!(writer.write_be_u32(6));
        try!(writer.write_be_u16(self.format));
        try!(writer.write_be_u16(self.tracks.len() as u16));
        try!(writer.write_be_i16(self.ticks));
        Ok(())
    }

    /// Write out all the tracks that have been added to this
    /// SMFWriter to the passed writer
    pub fn write_all(self, writer: &mut Writer) -> Result<(),IoError> {
        try!(self.write_header(writer));
        for track in self.tracks.into_iter() {
            try!(writer.write_all(track.as_slice()));
        }
        Ok(())
    }

    /// Write out the result of the tracks that have been added to a
    /// file.
    /// Warning: This will overwrite an existing file
    pub fn write_to_file(self, path: &Path) -> Result<(),IoError> {
        let mut file = try!(File::open_mode(path,Truncate,Write));
        self.write_all(&mut file)
    }

}

#[test]
fn vwrite() {
    let mut vec1 = Vec::new();
    SMFWriter::write_vtime(127,&mut vec1).unwrap();
    assert!(vec1[0] == 0x7f);

    vec1.clear();
    SMFWriter::write_vtime(255,&mut vec1).unwrap();
    assert!(vec1[0] == 0x81);
    assert!(vec1[1] == 0x7f);

    vec1.clear();
    SMFWriter::write_vtime(32768,&mut vec1).unwrap();
    assert!(vec1[0] == 0x82);
    assert!(vec1[1] == 0x80);
    assert!(vec1[2] == 0x00);
}

<commit_msg>add_track_with_name for named tracks<commit_after>use std::old_io::{File,Writer,IoError, Truncate, Write};

use SMF;
use ::{Event,AbsoluteEvent,MetaEvent,MetaCommand,SMFFormat};

/// An SMFWriter is used to write an SMF to a file.  It can be either
/// constructed empty and have tracks added, or created from an
/// existing rimd::SMF.
///
/// # Writing an existing SMF to a file
/// ```
/// use rimd::{SMF,SMFWriter};
/// // Create smf
/// // let writer = SMFWriter::from_smf(smf);
/// // let result = writer.write_to_file(Path::new("/path/to/file.smf"));
/// // handle result
pub struct SMFWriter {
    format: u16,
    ticks: i16,
    tracks: Vec<Vec<u8>>,
}

impl SMFWriter {

    /// Create a new SMFWriter with the given number of units per
    /// beat.  The SMFWriter will initially have no tracks.
    pub fn new_with_division(ticks: i16) -> SMFWriter {
        SMFWriter {
            format: 1,
            ticks: ticks,
            tracks: Vec::new(),
        }
    }

    /// Create a new SMFWriter with the given format and number of
    /// units per beat.  The SMFWriter will initially have no tracks.
    pub fn new_with_division_and_format(format: SMFFormat, ticks: i16) -> SMFWriter {
        SMFWriter {
            format: format as u16,
            ticks: ticks,
            tracks: Vec::new(),
        }
    }

    /// Create a writer that has all the tracks from the given SMF already added
    pub fn from_smf(smf: SMF) -> SMFWriter {
        let mut writer = SMFWriter::new_with_division_and_format
            (smf.format, smf.division);

        let mut length = 0;
        let mut saw_eot = false;

        for track in smf.tracks.iter() {
            let mut vec = Vec::new();
            writer.start_track_header(&mut vec);

            for event in track.events.iter() {
                length += SMFWriter::write_vtime(event.vtime as u64,&mut vec).unwrap(); // TODO: Handle error
                writer.write_event(&mut vec, &(event.event) , &mut length, &mut saw_eot);
            }

            writer.finish_track_write(&mut vec, &mut length, saw_eot);
            writer.tracks.push(vec);
        }

        writer
    }

    // Write a variable length value.  Return number of bytes written.
    fn write_vtime(val: u64, writer: &mut Writer) -> Result<u32,IoError> {
        let mut storage = Vec::new();
        let mut cur = val;
        let mut written = 0;
        let cont_mask = 0x80 as u8;
        let val_mask = 0x7F as u64;
        loop {
            let mut to_write = (cur & val_mask) as u8;
            cur = cur >> 7;
            if written != 0 {
                // we're writing a continuation byte, so set the bit
                to_write |= cont_mask;
            }
            storage.push(to_write);
            written += 1;
            if cur == 0 { break; }
        }
        for b in storage.iter().rev() {
            try!(writer.write_u8(*b));
        }
        Ok(written)
    }

    fn start_track_header(&self, vec: &mut Vec<u8>) {
        vec.push(0x4D);
        vec.push(0x54);
        vec.push(0x72);
        vec.push(0x6B);
        // reserve space for track len
        vec.push(0);
        vec.push(0);
        vec.push(0);
        vec.push(0);
    }

    fn write_event(&self, vec: &mut Vec<u8>, event: &Event, length: &mut u32, saw_eot: &mut bool) {
        match event {
            &Event::Midi(ref midi) => {
                vec.push_all(midi.data.as_slice());
                *length += midi.data.len() as u32;
            }
            &Event::Meta(ref meta) => {
                vec.push(0xff); // indicate we're writing a meta event
                vec.push(meta.command as u8);
                // +2 on next line for the 0xff and the command byte we just wrote
                *length += SMFWriter::write_vtime(meta.length,vec).unwrap() + 2;
                vec.push_all(meta.data.as_slice());
                *length += meta.data.len() as u32;
                if meta.command == MetaCommand::EndOfTrack {
                    *saw_eot = true;
                }
            }
        }
    }

    fn finish_track_write(&self, vec: &mut Vec<u8>, length: &mut u32, saw_eot: bool) {
        if !saw_eot {
            // no end of track marker in passed data, add one
            *length += SMFWriter::write_vtime(0,vec).unwrap();
            vec.push(0xff); // indicate we're writing a meta event
            vec.push(MetaCommand::EndOfTrack as u8);
            *length += SMFWriter::write_vtime(0,vec).unwrap() + 2; // write length of meta command: 0
        }

        // write in the length in the space we reserved
        for i in (0..4) {
            let lbyte = (*length & 0xFF) as u8;
            // 7-i because smf is big endian and we want to put this in bytes 4-7
            vec[7-i] = lbyte;
            *length = (*length)>>8;
        }
    }

    /// Add any sequence of AbsoluteEvents as a track to this writer
    pub fn add_track<'a,I>(&mut self, track: I) where I: Iterator<Item=&'a AbsoluteEvent> {
        self.add_track_with_name(track,None)
    }

    /// Add any sequence of AbsoluteEvents as a track to this writer.  A meta event with the given name will
    /// be added at the start of the track
    pub fn add_track_with_name<'a,I>(&mut self, track: I, name: Option<String>) where I: Iterator<Item=&'a AbsoluteEvent> {
        let mut vec = Vec::new();

        self.start_track_header(&mut vec);

        let mut length = 0;
        let mut cur_time: u64 = 0;
        let mut saw_eot = false;

        match name {
            Some(n) => {
                let namemeta = Event::Meta(MetaEvent::sequence_or_track_name(n));
                length += SMFWriter::write_vtime(0,&mut vec).unwrap();
                self.write_event(&mut vec, &namemeta, &mut length, &mut saw_eot);
            }
            None => {}
        }

        for ev in track {
            let vtime = ev.get_time() - cur_time;
            cur_time = vtime;
            length += SMFWriter::write_vtime(vtime as u64,&mut vec).unwrap(); // TODO: Handle error
            self.write_event(&mut vec, ev.get_event(), &mut length, &mut saw_eot);
        }

        self.finish_track_write(&mut vec, &mut length, saw_eot);

        self.tracks.push(vec);
    }

    // actual writing stuff below

    fn write_header(&self, writer: &mut Writer) -> Result<(),IoError> {
        try!(writer.write_all(&[0x4D,0x54,0x68,0x64]));
        try!(writer.write_be_u32(6));
        try!(writer.write_be_u16(self.format));
        try!(writer.write_be_u16(self.tracks.len() as u16));
        try!(writer.write_be_i16(self.ticks));
        Ok(())
    }

    /// Write out all the tracks that have been added to this
    /// SMFWriter to the passed writer
    pub fn write_all(self, writer: &mut Writer) -> Result<(),IoError> {
        try!(self.write_header(writer));
        for track in self.tracks.into_iter() {
            try!(writer.write_all(track.as_slice()));
        }
        Ok(())
    }

    /// Write out the result of the tracks that have been added to a
    /// file.
    /// Warning: This will overwrite an existing file
    pub fn write_to_file(self, path: &Path) -> Result<(),IoError> {
        let mut file = try!(File::open_mode(path,Truncate,Write));
        self.write_all(&mut file)
    }

}

#[test]
fn vwrite() {
    let mut vec1 = Vec::new();
    SMFWriter::write_vtime(127,&mut vec1).unwrap();
    assert!(vec1[0] == 0x7f);

    vec1.clear();
    SMFWriter::write_vtime(255,&mut vec1).unwrap();
    assert!(vec1[0] == 0x81);
    assert!(vec1[1] == 0x7f);

    vec1.clear();
    SMFWriter::write_vtime(32768,&mut vec1).unwrap();
    assert!(vec1[0] == 0x82);
    assert!(vec1[1] == 0x80);
    assert!(vec1[2] == 0x00);
}

<|endoftext|>


<commit_before>//
// rslex - a lexer generator for rust
//
// parser.rs
// Input file parsing
//
// Andrei de A. Formiga, 2013-08-04
//

extern mod std;

// --- utility functions -------------------------------------------

#[inline]
fn is_eof(c: char) -> bool {
    c == (-1 as char)
}

// --- the lookahead buffer ----------------------------------------
struct LookaheadBuffer {
    c: char,
    valid: bool,
    reader: @Reader
}

impl LookaheadBuffer {
    fn new(r: @Reader) -> LookaheadBuffer {
        LookaheadBuffer { c: ' ', valid: false, reader: r}
    }

    // FIX: should be read_char but doesn't work due to method resolution bug in rustc
    fn next_char(&mut self) -> char {
        if self.valid {
            self.valid = false;
            self.c
        }
        else {
            self.reader.read_char()
        }
    }

    fn return_char(&mut self, c: char) {
        // assumes at most one char of lookahead
        assert!(self.valid == false);
        self.c = c;
        self.valid = true;
    }
}

#[cfg(test)]
mod buffer_tests {
    use super::*;
    use std::io::with_str_reader;

    #[test]
    fn no_buffer_use() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(!buffer.valid);
    }

    #[test]
    fn buffer_use() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        buffer.return_char('a');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'a');
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
    }

    #[test]
    fn return_other_char() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        buffer.return_char('x');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'x');
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(!buffer.valid);
        buffer.return_char('c');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'c');
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'd');
        assert!(buffer.next_char() == 'e');
    }

    #[test]
    #[should_fail]
    fn return_other_char_expect_same() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        buffer.return_char('x');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'a');
    }

    #[test]
    fn read_until_eof() {
        let mut buffer = with_str_reader("abc", LookaheadBuffer::new);
        assert!(buffer.next_char() == 'a');
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(buffer.next_char() == -1 as char);
    }

    #[test]
    fn return_char_eof() {
        let mut buffer = with_str_reader("abc", LookaheadBuffer::new);
        assert!(buffer.next_char() == 'a');
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(buffer.next_char() == -1 as char);
        buffer.return_char(-1 as char);
        assert!(buffer.valid);
        assert!(buffer.next_char() == -1 as char);
    }
}

fn skip_whitespace(buffer: &mut LookaheadBuffer) {
    let mut c = buffer.next_char();
    while (!is_eof(c) && std::char::is_whitespace(c)) {
        c = buffer.next_char();
    }
    buffer.return_char(c);
}

#[test]
fn test_skip_ws() {
    let mut buffer = std::io::with_str_reader("   abc   ", LookaheadBuffer::new);
    skip_whitespace(&mut buffer);
    assert!(buffer.next_char() == 'a');
    skip_whitespace(&mut buffer);
    assert!(buffer.next_char() == 'b');
    skip_whitespace(&mut buffer);
    assert!(buffer.next_char() == 'c');
    skip_whitespace(&mut buffer);
    assert!(is_eof(buffer.next_char()));
}

// token types
enum Token { LBrace, RBrace, Equals, Comma, Semicolon,
             LParen, RParen, Star, Plus, Bar,
             DefsId, RulesId, CodeId, Id(~str), RegExp, Eof }

enum Block { Rules, Defs, Code }

// fn match_next_token() -> bool {
// }

fn get_next_token(buffer: &mut LookaheadBuffer) -> ~Token {
    let mut c = buffer.next_char();
    let mut done = false;
    let mut res = ~Eof;
    while !done {
        skip_whitespace(buffer);
        if is_eof(c) {
            done = true;
        }
        else {
            match c {
                '{' => { res = ~LBrace; done = true; },
                _   => { done = true; }   // FIX
            }
        }
    }
    res
}

fn parse_id(buffer: &mut LookaheadBuffer, first: char) -> ~str {
    let mut res : ~str = ~"";
    let mut c = buffer.next_char();
    res.push_char(first);
    while !is_eof(c) && !std::char::is_whitespace(c) {
        res.push_char(c);
        c = buffer.next_char();
    }
    buffer.return_char(c);
    res
}

#[test]
fn test_parse_id() {
    let mut buffer = std::io::with_str_reader("hombas   ", LookaheadBuffer::new);
    assert!(std::str::eq(&parse_id(&mut buffer, 's'), &~"shombas"));
}

fn parse_toplevel_block(buffer: &mut LookaheadBuffer) {
}

// fn parse_regexp() {
// }

pub fn parse_lexer_spec(reader: @Reader) {
    let mut buffer = LookaheadBuffer::new(reader);
    parse_toplevel_block(&mut buffer);
}

<commit_msg>Small addition to parse_id test<commit_after>//
// rslex - a lexer generator for rust
//
// parser.rs
// Input file parsing
//
// Andrei de A. Formiga, 2013-08-04
//

extern mod std;

// --- utility functions -------------------------------------------

#[inline]
fn is_eof(c: char) -> bool {
    c == (-1 as char)
}

// --- the lookahead buffer ----------------------------------------
struct LookaheadBuffer {
    c: char,
    valid: bool,
    reader: @Reader
}

impl LookaheadBuffer {
    fn new(r: @Reader) -> LookaheadBuffer {
        LookaheadBuffer { c: ' ', valid: false, reader: r}
    }

    // FIX: should be read_char but doesn't work due to method resolution bug in rustc
    fn next_char(&mut self) -> char {
        if self.valid {
            self.valid = false;
            self.c
        }
        else {
            self.reader.read_char()
        }
    }

    fn return_char(&mut self, c: char) {
        // assumes at most one char of lookahead
        assert!(self.valid == false);
        self.c = c;
        self.valid = true;
    }
}

#[cfg(test)]
mod buffer_tests {
    use super::*;
    use std::io::with_str_reader;

    #[test]
    fn no_buffer_use() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(!buffer.valid);
    }

    #[test]
    fn buffer_use() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        buffer.return_char('a');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'a');
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
    }

    #[test]
    fn return_other_char() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        buffer.return_char('x');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'x');
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(!buffer.valid);
        buffer.return_char('c');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'c');
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'd');
        assert!(buffer.next_char() == 'e');
    }

    #[test]
    #[should_fail]
    fn return_other_char_expect_same() {
        let mut buffer = with_str_reader("abcdef", LookaheadBuffer::new);
        assert!(!buffer.valid);
        assert!(buffer.next_char() == 'a');
        buffer.return_char('x');
        assert!(buffer.valid);
        assert!(buffer.next_char() == 'a');
    }

    #[test]
    fn read_until_eof() {
        let mut buffer = with_str_reader("abc", LookaheadBuffer::new);
        assert!(buffer.next_char() == 'a');
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(buffer.next_char() == -1 as char);
    }

    #[test]
    fn return_char_eof() {
        let mut buffer = with_str_reader("abc", LookaheadBuffer::new);
        assert!(buffer.next_char() == 'a');
        assert!(buffer.next_char() == 'b');
        assert!(buffer.next_char() == 'c');
        assert!(buffer.next_char() == -1 as char);
        buffer.return_char(-1 as char);
        assert!(buffer.valid);
        assert!(buffer.next_char() == -1 as char);
    }
}

fn skip_whitespace(buffer: &mut LookaheadBuffer) {
    let mut c = buffer.next_char();
    while (!is_eof(c) && std::char::is_whitespace(c)) {
        c = buffer.next_char();
    }
    buffer.return_char(c);
}

#[test]
fn test_skip_ws() {
    let mut buffer = std::io::with_str_reader("   abc   ", LookaheadBuffer::new);
    skip_whitespace(&mut buffer);
    assert!(buffer.next_char() == 'a');
    skip_whitespace(&mut buffer);
    assert!(buffer.next_char() == 'b');
    skip_whitespace(&mut buffer);
    assert!(buffer.next_char() == 'c');
    skip_whitespace(&mut buffer);
    assert!(is_eof(buffer.next_char()));
}

// token types
enum Token { LBrace, RBrace, Equals, Comma, Semicolon,
             LParen, RParen, Star, Plus, Bar,
             DefsId, RulesId, CodeId, Id(~str), RegExp, Eof }

enum Block { Rules, Defs, Code }

// fn match_next_token() -> bool {
// }

fn get_next_token(buffer: &mut LookaheadBuffer) -> ~Token {
    let mut c = buffer.next_char();
    let mut done = false;
    let mut res = ~Eof;
    while !done {
        skip_whitespace(buffer);
        if is_eof(c) {
            done = true;
        }
        else {
            match c {
                '{' => { res = ~LBrace; done = true; },
                _   => { done = true; }   // FIX
            }
        }
    }
    res
}

fn parse_id(buffer: &mut LookaheadBuffer, first: char) -> ~str {
    let mut res : ~str = ~"";
    let mut c = buffer.next_char();
    res.push_char(first);
    while !is_eof(c) && !std::char::is_whitespace(c) {
        res.push_char(c);
        c = buffer.next_char();
    }
    buffer.return_char(c);
    res
}

#[test]
fn test_parse_id() {
    let mut buffer = std::io::with_str_reader("hombas   ", LookaheadBuffer::new);
    assert!(std::str::eq(&parse_id(&mut buffer, 's'), &~"shombas"));

    let mut buffer2 = std::io::with_str_reader(" or die ", LookaheadBuffer::new);
    assert!(std::str::eq(&parse_id(&mut buffer2, 'b'), &~"b"));
}

fn parse_toplevel_block(buffer: &mut LookaheadBuffer) {
}

// fn parse_regexp() {
// }

pub fn parse_lexer_spec(reader: @Reader) {
    let mut buffer = LookaheadBuffer::new(reader);
    parse_toplevel_block(&mut buffer);
}

<|endoftext|>


<commit_before>use std::process;
use std::io;
use std::io::Write;

extern crate amqp;
use amqp::{Session, Options, Channel};
use amqp::protocol::basic::BasicProperties;
use amqp::Basic;
use amqp::{Table, TableEntry};

macro_rules! exitln(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

pub struct Sendable {
    pub exchange: String,
    pub routing_key: String,
    pub content_type: String,
    pub headers: Vec<String>,
    pub reader: Box<io::Read>,
}

fn _open(o:Options) -> (Session, Channel) {
    let mut session = Session::new(o)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    let channel = session.open_channel(1)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    (session, channel)
}

pub fn open_send(o:Options, s:Sendable) {
    let (mut session, mut channel) = _open(o);
    // exchange: S,
    // routing_key: S,
    // mandatory: bool,
    // immediate: bool,
    // properties: BasicProperties,
    // content: Vec<u8>
    let mut buffer = Vec::new();
    let mut reader = s.reader;
    let headers = s.headers.iter().fold(Table::new(), |mut h, st| {
        let idx = st.find(':').expect("Header must have a :");
        let (name, value) = st.split_at(idx);
        let key = name.trim();
        let val = (&value[1..]).trim();
        h.insert(String::from(key), TableEntry::LongString(String::from(val)));
        h
    });
    let props = BasicProperties {
        content_type: Some(s.content_type),
        headers: Some(headers),
        ..Default::default()
    };
    reader.read_to_end(&mut buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.basic_publish(s.exchange, s.routing_key, false, false, props, buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.close(200, "Bye")
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    session.close(200, "Good Bye");
}
<commit_msg>read to heap<commit_after>use std::process;
use std::io;
use std::io::Write;

extern crate amqp;
use amqp::{Session, Options, Channel};
use amqp::protocol::basic::BasicProperties;
use amqp::Basic;
use amqp::{Table, TableEntry};

macro_rules! exitln(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

pub struct Sendable {
    pub exchange: String,
    pub routing_key: String,
    pub content_type: String,
    pub headers: Vec<String>,
    pub reader: Box<io::Read>,
}

fn _open(o:Options) -> (Session, Channel) {
    let mut session = Session::new(o)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    let channel = session.open_channel(1)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    (session, channel)
}

pub fn open_send(o:Options, s:Sendable) {
    let (mut session, mut channel) = _open(o);
    // exchange: S,
    // routing_key: S,
    // mandatory: bool,
    // immediate: bool,
    // properties: BasicProperties,
    // content: Vec<u8>
    let headers = s.headers.iter().fold(Table::new(), |mut h, st| {
        let idx = st.find(':').expect("Header must have a :");
        let (name, value) = st.split_at(idx);
        let key = name.trim();
        let val = (&value[1..]).trim();
        h.insert(String::from(key), TableEntry::LongString(String::from(val)));
        h
    });
    let props = BasicProperties {
        content_type: Some(s.content_type),
        headers: Some(headers),
        ..Default::default()
    };
    let mut buffer = Box::new(vec![]);
    let mut reader = s.reader;
    reader.read_to_end(&mut buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.basic_publish(s.exchange, s.routing_key, false, false, props, *buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.close(200, "Bye")
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    session.close(200, "Good Bye");
}
<|endoftext|>


<commit_before>use std::{io, fmt, thread, time};
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::{AtomicPtr, Ordering};

use hyper;
use hyper_tls;
use futures::{future, Future, Stream};
use futures::sync::oneshot;
use openssl;
use serde::{Deserialize, Serialize};
use serde_json;
use tokio_core::reactor;

use auth;

// (max) NOTE please don't use this directly - prefer 'access_hyper_client'
type HyperClient = hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>;
lazy_static!(static ref HYPER_CLIENT: AtomicPtr<HyperClient> = Default::default(););

// (max) NOTE since Handle is only available on the reactor thread, we can
// enforce that HyperClient is only used there by requiring Handle as an arg
fn access_hyper_client(_: &reactor::Handle) -> &HyperClient {
    unsafe { &*HYPER_CLIENT.load(Ordering::SeqCst) }
}

const DNS_WORKER_THREADS: usize = 4;
const KEEP_ALIVE_TIMEOUT: u64 = 600; // 10 minutes

pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    ApiError(ApiError),
    OpenSslError(openssl::error::ErrorStack),
    HyperError(hyper::Error),
    JsonError(serde_json::Error),
    Unauthorized, // a generic "unauthorized" error
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            Error::ApiError(ref e) => write!(f, "ApiError {:?}", e),
            Error::HyperError(ref e) => write!(f, "HyperError {:?}", e),
            Error::JsonError(ref e) => write!(f, "JsonError {:?}", e),
            Error::OpenSslError(ref e) => write!(f, "OpenSslError {:?}", e),
            Error::Unauthorized => write!(f, "Unauthorized"),
        }
    }
}

#[derive(Deserialize, Debug)]
pub struct ApiError {
    #[serde(default)]
    pub error: Option<ErrorDetails>,
    pub error_description: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct ErrorDetails {
    pub code: Option<usize>,
    pub message: Option<String>,
    pub status: Option<String>,
}

#[derive(Clone)]
pub struct GoogleCloudClient {
    project_id: String,
    remote: reactor::Remote,
    auth: auth::GoogleCloudAuth,
    _core_thread: Arc<thread::JoinHandle<()>>,
}

impl GoogleCloudClient {
    pub fn new(project_id: &str) -> io::Result<Self> {
        let (tx, rx) = oneshot::channel();
        let core_thread = thread::Builder::new()
            .name("google-cloud-client".into())
            .spawn(move || {
                let mut core = reactor::Core::new().unwrap();

                let https = hyper_tls::HttpsConnector::new(DNS_WORKER_THREADS, &core.handle())
                    .unwrap();

                let mut client = hyper::Client::configure()
                    .connector(https)
                    .keep_alive_timeout(Some(time::Duration::from_secs(KEEP_ALIVE_TIMEOUT)))
                    .keep_alive(true)
                    .build(&core.handle());

                HYPER_CLIENT.store(&mut client, Ordering::SeqCst);

                let remote = core.remote();
                tx.send(remote).unwrap();

                core.run(future::empty::<(), ()>()).unwrap()
            })?;

        let remote = rx.wait().unwrap();
        Ok(GoogleCloudClient {
               project_id: project_id.to_string(),
               remote: remote,
               auth: auth::default_credentials(),
               _core_thread: Arc::new(core_thread),
           })
    }
    pub fn hub<S>(&self) -> Hub<S> {
        Hub {
            client: self,
            _service: PhantomData,
        }
    }
    pub fn get_firebase_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.auth.get_firebase_pkey(&self.hub::<()>(), kid)
    }
    pub fn get_google_auth_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.auth.get_google_auth_pkey(&self.hub::<()>(), kid)
    }
}

pub struct Hub<'a, S> {
    client: &'a GoogleCloudClient,
    _service: PhantomData<S>,
}

impl<'a, S> Hub<'a, S> {
    pub fn project_id(&self) -> &str {
        &self.client.project_id
    }
}

impl<'a, S> ApiClient for Hub<'a, S> {
    fn token(&self, scopes: &[String]) -> Result<auth::Token> {
        self.client.auth.token(self, scopes)
    }

    fn request<D: 'static + Send>(&self,
                                  r: hyper::Request<hyper::Body>)
                                  -> Result<(hyper::Headers, D)>
        where for<'de> D: 'static + Send + Deserialize<'de>
    {
        trace!("send request: {:?}", r);
        let (tx, rx) = oneshot::channel();

        self.client
            .remote
            .spawn(|handle| {
                let work = access_hyper_client(handle).request(r);
                work.and_then(|res| {
                        trace!("recv response: {:?}", res);
                        let status = res.status();
                        let headers = res.headers().clone();

                        res.body()
                            .collect()
                            .map(move |chunks| {
                                trace!("fold chunks: {:?}", chunks);

                                let body = chunks
                                    .into_iter()
                                    .fold(vec![], |mut acc, chunk| {
                                        acc.extend_from_slice(&*chunk);
                                        acc
                                    });

                                let res = if status.is_success() {
                                    serde_json::from_slice(&body).map_err(|e| Error::JsonError(e))
                                } else {
                                    match serde_json::from_slice::<ApiError>(&body) {
                                        Ok(e) => Err(Error::ApiError(e)),
                                        Err(e) => Err(Error::JsonError(e)),
                                    }
                                };

                                let as_str = unsafe { ::std::str::from_utf8_unchecked(&body) };
                                trace!("recv oneshot: {}", as_str);

                                tx.send(res.map(|res| (headers, res))).unwrap_or(())
                            })
                    })
                    .map_err(|_| ())
            });
        rx.wait().unwrap()
    }
}

pub fn encode_query_params<'a, I>(i: I) -> String
    where I: IntoIterator<Item = (&'a str, String)>
{
    use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

    let mut query = String::new();
    for (i, (k, v)) in i.into_iter().enumerate() {
        if i != 0 {
            query.push('&');
        }
        query.extend(utf8_percent_encode(k, QUERY_ENCODE_SET));
        query.push('=');
        query.extend(utf8_percent_encode(&v, QUERY_ENCODE_SET));
    }
    query
}

pub trait ApiClient {
    // submits a raw request using hyper
    fn request<D>(&self, hyper::Request<hyper::Body>) -> Result<(hyper::Headers, D)>
        where for<'de> D: 'static + Send + Deserialize<'de>;

    // fetches an access token for use in requests
    fn token(&self, scopes: &[String]) -> Result<auth::Token>;

    // helper method for making a GET request
    fn get<D>(&self, uri: &hyper::Uri, scopes: &[String]) -> Result<D>
        where for<'de> D: 'static + Send + Deserialize<'de>
    {
        let mut req = hyper::Request::new(hyper::Method::Get, uri.clone());
        req.headers_mut().set(self.token(scopes)?.into_header());

        self.request(req).map(|(_, res)| res)
    }

    // helper method for making a POST request with a JSON body
    fn post<B: Serialize, D>(&self, uri: &hyper::Uri, body: B, scopes: &[String]) -> Result<D>
        where for<'de> D: 'static + Send + Deserialize<'de>
    {
        let mut req = hyper::Request::new(hyper::Method::Post, uri.clone());
        req.headers_mut().set(hyper::header::ContentType::json());
        req.headers_mut().set(self.token(scopes)?.into_header());

        let body = serde_json::to_string(&body).unwrap();
        req.set_body(body);

        self.request(req).map(|(_, res)| res)
    }
}
<commit_msg>move fns into tokeninfo hub<commit_after>use std::{io, fmt, thread, time};
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::{AtomicPtr, Ordering};

use hyper;
use hyper_tls;
use futures::{future, Future, Stream};
use futures::sync::oneshot;
use openssl;
use serde::{Deserialize, Serialize};
use serde_json;
use tokio_core::reactor;

use auth;

// (max) NOTE please don't use this directly - prefer 'access_hyper_client'
type HyperClient = hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>;
lazy_static!(static ref HYPER_CLIENT: AtomicPtr<HyperClient> = Default::default(););

// (max) NOTE since Handle is only available on the reactor thread, we can
// enforce that HyperClient is only used there by requiring Handle as an arg
fn access_hyper_client(_: &reactor::Handle) -> &HyperClient {
    unsafe { &*HYPER_CLIENT.load(Ordering::SeqCst) }
}

const DNS_WORKER_THREADS: usize = 4;
const KEEP_ALIVE_TIMEOUT: u64 = 600; // 10 minutes

pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    ApiError(ApiError),
    OpenSslError(openssl::error::ErrorStack),
    HyperError(hyper::Error),
    JsonError(serde_json::Error),
    Unauthorized, // a generic "unauthorized" error
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            Error::ApiError(ref e) => write!(f, "ApiError {:?}", e),
            Error::HyperError(ref e) => write!(f, "HyperError {:?}", e),
            Error::JsonError(ref e) => write!(f, "JsonError {:?}", e),
            Error::OpenSslError(ref e) => write!(f, "OpenSslError {:?}", e),
            Error::Unauthorized => write!(f, "Unauthorized"),
        }
    }
}

#[derive(Deserialize, Debug)]
pub struct ApiError {
    #[serde(default)]
    pub error: Option<ErrorDetails>,
    pub error_description: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct ErrorDetails {
    pub code: Option<usize>,
    pub message: Option<String>,
    pub status: Option<String>,
}

#[derive(Clone)]
pub struct GoogleCloudClient {
    project_id: String,
    remote: reactor::Remote,
    auth: auth::GoogleCloudAuth,
    _core_thread: Arc<thread::JoinHandle<()>>,
}

impl GoogleCloudClient {
    pub fn new(project_id: &str) -> io::Result<Self> {
        let (tx, rx) = oneshot::channel();
        let core_thread = thread::Builder::new()
            .name("google-cloud-client".into())
            .spawn(move || {
                let mut core = reactor::Core::new().unwrap();

                let https = hyper_tls::HttpsConnector::new(DNS_WORKER_THREADS, &core.handle())
                    .unwrap();

                let mut client = hyper::Client::configure()
                    .connector(https)
                    .keep_alive_timeout(Some(time::Duration::from_secs(KEEP_ALIVE_TIMEOUT)))
                    .keep_alive(true)
                    .build(&core.handle());

                HYPER_CLIENT.store(&mut client, Ordering::SeqCst);

                let remote = core.remote();
                tx.send(remote).unwrap();

                core.run(future::empty::<(), ()>()).unwrap()
            })?;

        let remote = rx.wait().unwrap();
        Ok(GoogleCloudClient {
               project_id: project_id.to_string(),
               remote: remote,
               auth: auth::default_credentials(),
               _core_thread: Arc::new(core_thread),
           })
    }
    pub fn hub<S>(&self) -> Hub<S> {
        Hub {
            client: self,
            _service: PhantomData,
        }
    }
}

pub struct Hub<'a, S> {
    client: &'a GoogleCloudClient,
    _service: PhantomData<S>,
}

impl<'a, S> Hub<'a, S> {
    pub fn project_id(&self) -> &str {
        &self.client.project_id
    }
}

impl<'a> Hub<'a, ::svc::tokeninfo::TokenInfoService> {
    pub fn get_firebase_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.client.auth.get_firebase_pkey(self, kid)
    }
    pub fn get_google_auth_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.client.auth.get_google_auth_pkey(self, kid)
    }
}

impl<'a, S> ApiClient for Hub<'a, S> {
    fn token(&self, scopes: &[String]) -> Result<auth::Token> {
        self.client.auth.token(self, scopes)
    }

    fn request<D: 'static + Send>(&self,
                                  r: hyper::Request<hyper::Body>)
                                  -> Result<(hyper::Headers, D)>
        where for<'de> D: 'static + Send + Deserialize<'de>
    {
        trace!("send request: {:?}", r);
        let (tx, rx) = oneshot::channel();

        self.client
            .remote
            .spawn(|handle| {
                let work = access_hyper_client(handle).request(r);
                work.and_then(|res| {
                        trace!("recv response: {:?}", res);
                        let status = res.status();
                        let headers = res.headers().clone();

                        res.body()
                            .collect()
                            .map(move |chunks| {
                                trace!("fold chunks: {:?}", chunks);

                                let body = chunks
                                    .into_iter()
                                    .fold(vec![], |mut acc, chunk| {
                                        acc.extend_from_slice(&*chunk);
                                        acc
                                    });

                                let res = if status.is_success() {
                                    serde_json::from_slice(&body).map_err(|e| Error::JsonError(e))
                                } else {
                                    match serde_json::from_slice::<ApiError>(&body) {
                                        Ok(e) => Err(Error::ApiError(e)),
                                        Err(e) => Err(Error::JsonError(e)),
                                    }
                                };

                                let as_str = unsafe { ::std::str::from_utf8_unchecked(&body) };
                                trace!("recv oneshot: {}", as_str);

                                tx.send(res.map(|res| (headers, res))).unwrap_or(())
                            })
                    })
                    .map_err(|_| ())
            });
        rx.wait().unwrap()
    }
}

pub fn encode_query_params<'a, I>(i: I) -> String
    where I: IntoIterator<Item = (&'a str, String)>
{
    use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

    let mut query = String::new();
    for (i, (k, v)) in i.into_iter().enumerate() {
        if i != 0 {
            query.push('&');
        }
        query.extend(utf8_percent_encode(k, QUERY_ENCODE_SET));
        query.push('=');
        query.extend(utf8_percent_encode(&v, QUERY_ENCODE_SET));
    }
    query
}

pub trait ApiClient {
    // submits a raw request using hyper
    fn request<D>(&self, hyper::Request<hyper::Body>) -> Result<(hyper::Headers, D)>
        where for<'de> D: 'static + Send + Deserialize<'de>;

    // fetches an access token for use in requests
    fn token(&self, scopes: &[String]) -> Result<auth::Token>;

    // helper method for making a GET request
    fn get<D>(&self, uri: &hyper::Uri, scopes: &[String]) -> Result<D>
        where for<'de> D: 'static + Send + Deserialize<'de>
    {
        let mut req = hyper::Request::new(hyper::Method::Get, uri.clone());
        req.headers_mut().set(self.token(scopes)?.into_header());

        self.request(req).map(|(_, res)| res)
    }

    // helper method for making a POST request with a JSON body
    fn post<B: Serialize, D>(&self, uri: &hyper::Uri, body: B, scopes: &[String]) -> Result<D>
        where for<'de> D: 'static + Send + Deserialize<'de>
    {
        let mut req = hyper::Request::new(hyper::Method::Post, uri.clone());
        req.headers_mut().set(hyper::header::ContentType::json());
        req.headers_mut().set(self.token(scopes)?.into_header());

        let body = serde_json::to_string(&body).unwrap();
        req.set_body(body);

        self.request(req).map(|(_, res)| res)
    }
}
<|endoftext|>


<commit_before>use nom::{alphanumeric, digit, eof, is_alphanumeric, line_ending, multispace};
use nom::{IResult, Err, ErrorKind, Needed};
use std::fmt::{self, Display};
use std::str;
use std::str::FromStr;

use column::{Column, FunctionExpression};
use keywords::sql_keyword;
use table::Table;

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum SqlType {
    Char(u16),
    Varchar(u16),
    Int(u16),
    Bigint(u16),
    Tinyint(u16),
    Blob,
    Longblob,
    Mediumblob,
    Tinyblob,
    Double,
    Float,
    Real,
    Tinytext,
    Mediumtext,
    Text,
    Date,
    Timestamp,
    Varbinary(u16),
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum Literal {
    Null,
    Integer(i64),
    String(String),
    Blob(Vec<u8>),
    CurrentTime,
    CurrentDate,
    CurrentTimestamp,
}

impl From<i64> for Literal {
    fn from(i: i64) -> Self {
        Literal::Integer(i)
    }
}

impl From<String> for Literal {
    fn from(s: String) -> Self {
        Literal::String(s)
    }
}

impl<'a> From<&'a str> for Literal {
    fn from(s: &'a str) -> Self {
        Literal::String(String::from(s))
    }
}

impl ToString for Literal {
    fn to_string(&self) -> String {
        match *self {
            Literal::Null => "NULL".to_string(),
            Literal::Integer(ref i) => format!("{}", i),
            Literal::String(ref s) => s.clone(),
            Literal::Blob(ref bv) => {
                format!("{}",
                        bv.iter()
                            .map(|v| format!("{:x}", v))
                            .collect::<Vec<String>>()
                            .join(" "))
            }
            Literal::CurrentTime => "CURRENT_TIME".to_string(),
            Literal::CurrentDate => "CURRENT_DATE".to_string(),
            Literal::CurrentTimestamp => "CURRENT_TIMESTAMP".to_string(),
        }
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum Operator {
    Not,
    And,
    Or,
    Like,
    NotLike,
    Equal,
    NotEqual,
    Greater,
    GreaterOrEqual,
    Less,
    LessOrEqual,
}

impl Display for Operator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let op = match *self {
            Operator::Not => "not",
            Operator::And => "and",
            Operator::Or => "or",
            Operator::Like => "like",
            Operator::NotLike => "not_like",
            Operator::Equal => "=",
            Operator::NotEqual => "!=",
            Operator::Greater => ">",
            Operator::GreaterOrEqual => ">=",
            Operator::Less => "<",
            Operator::LessOrEqual => "<=",
        };
        write!(f, "{}", op)
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum TableKey {
    PrimaryKey(Vec<Column>),
    UniqueKey(Option<String>, Vec<Column>),
    FulltextKey(Option<String>, Vec<Column>),
    Key(String, Vec<Column>),
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum FieldExpression {
    All,
    AllInTable(String),
    Col(Column),
}

impl Display for FieldExpression {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FieldExpression::All => write!(f, "*"),
            FieldExpression::AllInTable(ref table) => write!(f, "{}.*", table),
            FieldExpression::Col(ref col) => write!(f, "{}", col.name.as_str()),
        }
    }
}

impl Default for FieldExpression {
    fn default() -> FieldExpression {
        FieldExpression::All
    }
}

#[inline]
pub fn is_sql_identifier(chr: u8) -> bool {
    is_alphanumeric(chr) || chr == '_' as u8
}

/// Parses the arguments for an agregation function, and also returns whether the distinct flag is
/// present.
named!(pub function_arguments<&[u8], (Column, bool)>,
       chain!(
           distinct: opt!(chain!(
               caseless_tag!("distinct") ~
               multispace,
               ||{}
           )) ~
           column: column_identifier_no_alias,
           || {
               (column, distinct.is_some())
           }
       )
);

named!(pub column_function<&[u8], FunctionExpression>,
    alt_complete!(
        chain!(
            caseless_tag!("count(*)"),
            || {
                FunctionExpression::CountStar
            }
        )
    |   chain!(
            caseless_tag!("count") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Count(args.0.clone(), args.1)
            }
        )
    |   chain!(
            caseless_tag!("sum") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Sum(args.0.clone(), args.1)
            }
        )
    |   chain!(
            caseless_tag!("avg") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Avg(args.0.clone(), args.1)
            }
        )
    |   chain!(
            caseless_tag!("max") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Max(args.0.clone())
            }
        )
    |   chain!(
            caseless_tag!("min") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Min(args.0.clone())
            }
        )
    |   chain!(
            caseless_tag!("group_concat") ~
            spec: delimited!(tag!("("),
                       complete!(chain!(
                               column: column_identifier_no_alias ~
                               seperator: opt!(
                                   chain!(
                                       multispace? ~
                                       caseless_tag!("separator") ~
                                       sep: delimited!(tag!("'"), opt!(alphanumeric), tag!("'")) ~
                                       multispace?,
                                       || { sep.unwrap_or("".as_bytes()) }
                                   )
                               ),
                               || { (column, seperator) }
                       )),
                       tag!(")")),
            || {
                let (ref col, ref sep) = spec;
                let sep = match *sep {
                    // default separator is a comma, see MySQL manual §5.7
                    None => String::from(","),
                    Some(s) => String::from_utf8(s.to_vec()).unwrap(),
                };

                FunctionExpression::GroupConcat(col.clone(), sep)
            }
        )
    )
);

/// Parses a SQL column identifier in the table.column format
named!(pub column_identifier_no_alias<&[u8], Column>,
    alt_complete!(
        chain!(
            function: column_function,
            || {
                Column {
                    name: format!("{}", function),
                    alias: None,
                    table: None,
                    function: Some(Box::new(function)),
                }
            }
        )
        | chain!(
            table: opt!(
                chain!(
                    tbl_name: map_res!(sql_identifier, str::from_utf8) ~
                    tag!("."),
                    || { tbl_name }
                )
            ) ~
            column: map_res!(sql_identifier, str::from_utf8),
            || {
                Column {
                    name: String::from(column),
                    alias: None,
                    table: match table {
                        None => None,
                        Some(t) => Some(String::from(t)),
                    },
                    function: None,
                }
            }
        )
    )
);

/// Parses a SQL column identifier in the table.column format
named!(pub column_identifier<&[u8], Column>,
    alt_complete!(
        chain!(
            function: column_function ~
            alias: opt!(as_alias),
            || {
                Column {
                    name: match alias {
                        None => format!("{}", function),
                        Some(a) => String::from(a),
                    },
                    alias: match alias {
                        None => None,
                        Some(a) => Some(String::from(a)),
                    },
                    table: None,
                    function: Some(Box::new(function)),
                }
            }
        )
        | chain!(
            table: opt!(
                chain!(
                    tbl_name: map_res!(sql_identifier, str::from_utf8) ~
                    tag!("."),
                    || { tbl_name }
                )
            ) ~
            column: map_res!(sql_identifier, str::from_utf8) ~
            alias: opt!(as_alias),
            || {
                Column {
                    name: String::from(column),
                    alias: match alias {
                        None => None,
                        Some(a) => Some(String::from(a)),
                    },
                    table: match table {
                        None => None,
                        Some(t) => Some(String::from(t)),
                    },
                    function: None,
                }
            }
        )
    )
);

/// Parses a SQL identifier (alphanumeric and "_").
named!(pub sql_identifier<&[u8], &[u8]>,
    alt_complete!(
          chain!(
                not!(peek!(sql_keyword)) ~
                ident: take_while1!(is_sql_identifier),
                || { ident }
          )
        | delimited!(tag!("`"), take_while1!(is_sql_identifier), tag!("`"))
        | delimited!(tag!("'"), take_while1!(is_sql_identifier), tag!("'"))
    )
);

/// Parse an unsigned integer.
named!(pub unsigned_number<&[u8], u64>,
    map_res!(
        map_res!(digit, str::from_utf8),
        FromStr::from_str
    )
);

/// Parse a terminator that ends a SQL statement.
named!(pub statement_terminator,
    delimited!(opt!(multispace),
               alt_complete!(tag!(";") | line_ending | eof),
               opt!(multispace)
    )
);

/// Parse binary comparison operators
named!(pub binary_comparison_operator<&[u8], Operator>,
    alt_complete!(
           map!(caseless_tag!("not_like"), |_| Operator::NotLike)
         | map!(caseless_tag!("like"), |_| Operator::Like)
         | map!(caseless_tag!("!="), |_| Operator::NotEqual)
         | map!(caseless_tag!("<>"), |_| Operator::NotEqual)
         | map!(caseless_tag!(">="), |_| Operator::GreaterOrEqual)
         | map!(caseless_tag!("<="), |_| Operator::LessOrEqual)
         | map!(caseless_tag!("="), |_| Operator::Equal)
         | map!(caseless_tag!("<"), |_| Operator::Less)
         | map!(caseless_tag!(">"), |_| Operator::Greater)
    )
);

/// Parse rule for AS-based aliases for SQL entities.
named!(pub as_alias<&[u8], &str>,
    complete!(
        chain!(
            multispace ~
            opt!(chain!(caseless_tag!("as") ~ multispace, ||{})) ~
            alias: map_res!(sql_identifier, str::from_utf8),
            || { alias }
        )
    )
);

/// Parse rule for a comma-separated list of fields without aliases.
named!(pub field_list<&[u8], Vec<Column> >,
       many0!(
           chain!(
               fieldname: column_identifier_no_alias ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       ||{}
                   ))
               ),
               || { fieldname }
           )
       )
);

/// Parse list of column/field definitions.
/// XXX(malte): add support for named table notation
named!(pub field_definition_expr<&[u8], Vec<FieldExpression>>,
       many0!(
           chain!(
               field: alt_complete!(
                   chain!(
                       tag!("*"),
                       || {
                           FieldExpression::All
                       }
                   )
                 | chain!(
                     table: table_reference ~ tag!(".*"),
                     || {
                         FieldExpression::AllInTable(table.name.clone())
                     }
                 )
                 | chain!(
                     column: column_identifier,
                     || {
                         FieldExpression::Col(column)
                     }
                 )
               ) ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                           tag!(",") ~
                           multispace?,
                       ||{}
                   ))
               ),
               || { field }
           )
       )
);

/// Parse list of table names.
/// XXX(malte): add support for aliases
named!(pub table_list<&[u8], Vec<Table> >,
       many0!(
           chain!(
               table: table_reference ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       || {}
                   ))
               ),
               || { table }
           )
       )
);

/// Integer literal value
named!(pub integer_literal<&[u8], Literal>,
    chain!(
        val: delimited!(opt!(multispace), digit, opt!(multispace)),
        || {
            let intval = i64::from_str(str::from_utf8(val).unwrap()).unwrap();
            Literal::Integer(intval)
        }
    )
);

/// String literal value
named!(pub string_literal<&[u8], Literal>,
    chain!(
        val: delimited!(opt!(multispace),
                 alt_complete!(
                       delimited!(tag!("\""), opt!(take_until!("\"")), tag!("\""))
                     | delimited!(tag!("'"), opt!(take_until!("'")), tag!("'"))
                 ),
                 opt!(multispace)
              ),
        || {
            let val = val.unwrap_or("".as_bytes());
            let s = String::from(str::from_utf8(val).unwrap());
            Literal::String(s)
        }
    )
);

/// Any literal value.
named!(pub literal<&[u8], Literal>,
    alt_complete!(
          integer_literal
        | string_literal
        | chain!(caseless_tag!("NULL"), || Literal::Null)
        | chain!(caseless_tag!("CURRENT_TIMESTAMP"), || Literal::CurrentTimestamp)
        | chain!(caseless_tag!("CURRENT_DATE"), || Literal::CurrentDate)
        | chain!(caseless_tag!("CURRENT_TIME"), || Literal::CurrentTime)
//        | float_literal
    )
);

/// Parse a list of values (e.g., for INSERT syntax).
named!(pub value_list<&[u8], Vec<Literal> >,
       many0!(
           chain!(
               val: literal ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       ||{}
                   ))
               ),
               || { val }
           )
       )
);

/// Parse a reference to a named table
/// XXX(malte): add support for schema.table notation
named!(pub table_reference<&[u8], Table>,
    chain!(
        table: map_res!(sql_identifier, str::from_utf8) ~
        alias: opt!(as_alias),
        || {
            Table {
                name: String::from(table),
                alias: match alias {
                    Some(a) => Some(String::from(a)),
                    None => None,
                }
            }
        }
    )
);

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

    #[test]
    fn sql_identifiers() {
        let id1 = b"foo";
        let id2 = b"f_o_o";
        let id3 = b"foo12";
        let id4 = b":fo oo";
        let id5 = b"primary ";
        let id6 = b"`primary`";

        assert!(sql_identifier(id1).is_done());
        assert!(sql_identifier(id2).is_done());
        assert!(sql_identifier(id3).is_done());
        assert!(sql_identifier(id4).is_err());
        assert!(sql_identifier(id5).is_err());
        assert!(sql_identifier(id6).is_done());
    }

    #[test]
    fn simple_column_function() {
        let qs = b"max(addr_id)";

        let res = column_identifier(qs);
        let expected = Column {
            name: String::from("max(addr_id)"),
            alias: None,
            table: None,
            function: Some(Box::new(FunctionExpression::Max(Column::from("addr_id")))),
        };
        assert_eq!(res.unwrap().1, expected);
    }
}
<commit_msg>Remove obsolete comment<commit_after>use nom::{alphanumeric, digit, eof, is_alphanumeric, line_ending, multispace};
use nom::{IResult, Err, ErrorKind, Needed};
use std::fmt::{self, Display};
use std::str;
use std::str::FromStr;

use column::{Column, FunctionExpression};
use keywords::sql_keyword;
use table::Table;

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum SqlType {
    Char(u16),
    Varchar(u16),
    Int(u16),
    Bigint(u16),
    Tinyint(u16),
    Blob,
    Longblob,
    Mediumblob,
    Tinyblob,
    Double,
    Float,
    Real,
    Tinytext,
    Mediumtext,
    Text,
    Date,
    Timestamp,
    Varbinary(u16),
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum Literal {
    Null,
    Integer(i64),
    String(String),
    Blob(Vec<u8>),
    CurrentTime,
    CurrentDate,
    CurrentTimestamp,
}

impl From<i64> for Literal {
    fn from(i: i64) -> Self {
        Literal::Integer(i)
    }
}

impl From<String> for Literal {
    fn from(s: String) -> Self {
        Literal::String(s)
    }
}

impl<'a> From<&'a str> for Literal {
    fn from(s: &'a str) -> Self {
        Literal::String(String::from(s))
    }
}

impl ToString for Literal {
    fn to_string(&self) -> String {
        match *self {
            Literal::Null => "NULL".to_string(),
            Literal::Integer(ref i) => format!("{}", i),
            Literal::String(ref s) => s.clone(),
            Literal::Blob(ref bv) => {
                format!("{}",
                        bv.iter()
                            .map(|v| format!("{:x}", v))
                            .collect::<Vec<String>>()
                            .join(" "))
            }
            Literal::CurrentTime => "CURRENT_TIME".to_string(),
            Literal::CurrentDate => "CURRENT_DATE".to_string(),
            Literal::CurrentTimestamp => "CURRENT_TIMESTAMP".to_string(),
        }
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum Operator {
    Not,
    And,
    Or,
    Like,
    NotLike,
    Equal,
    NotEqual,
    Greater,
    GreaterOrEqual,
    Less,
    LessOrEqual,
}

impl Display for Operator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let op = match *self {
            Operator::Not => "not",
            Operator::And => "and",
            Operator::Or => "or",
            Operator::Like => "like",
            Operator::NotLike => "not_like",
            Operator::Equal => "=",
            Operator::NotEqual => "!=",
            Operator::Greater => ">",
            Operator::GreaterOrEqual => ">=",
            Operator::Less => "<",
            Operator::LessOrEqual => "<=",
        };
        write!(f, "{}", op)
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum TableKey {
    PrimaryKey(Vec<Column>),
    UniqueKey(Option<String>, Vec<Column>),
    FulltextKey(Option<String>, Vec<Column>),
    Key(String, Vec<Column>),
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum FieldExpression {
    All,
    AllInTable(String),
    Col(Column),
}

impl Display for FieldExpression {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FieldExpression::All => write!(f, "*"),
            FieldExpression::AllInTable(ref table) => write!(f, "{}.*", table),
            FieldExpression::Col(ref col) => write!(f, "{}", col.name.as_str()),
        }
    }
}

impl Default for FieldExpression {
    fn default() -> FieldExpression {
        FieldExpression::All
    }
}

#[inline]
pub fn is_sql_identifier(chr: u8) -> bool {
    is_alphanumeric(chr) || chr == '_' as u8
}

/// Parses the arguments for an agregation function, and also returns whether the distinct flag is
/// present.
named!(pub function_arguments<&[u8], (Column, bool)>,
       chain!(
           distinct: opt!(chain!(
               caseless_tag!("distinct") ~
               multispace,
               ||{}
           )) ~
           column: column_identifier_no_alias,
           || {
               (column, distinct.is_some())
           }
       )
);

named!(pub column_function<&[u8], FunctionExpression>,
    alt_complete!(
        chain!(
            caseless_tag!("count(*)"),
            || {
                FunctionExpression::CountStar
            }
        )
    |   chain!(
            caseless_tag!("count") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Count(args.0.clone(), args.1)
            }
        )
    |   chain!(
            caseless_tag!("sum") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Sum(args.0.clone(), args.1)
            }
        )
    |   chain!(
            caseless_tag!("avg") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Avg(args.0.clone(), args.1)
            }
        )
    |   chain!(
            caseless_tag!("max") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Max(args.0.clone())
            }
        )
    |   chain!(
            caseless_tag!("min") ~
            args: delimited!(tag!("("), function_arguments, tag!(")")),
            || {
                FunctionExpression::Min(args.0.clone())
            }
        )
    |   chain!(
            caseless_tag!("group_concat") ~
            spec: delimited!(tag!("("),
                       complete!(chain!(
                               column: column_identifier_no_alias ~
                               seperator: opt!(
                                   chain!(
                                       multispace? ~
                                       caseless_tag!("separator") ~
                                       sep: delimited!(tag!("'"), opt!(alphanumeric), tag!("'")) ~
                                       multispace?,
                                       || { sep.unwrap_or("".as_bytes()) }
                                   )
                               ),
                               || { (column, seperator) }
                       )),
                       tag!(")")),
            || {
                let (ref col, ref sep) = spec;
                let sep = match *sep {
                    // default separator is a comma, see MySQL manual §5.7
                    None => String::from(","),
                    Some(s) => String::from_utf8(s.to_vec()).unwrap(),
                };

                FunctionExpression::GroupConcat(col.clone(), sep)
            }
        )
    )
);

/// Parses a SQL column identifier in the table.column format
named!(pub column_identifier_no_alias<&[u8], Column>,
    alt_complete!(
        chain!(
            function: column_function,
            || {
                Column {
                    name: format!("{}", function),
                    alias: None,
                    table: None,
                    function: Some(Box::new(function)),
                }
            }
        )
        | chain!(
            table: opt!(
                chain!(
                    tbl_name: map_res!(sql_identifier, str::from_utf8) ~
                    tag!("."),
                    || { tbl_name }
                )
            ) ~
            column: map_res!(sql_identifier, str::from_utf8),
            || {
                Column {
                    name: String::from(column),
                    alias: None,
                    table: match table {
                        None => None,
                        Some(t) => Some(String::from(t)),
                    },
                    function: None,
                }
            }
        )
    )
);

/// Parses a SQL column identifier in the table.column format
named!(pub column_identifier<&[u8], Column>,
    alt_complete!(
        chain!(
            function: column_function ~
            alias: opt!(as_alias),
            || {
                Column {
                    name: match alias {
                        None => format!("{}", function),
                        Some(a) => String::from(a),
                    },
                    alias: match alias {
                        None => None,
                        Some(a) => Some(String::from(a)),
                    },
                    table: None,
                    function: Some(Box::new(function)),
                }
            }
        )
        | chain!(
            table: opt!(
                chain!(
                    tbl_name: map_res!(sql_identifier, str::from_utf8) ~
                    tag!("."),
                    || { tbl_name }
                )
            ) ~
            column: map_res!(sql_identifier, str::from_utf8) ~
            alias: opt!(as_alias),
            || {
                Column {
                    name: String::from(column),
                    alias: match alias {
                        None => None,
                        Some(a) => Some(String::from(a)),
                    },
                    table: match table {
                        None => None,
                        Some(t) => Some(String::from(t)),
                    },
                    function: None,
                }
            }
        )
    )
);

/// Parses a SQL identifier (alphanumeric and "_").
named!(pub sql_identifier<&[u8], &[u8]>,
    alt_complete!(
          chain!(
                not!(peek!(sql_keyword)) ~
                ident: take_while1!(is_sql_identifier),
                || { ident }
          )
        | delimited!(tag!("`"), take_while1!(is_sql_identifier), tag!("`"))
        | delimited!(tag!("'"), take_while1!(is_sql_identifier), tag!("'"))
    )
);

/// Parse an unsigned integer.
named!(pub unsigned_number<&[u8], u64>,
    map_res!(
        map_res!(digit, str::from_utf8),
        FromStr::from_str
    )
);

/// Parse a terminator that ends a SQL statement.
named!(pub statement_terminator,
    delimited!(opt!(multispace),
               alt_complete!(tag!(";") | line_ending | eof),
               opt!(multispace)
    )
);

/// Parse binary comparison operators
named!(pub binary_comparison_operator<&[u8], Operator>,
    alt_complete!(
           map!(caseless_tag!("not_like"), |_| Operator::NotLike)
         | map!(caseless_tag!("like"), |_| Operator::Like)
         | map!(caseless_tag!("!="), |_| Operator::NotEqual)
         | map!(caseless_tag!("<>"), |_| Operator::NotEqual)
         | map!(caseless_tag!(">="), |_| Operator::GreaterOrEqual)
         | map!(caseless_tag!("<="), |_| Operator::LessOrEqual)
         | map!(caseless_tag!("="), |_| Operator::Equal)
         | map!(caseless_tag!("<"), |_| Operator::Less)
         | map!(caseless_tag!(">"), |_| Operator::Greater)
    )
);

/// Parse rule for AS-based aliases for SQL entities.
named!(pub as_alias<&[u8], &str>,
    complete!(
        chain!(
            multispace ~
            opt!(chain!(caseless_tag!("as") ~ multispace, ||{})) ~
            alias: map_res!(sql_identifier, str::from_utf8),
            || { alias }
        )
    )
);

/// Parse rule for a comma-separated list of fields without aliases.
named!(pub field_list<&[u8], Vec<Column> >,
       many0!(
           chain!(
               fieldname: column_identifier_no_alias ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       ||{}
                   ))
               ),
               || { fieldname }
           )
       )
);

/// Parse list of column/field definitions.
named!(pub field_definition_expr<&[u8], Vec<FieldExpression>>,
       many0!(
           chain!(
               field: alt_complete!(
                   chain!(
                       tag!("*"),
                       || {
                           FieldExpression::All
                       }
                   )
                 | chain!(
                     table: table_reference ~ tag!(".*"),
                     || {
                         FieldExpression::AllInTable(table.name.clone())
                     }
                 )
                 | chain!(
                     column: column_identifier,
                     || {
                         FieldExpression::Col(column)
                     }
                 )
               ) ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                           tag!(",") ~
                           multispace?,
                       ||{}
                   ))
               ),
               || { field }
           )
       )
);

/// Parse list of table names.
/// XXX(malte): add support for aliases
named!(pub table_list<&[u8], Vec<Table> >,
       many0!(
           chain!(
               table: table_reference ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       || {}
                   ))
               ),
               || { table }
           )
       )
);

/// Integer literal value
named!(pub integer_literal<&[u8], Literal>,
    chain!(
        val: delimited!(opt!(multispace), digit, opt!(multispace)),
        || {
            let intval = i64::from_str(str::from_utf8(val).unwrap()).unwrap();
            Literal::Integer(intval)
        }
    )
);

/// String literal value
named!(pub string_literal<&[u8], Literal>,
    chain!(
        val: delimited!(opt!(multispace),
                 alt_complete!(
                       delimited!(tag!("\""), opt!(take_until!("\"")), tag!("\""))
                     | delimited!(tag!("'"), opt!(take_until!("'")), tag!("'"))
                 ),
                 opt!(multispace)
              ),
        || {
            let val = val.unwrap_or("".as_bytes());
            let s = String::from(str::from_utf8(val).unwrap());
            Literal::String(s)
        }
    )
);

/// Any literal value.
named!(pub literal<&[u8], Literal>,
    alt_complete!(
          integer_literal
        | string_literal
        | chain!(caseless_tag!("NULL"), || Literal::Null)
        | chain!(caseless_tag!("CURRENT_TIMESTAMP"), || Literal::CurrentTimestamp)
        | chain!(caseless_tag!("CURRENT_DATE"), || Literal::CurrentDate)
        | chain!(caseless_tag!("CURRENT_TIME"), || Literal::CurrentTime)
//        | float_literal
    )
);

/// Parse a list of values (e.g., for INSERT syntax).
named!(pub value_list<&[u8], Vec<Literal> >,
       many0!(
           chain!(
               val: literal ~
               opt!(
                   complete!(chain!(
                       multispace? ~
                       tag!(",") ~
                       multispace?,
                       ||{}
                   ))
               ),
               || { val }
           )
       )
);

/// Parse a reference to a named table
/// XXX(malte): add support for schema.table notation
named!(pub table_reference<&[u8], Table>,
    chain!(
        table: map_res!(sql_identifier, str::from_utf8) ~
        alias: opt!(as_alias),
        || {
            Table {
                name: String::from(table),
                alias: match alias {
                    Some(a) => Some(String::from(a)),
                    None => None,
                }
            }
        }
    )
);

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

    #[test]
    fn sql_identifiers() {
        let id1 = b"foo";
        let id2 = b"f_o_o";
        let id3 = b"foo12";
        let id4 = b":fo oo";
        let id5 = b"primary ";
        let id6 = b"`primary`";

        assert!(sql_identifier(id1).is_done());
        assert!(sql_identifier(id2).is_done());
        assert!(sql_identifier(id3).is_done());
        assert!(sql_identifier(id4).is_err());
        assert!(sql_identifier(id5).is_err());
        assert!(sql_identifier(id6).is_done());
    }

    #[test]
    fn simple_column_function() {
        let qs = b"max(addr_id)";

        let res = column_identifier(qs);
        let expected = Column {
            name: String::from("max(addr_id)"),
            alias: None,
            table: None,
            function: Some(Box::new(FunctionExpression::Max(Column::from("addr_id")))),
        };
        assert_eq!(res.unwrap().1, expected);
    }
}
<|endoftext|>


<commit_before>/* The info hash is stored in mongo, which is not binary string safe apparently,
so it needs to be parsed to a hexadecimal string rather than a binary one. */
pub fn parse_info_hash(input: &str) -> Result<String, String> {
    let mut output = String::new();
    let mut input_iterator = input.as_bytes().into_iter();
    while let Some(i) = input_iterator.next() {
        match i {
            &37u8 => { //% in ASCII
                let hexdigits = (input_iterator.next(), input_iterator.next());
                match hexdigits {
                    (Some(hex1), Some(hex2)) => {
                        output.push_str(&String::from_utf8_lossy(&[hex1.clone()]).to_lowercase());
                        output.push_str(&String::from_utf8_lossy(&[hex2.clone()]).to_lowercase());
                    },
                    _ => {},
                }
            }
            _ => {
                output.push_str(&format!("{:X}", i));
            }
        }
    }

    match output.len() {
        40 => return Ok(output),
        _ => return Err("Hash is invalid (too short).".to_string()),
    }
}

#[test]
fn parse_info_hash_test() {
    // Success
    assert_eq!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A").unwrap(), "123456789abcdef123456789abcdef123456789a");

    // Failures
    assert!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx").is_err()); // too short
    assert!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vxab").is_err()); // too long
    assert!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%ZA").is_err()); // invalid percent encoding
}
<commit_msg>parse_info_hash: Address invalid percent encoding<commit_after>/* The info hash is stored in mongo, which is not binary string safe apparently,
so it needs to be parsed to a hexadecimal string rather than a binary one. */
pub fn parse_info_hash(input: &str) -> Result<String, String> {
    let mut output = String::new();
    let mut input_iterator = input.as_bytes().into_iter();
    while let Some(i) = input_iterator.next() {
        match i {
            &37u8 => { //% in ASCII
                let hexdigits = (input_iterator.next(), input_iterator.next());
                match hexdigits {
                    (Some(hex1), Some(hex2)) => {
                        output.push_str(&String::from_utf8_lossy(&[hex1.clone()]).to_lowercase());
                        output.push_str(&String::from_utf8_lossy(&[hex2.clone()]).to_lowercase());
                    },
                    _ => {},
                }
            }
            _ => {
                output.push_str(&format!("{:X}", i));
            }
        }
    }

    const VALID_CHARACTERS : &'static [char] = &['a', 'b', 'c', 'd',
        'e', 'f', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
    for i in output.chars() {
        match VALID_CHARACTERS.iter().find(|&&j| j == i) {
            Some(_) => {},
            None => return Err("Hash is invalid (encoding).".to_string()),
        }
    }

    if output.len() != 40 {
        return Err("Hash is invalid (too short).".to_string());
    }

    return Ok(output);
}

#[test]
fn parse_info_hash_test() {
    // Success
    assert_eq!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A").unwrap(), "123456789abcdef123456789abcdef123456789a");

    // Failures
    assert!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx").is_err()); // too short
    assert!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vxab").is_err()); // too long
    assert!(parse_info_hash("%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%ZA").is_err()); // invalid percent encoding
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(non_snake_case)]

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts,
    FcConfigSubstitute, FcDefaultSubstitute,
    FcFontMatch,
    FcNameParse, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy,
    FcMatchPattern,
    FcPatternCreate, FcPatternAddString,
    FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use util::str::c_str_to_string;

use libc;
use libc::{c_int, c_char};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::ptr;

static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";

pub fn get_available_families<F>(mut callback: F) where F: FnMut(String) {
    unsafe {
        let config = FcConfigGetCurrent();
        let fontSet = FcConfigGetFonts(config, FcSetSystem);
        for i in 0..((*fontSet).nfont as isize) {
            let font = (*fontSet).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::null_mut();
            let mut v: c_int = 0;
            while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) == FcResultMatch {
                let family_name = c_str_to_string(family as *const c_char);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn get_variations_for_family<F>(family_name: &str, mut callback: F)
    where F: FnMut(String)
{
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(!pattern.is_null());
        let family_name_c = CString::new(family_name).unwrap();
        let family_name = family_name_c.as_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(!object_set.is_null());

        FcObjectSetAdd(object_set, FC_FILE.as_ptr() as *mut c_char);
        FcObjectSetAdd(object_set, FC_INDEX.as_ptr() as *mut c_char);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in 0..((*matches).nfont as isize) {
            let font = (*matches).fonts.offset(i);
            let mut file: *mut FcChar8 = ptr::null_mut();
            let file = if FcPatternGetString(*font, FC_FILE.as_ptr() as *mut c_char, 0, &mut file) == FcResultMatch {
                c_str_to_string(file as *const c_char)
            } else {
                panic!();
            };
            let mut index: libc::c_int = 0;
            let index = if FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index) == FcResultMatch {
                index
            } else {
                panic!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn get_system_default_family(generic_name: &str) -> Option<String> {
    let generic_name_c = CString::new(generic_name).unwrap();
    let generic_name_ptr = generic_name_c.as_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::null_mut(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::null_mut(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut match_string: *mut FcChar8 = ptr::null_mut();
            FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut c_char, 0, &mut match_string);
            let result = c_str_to_string(match_string as *const c_char);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

#[cfg(target_os="linux")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_owned(),
        "DejaVu Sans".to_owned(),
        "Arial".to_owned()
    )
}

#[cfg(target_os="android")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!("Roboto".to_owned())
}
<commit_msg>Avoid selecting bitmap fonts from fontconfig<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(non_snake_case)]

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts,
    FcConfigSubstitute, FcDefaultSubstitute,
    FcFontMatch,
    FcNameParse, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy,
    FcMatchPattern,
    FcPatternCreate, FcPatternAddString,
    FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use util::str::c_str_to_string;

use libc;
use libc::{c_int, c_char};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::ptr;

static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";
static FC_FONTFORMAT: &'static [u8] = b"fontformat\0";

pub fn get_available_families<F>(mut callback: F) where F: FnMut(String) {
    unsafe {
        let config = FcConfigGetCurrent();
        let fontSet = FcConfigGetFonts(config, FcSetSystem);
        for i in 0..((*fontSet).nfont as isize) {
            let font = (*fontSet).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::null_mut();
            let mut format: *mut FcChar8 = ptr::null_mut();
            let mut v: c_int = 0;
            if FcPatternGetString(*font, FC_FONTFORMAT.as_ptr() as *mut c_char, v, &mut format) != FcResultMatch {
                continue;
            }

            // Skip bitmap fonts. They aren't supported by FreeType.
            let fontformat = c_str_to_string(format as *const c_char);
            if fontformat != "TrueType" &&
               fontformat != "CFF" &&
               fontformat != "Type 1" {
                continue;
            }

            while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) == FcResultMatch {
                let family_name = c_str_to_string(family as *const c_char);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn get_variations_for_family<F>(family_name: &str, mut callback: F)
    where F: FnMut(String)
{
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(!pattern.is_null());
        let family_name_c = CString::new(family_name).unwrap();
        let family_name = family_name_c.as_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(!object_set.is_null());

        FcObjectSetAdd(object_set, FC_FILE.as_ptr() as *mut c_char);
        FcObjectSetAdd(object_set, FC_INDEX.as_ptr() as *mut c_char);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in 0..((*matches).nfont as isize) {
            let font = (*matches).fonts.offset(i);
            let mut file: *mut FcChar8 = ptr::null_mut();
            let file = if FcPatternGetString(*font, FC_FILE.as_ptr() as *mut c_char, 0, &mut file) == FcResultMatch {
                c_str_to_string(file as *const c_char)
            } else {
                panic!();
            };
            let mut index: libc::c_int = 0;
            let index = if FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index) == FcResultMatch {
                index
            } else {
                panic!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn get_system_default_family(generic_name: &str) -> Option<String> {
    let generic_name_c = CString::new(generic_name).unwrap();
    let generic_name_ptr = generic_name_c.as_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::null_mut(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::null_mut(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut match_string: *mut FcChar8 = ptr::null_mut();
            FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut c_char, 0, &mut match_string);
            let result = c_str_to_string(match_string as *const c_char);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

#[cfg(target_os="linux")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_owned(),
        "DejaVu Sans".to_owned(),
        "Arial".to_owned()
    )
}

#[cfg(target_os="android")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!("Roboto".to_owned())
}
<|endoftext|>


<commit_before>use std::error::Error;
use yaml_rust::yaml::YamlLoader;

pub struct Config {
    source_dir: String,
}

impl Config {
    pub fn new(config_string: String) -> Self {
        let yaml = match YamlLoader::load_from_str(&config_string) {
            Ok(yaml) => yaml,
            Err(what) => panic!("{}", Error::description(&what))
        };

        let yaml = yaml.get(0).unwrap();

        let mut config = Config {
            source_dir: String::new()
        };

        if let Some(source) = yaml["source"].as_str() {
            config.source_dir = String::from(source);
        }

        config
    }
}

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

    fn setup() -> String {
        String::from("source: pages\n\
            output: _site")
    }

    #[test]
    fn it_parses_input_directory_option_in_config() {
        let config_string = setup();

        let config = Config::new(config_string);

        assert_eq!("pages", config.source_dir);
    }
}
<commit_msg>feat: Change output directory from yaml config<commit_after>use std::error::Error;
use yaml_rust::yaml::YamlLoader;

pub struct Config {
    source_dir: String,
    output_dir: String,
}

impl Config {
    pub fn new(config_string: String) -> Self {
        let yaml = match YamlLoader::load_from_str(&config_string) {
            Ok(yaml) => yaml,
            Err(what) => panic!("{}", Error::description(&what))
        };

        let yaml = yaml.get(0).unwrap();

        let mut config = Config {
            source_dir: String::new(),
            output_dir: String::new(),
        };

        if let Some(source) = yaml["source"].as_str() {
            config.source_dir = String::from(source);
        }

        if let Some(output) = yaml["output"].as_str() {
            config.output_dir = String::from(output);
        }

        config
    }
}

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

    fn setup() -> String {
        String::from("source: pages\n\
            output: _site")
    }

    #[test]
    fn it_parses_input_directory_option_in_config() {
        let config_string = setup();

        let config = Config::new(config_string);

        assert_eq!("pages", config.source_dir);
    }

    #[test]
    fn it_parses_output_directory_option_in_config() {
        let config_string = setup();

        let config = Config::new(config_string);

        assert_eq!("_site", config.output_dir);
    }
}
<|endoftext|>