<commit_before>use std::collections::BTreeMap;
use rustc_serialize::json::{ToJson, Json};
use postgres;
use uuid::Uuid;
use std::fmt;

use youtube;
use soundcloud;
use error::Error;
use super::{conn, PaginatedCollection};

#[derive(Debug, Copy, Clone)]
pub enum Provider {
    YouTube,
    SoundCloud,
    Raw
}
impl PartialEq for Provider {
    fn eq(&self, p: &Provider) -> bool {
        match *self {
            Provider::YouTube    => match *p { Provider::YouTube    => true, _ => false },
            Provider::SoundCloud => match *p { Provider::SoundCloud => true, _ => false },
            Provider::Raw        => match *p { Provider::Raw        => true, _ => false }
        }
    }
}
impl Provider {
    fn to_string(&self) -> String {
        match *self {
            Provider::YouTube    => "YouTube",
            Provider::SoundCloud => "SoundCloud",
            Provider::Raw        => "Raw",
        }.to_string()
    }
    pub fn new(str: String) -> Provider {
        match str.as_ref() {
            "YouTube"    => Provider::YouTube,
            "youtube"    => Provider::YouTube,
            "SoundCloud" => Provider::SoundCloud,
            "soundcloud" => Provider::SoundCloud,
            _            => Provider::Raw,
        }
    }
}

impl ToJson for Provider {
    fn to_json(&self) -> Json {
        self.to_string().to_json()
    }
}

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

#[derive(Debug, Clone)]
pub struct Track {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub url:           String,
    pub title:         String,
    pub description:   Option<String>,
    pub artist:        Option<String>,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub duration:      i32,
}

impl PartialEq for Track {
    fn eq(&self, t: &Track) -> bool {
        return self.identifier == t.identifier && self.provider == t.provider
    }
}

impl ToJson for Track {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("id".to_string()           , self.id.to_string().to_json());
        d.insert("provider".to_string()     , self.provider.to_json());
        d.insert("identifier".to_string()   , self.identifier.to_json());
        d.insert("url".to_string()          , self.url.to_json());
        d.insert("title".to_string()        , self.title.to_json());
        d.insert("description".to_string()  , self.description.to_json());
        d.insert("artist".to_string()       , self.artist.to_json());
        d.insert("thumbnail_url".to_string(), self.thumbnail_url.to_json());
        d.insert("artwork_url".to_string()  , self.artwork_url.to_json());
        d.insert("duration".to_string()     , self.duration.to_json());
        Json::Object(d)
    }
}

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

impl Track {
    pub fn new(provider: Provider, identifier: String) -> Track {
        Track {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            url:           "".to_string(),
            title:         "".to_string(),
            description:   None,
            artist:        None,
            thumbnail_url: None,
            artwork_url:   None,
            duration:      0,
        }
    }
    pub fn from_yt_playlist_item(item: &youtube::PlaylistItem) -> Track {
        let identifier = (*item).snippet.resourceId["videoId"].to_string();
        Track::new(Provider::YouTube, identifier.to_string())
            .update_with_yt_playlist_item(item)
            .clone()
    }
    pub fn from_sc_track(track: &soundcloud::Track) -> Track {
        Track::new(Provider::SoundCloud, (*track).id.to_string())
            .update_with_sc_track(track)
            .clone()
    }

    pub fn update_with_yt_video(&mut self, video: &youtube::Video) -> &mut Track {
        self.provider      = Provider::YouTube;
        self.identifier    = video.id.to_string();
        self.url           = format!("https://www.youtube.com/watch/?v={}", video.id);
        self.title         = video.snippet.title.to_string();
        self.description   = Some(video.snippet.description.to_string());
        self.artist        = Some(video.snippet.channelTitle.to_string());
        self.thumbnail_url = Some(video.snippet.thumbnails["default"].url.to_string());
        self.artwork_url   = Some(video.snippet.thumbnails["maxres"].url.to_string());
        self
    }

    pub fn update_with_yt_playlist_item(&mut self, item: &youtube::PlaylistItem) -> &mut Track {
        self.provider      = Provider::YouTube;
        self.identifier    = item.id.to_string();
        self.url           = format!("https://www.youtube.com/watch/?v={}", item.id);
        self.title         = item.snippet.title.to_string();
        self.description   = Some(item.snippet.description.to_string());
        self.artist        = Some(item.snippet.channelTitle.to_string());
        self.thumbnail_url = Some(item.snippet.thumbnails["default"].url.to_string());
        self.artwork_url   = Some(item.snippet.thumbnails["maxres"].url.to_string());
        self
    }

    pub fn update_with_sc_track(&mut self, track: &soundcloud::Track) -> &mut Track {
        self.provider      = Provider::SoundCloud;
        self.identifier    = track.id.to_string();
        self.url           = track.permalink_url.to_string();
        self.title         = track.title.to_string();
        self.description   = Some(track.description.to_string());
        self.artist        = track.artist.clone();
        self.thumbnail_url = track.artwork_url.clone();
        self.artwork_url   = track.artwork_url.clone();
        self
    }

    fn rows_to_tracks(rows: postgres::rows::Rows) -> Vec<Track> {
        let mut tracks = Vec::new();
        for row in rows.iter() {
            let track = Track {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                url:           row.get(3),
                title:         row.get(4),
                description:   row.get(5),
                artist:        row.get(6),
                thumbnail_url: row.get(7),
                artwork_url:   row.get(8),
                duration:      row.get(9)
            };
            tracks.push(track)
        }
        tracks
    }

    pub fn find_by_id(id: &str) -> Result<Track, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, identifier, url, title, description,
                                        artist, thumbnail_url, artwork_url, duration
                                 FROM tracks WHERE id = $1").unwrap();
        let uuid   = try!(Uuid::parse_str(id).map_err(|_| Error::Unprocessable));
        let rows   = stmt.query(&[&uuid]).unwrap();
        let tracks = Track::rows_to_tracks(rows);
        if tracks.len() > 0 {
            return Ok(tracks[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_by(provider: &Provider, identifier: &str) -> Result<Track, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, identifier, url, title, description,
                                        artist, thumbnail_url, artwork_url, duration
                                 FROM tracks WHERE provider = $1
                                 AND identifier = $2").unwrap();
        let rows = stmt.query(&[&(*provider).to_string(), &identifier]).unwrap();
        let tracks = Track::rows_to_tracks(rows);
        if tracks.len() > 0 {
            return Ok(tracks[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_by_entry_id(entry_id: Uuid) -> Vec<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT t.id, t.provider, t.identifier, t.url, t.title, t.description,
                                        t.artist, t.thumbnail_url, t.artwork_url, t.duration
                                 FROM tracks t LEFT JOIN track_entries te
                                 ON t.id = te.track_id
                                 WHERE te.entry_id = $1").unwrap();
        let rows = stmt.query(&[&entry_id]).unwrap();
        Track::rows_to_tracks(rows)
    }

    pub fn find(page: i64, per_page: i64) -> PaginatedCollection<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, identifier, url, title, description,
                                        artist, thumbnail_url, artwork_url, duration
                                 FROM tracks LIMIT $2 OFFSET $1").unwrap();
        let offset = page * per_page;
        let rows   = stmt.query(&[&offset, &per_page]).unwrap();
        let tracks = Track::rows_to_tracks(rows);
        let mut total: i64 = 0;
        for row in conn.query("SELECT COUNT(*) FROM tracks", &[]).unwrap().iter() {
            total = row.get(0);
        }
        PaginatedCollection {
            page:     page,
            per_page: per_page,
            total:    total,
            items:    tracks,
        }
    }

    pub fn create(&self) -> Result<Track, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO tracks (provider, identifier, url, title)
                                 VALUES ($1, $2, $3, $4) RETURNING id").unwrap();
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.title]));
        let mut track = self.clone();
        for row in rows.iter() {
            track.id = row.get(0);
        }
        Ok(track)
    }

    pub fn find_or_create(provider: Provider, identifier: String) -> Result<Track, Error> {
        return match Track::find_by(&provider, &identifier) {
            Ok(track) => Ok(track),
            Err(_)    => Track::new(provider, identifier).create()
        }
    }

    pub fn save(&self) -> Result<(), Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE tracks SET
                                 provider      = $2,
                                 identifier    = $3,
                                 url           = $4,
                                 title         = $5,
                                 description   = $6,
                                 artist        = $7,
                                 thumbnail_url = $8,
                                 artwork_url   = $9,
                                 duration      = $10
                                 WHERE id = $1").unwrap();
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.artist,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.duration]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected)
        }
    }
}

pub struct Playlist {
    pub title:  String,
    pub tracks: Vec<Track>,
}

<commit_msg>Add published_at and state property to model::Track struct<commit_after>use std::collections::BTreeMap;
use rustc_serialize::json::{ToJson, Json};
use postgres;
use uuid::Uuid;
use std::fmt;
use chrono::{NaiveDateTime, UTC, DateTime};

use youtube;
use youtube::HasThumbnail;
use soundcloud;
use error::Error;
use super::{conn, PaginatedCollection};

#[derive(Debug, Copy, Clone)]
pub enum Provider {
    YouTube,
    SoundCloud,
    Raw
}

impl PartialEq for Provider {
    fn eq(&self, p: &Provider) -> bool {
        match *self {
            Provider::YouTube    => match *p { Provider::YouTube    => true, _ => false },
            Provider::SoundCloud => match *p { Provider::SoundCloud => true, _ => false },
            Provider::Raw        => match *p { Provider::Raw        => true, _ => false }
        }
    }
}

impl Provider {
    fn to_string(&self) -> String {
        match *self {
            Provider::YouTube    => "YouTube",
            Provider::SoundCloud => "SoundCloud",
            Provider::Raw        => "Raw",
        }.to_string()
    }
    pub fn new(str: String) -> Provider {
        match str.as_ref() {
            "YouTube"    => Provider::YouTube,
            "youtube"    => Provider::YouTube,
            "SoundCloud" => Provider::SoundCloud,
            "soundcloud" => Provider::SoundCloud,
            _            => Provider::Raw,
        }
    }
}

impl ToJson for Provider {
    fn to_json(&self) -> Json {
        self.to_string().to_json()
    }
}

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

#[derive(Debug, Copy, Clone)]
pub enum State {
    Alive,
    Dead,
}

impl PartialEq for State {
    fn eq(&self, p: &State) -> bool {
        match *self {
            State::Alive => match *p { State::Alive => true, _ => false },
            State::Dead  => match *p { State::Dead  => true, _ => false },
        }
    }
}

impl State {
    fn to_string(&self) -> String {
        match *self {
            State::Alive   => "alive",
            State::Dead => "dead",
        }.to_string()
    }
    pub fn new(str: String) -> State {
        match str.as_ref() {
            "alive" => State::Alive,
            "dead"  => State::Dead,
            _       => State::Dead,
        }
    }
}

impl ToJson for State {
    fn to_json(&self) -> Json {
        self.to_string().to_json()
    }
}

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

#[derive(Debug, Clone)]
pub struct Track {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub url:           String,
    pub title:         String,
    pub description:   Option<String>,
    pub artist:        Option<String>,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub duration:      i32,
    pub published_at:  NaiveDateTime,
    pub state:         State,
}

impl PartialEq for Track {
    fn eq(&self, t: &Track) -> bool {
        return self.identifier == t.identifier && self.provider == t.provider
    }
}

impl ToJson for Track {
    fn to_json(&self) -> Json {
        let published_at = DateTime::<UTC>::from_utc(self.published_at, UTC);
        let mut d = BTreeMap::new();
        d.insert("id".to_string()           , self.id.to_string().to_json());
        d.insert("provider".to_string()     , self.provider.to_json());
        d.insert("identifier".to_string()   , self.identifier.to_json());
        d.insert("url".to_string()          , self.url.to_json());
        d.insert("title".to_string()        , self.title.to_json());
        d.insert("description".to_string()  , self.description.to_json());
        d.insert("artist".to_string()       , self.artist.to_json());
        d.insert("thumbnail_url".to_string(), self.thumbnail_url.to_json());
        d.insert("artwork_url".to_string()  , self.artwork_url.to_json());
        d.insert("duration".to_string()     , self.duration.to_json());
        d.insert("published_at".to_string() , published_at.to_rfc3339().to_json());
        d.insert("state".to_string()        , self.state.to_json());
        Json::Object(d)
    }
}

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

impl Track {
    pub fn new(provider: Provider, identifier: String) -> Track {
        Track {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            url:           "".to_string(),
            title:         "".to_string(),
            description:   None,
            artist:        None,
            thumbnail_url: None,
            artwork_url:   None,
            duration:      0,
            published_at:  UTC::now().naive_utc(),
            state:         State::Alive,
        }
    }
    pub fn from_yt_playlist_item(item: &youtube::PlaylistItem) -> Track {
        let identifier = (*item).snippet.resourceId["videoId"].to_string();
        Track::new(Provider::YouTube, identifier.to_string())
            .update_with_yt_playlist_item(item)
            .clone()
    }
    pub fn from_sc_track(track: &soundcloud::Track) -> Track {
        Track::new(Provider::SoundCloud, (*track).id.to_string())
            .update_with_sc_track(track)
            .clone()
    }

    pub fn update_with_yt_video(&mut self, video: &youtube::Video) -> &mut Track {
        self.provider      = Provider::YouTube;
        self.identifier    = video.id.to_string();
        self.url           = format!("https://www.youtube.com/watch/?v={}", video.id);
        self.title         = video.snippet.title.to_string();
        self.description   = Some(video.snippet.description.to_string());
        self.artist        = Some(video.snippet.channelTitle.to_string());
        self.thumbnail_url = video.snippet.get_thumbnail_url();
        self.artwork_url   = video.snippet.get_artwork_url();
        match DateTime::parse_from_rfc3339(&video.snippet.publishedAt) {
            Ok(published_at) => self.published_at = published_at.naive_utc(),
            Err(_)           => (),
        }
        self.state = State::Alive;
        self
    }

    pub fn update_with_yt_playlist_item(&mut self, item: &youtube::PlaylistItem) -> &mut Track {
        self.provider      = Provider::YouTube;
        self.identifier    = item.id.to_string();
        self.url           = format!("https://www.youtube.com/watch/?v={}", item.id);
        self.title         = item.snippet.title.to_string();
        self.description   = Some(item.snippet.description.to_string());
        self.artist        = Some(item.snippet.channelTitle.to_string());
        self.thumbnail_url = item.snippet.get_thumbnail_url();
        self.artwork_url   = item.snippet.get_artwork_url();
        match DateTime::parse_from_rfc3339(&item.snippet.publishedAt) {
            Ok(published_at) => self.published_at = published_at.naive_utc(),
            Err(_)           => (),
        }
        self.state = State::Alive;
        self
    }

    pub fn update_with_sc_track(&mut self, track: &soundcloud::Track) -> &mut Track {
        self.provider      = Provider::SoundCloud;
        self.identifier    = track.id.to_string();
        self.url           = track.permalink_url.to_string();
        self.title         = track.title.to_string();
        self.description   = Some(track.description.to_string());
        self.artist        = Some(track.user.username.clone());
        self.thumbnail_url = track.artwork_url.clone();
        self.artwork_url   = track.artwork_url.clone();
        match DateTime::parse_from_str(&track.created_at, "%Y/%m/%d %H:%M:%S %z") {
            Ok(published_at) => self.published_at = published_at.naive_utc(),
            Err(_)           => (),
        }
        self.state = State::Alive;
        self
    }

    pub fn disable(&mut self) -> &mut Track {
        self.state = State::Dead;
        self
    }

    fn rows_to_tracks(rows: postgres::rows::Rows) -> Vec<Track> {
        let mut tracks = Vec::new();
        for row in rows.iter() {
            let track = Track {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                url:           row.get(3),
                title:         row.get(4),
                description:   row.get(5),
                artist:        row.get(6),
                thumbnail_url: row.get(7),
                artwork_url:   row.get(8),
                duration:      row.get(9),
                published_at:  row.get(10),
                state:         State::new(row.get(11)),
            };
            tracks.push(track)
        }
        tracks
    }

    pub fn find_by_id(id: &str) -> Result<Track, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, identifier, url, title, description,
                                        artist, thumbnail_url, artwork_url, duration,
                                        published_at, state
                                 FROM tracks WHERE id = $1").unwrap();
        let uuid   = try!(Uuid::parse_str(id).map_err(|_| Error::Unprocessable));
        let rows   = stmt.query(&[&uuid]).unwrap();
        let tracks = Track::rows_to_tracks(rows);
        if tracks.len() > 0 {
            return Ok(tracks[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_by(provider: &Provider, identifier: &str) -> Result<Track, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, identifier, url, title, description,
                                        artist, thumbnail_url, artwork_url, duration,
                                        published_at, state
                                 FROM tracks
                                 WHERE provider = $1 AND identifier = $2
                                 ORDER BY published_at DESC").unwrap();
        let rows = stmt.query(&[&(*provider).to_string(), &identifier]).unwrap();
        let tracks = Track::rows_to_tracks(rows);
        if tracks.len() > 0 {
            return Ok(tracks[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_by_entry_id(entry_id: Uuid) -> Vec<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT t.id, t.provider, t.identifier, t.url, t.title, t.description,
                                        t.artist, t.thumbnail_url, t.artwork_url,
                                        t.duration, t.published_at, t.state
                                 FROM tracks t LEFT JOIN track_entries te
                                 ON t.id = te.track_id
                                 WHERE te.entry_id = $1
                                 ORDER BY t.published_at DESC").unwrap();
        let rows = stmt.query(&[&entry_id]).unwrap();
        Track::rows_to_tracks(rows)
    }

    pub fn find(page: i64, per_page: i64) -> PaginatedCollection<Track> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("SELECT id, provider, identifier, url, title, description,
                                        artist, thumbnail_url, artwork_url, duration,
                                        published_at, state
                                 FROM tracks
                                 ORDER BY published_at DESC
                                 LIMIT $2 OFFSET $1
                                 ").unwrap();
        let offset = page * per_page;
        let rows   = stmt.query(&[&offset, &per_page]).unwrap();
        let tracks = Track::rows_to_tracks(rows);
        let mut total: i64 = 0;
        for row in conn.query("SELECT COUNT(*) FROM tracks", &[]).unwrap().iter() {
            total = row.get(0);
        }
        PaginatedCollection {
            page:     page,
            per_page: per_page,
            total:    total,
            items:    tracks,
        }
    }

    pub fn create(&self) -> Result<Track, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("INSERT INTO tracks (provider, identifier, url, title)
                                 VALUES ($1, $2, $3, $4) RETURNING id").unwrap();
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.title]));
        let mut track = self.clone();
        for row in rows.iter() {
            track.id = row.get(0);
        }
        Ok(track)
    }

    pub fn find_or_create(provider: Provider, identifier: String) -> Result<Track, Error> {
        return match Track::find_by(&provider, &identifier) {
            Ok(track) => Ok(track),
            Err(_)    => Track::new(provider, identifier).create()
        }
    }

    pub fn save(&self) -> Result<(), Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE tracks SET
                                 provider      = $2,
                                 identifier    = $3,
                                 url           = $4,
                                 title         = $5,
                                 description   = $6,
                                 artist        = $7,
                                 thumbnail_url = $8,
                                 artwork_url   = $9,
                                 duration      = $10,
                                 published_at  = $11,
                                 state         = $12
                                 WHERE id = $1").unwrap();
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.artist,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.duration,
                                  &self.published_at,
                                  &self.state.to_string(),
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected)
        }
    }
}

pub struct Playlist {
    pub title:  String,
    pub tracks: Vec<Track>,
}

<|endoftext|>


<commit_before>use game_state;
use std;
use rand;
use time;
use std::collections::HashSet;
use std::collections::HashMap;

#[derive(Debug, Copy, Clone)]
pub struct UCTData{
    pub wins : f64,
    pub num_plays : i32,
    pub win_tie : i32
}

impl UCTData{
    fn new(w : f64, n : i32) -> UCTData{
        UCTData{
            wins : w,
            num_plays : n,
            win_tie : 0
        }
    }

    fn win_percentage(&self) -> f64{
        (self.win_tie as f64 / self.num_plays as f64)
    }
}

pub struct TreePolicyResult{
    pub path : Vec<game_state::GameState>,
    pub expanded_node : game_state::GameState
}

impl TreePolicyResult{
    pub fn new(path : Vec<game_state::GameState>,
        expanded_node : game_state::GameState) -> TreePolicyResult{
            TreePolicyResult{
                path : path,
                expanded_node : expanded_node
            }
    }
}

fn ucb1(win_value : f64, number_played : f64, total_played : f64) -> f64{
    ((2f64 * total_played.ln()) / number_played).sqrt() + win_value / number_played
}

pub fn victory(end : game_state::End) -> bool{
    match end{
        game_state::End::Victory(_) => true,
        game_state::End::Tie => true,
        _ => false
    }
}

pub fn choose_random(possible_moves : &Vec<game_state::Move>) -> game_state::Move{
    let random_number = rand::random::<usize>() % possible_moves.len();
    let random_move = possible_moves[random_number].clone();
    return random_move;
}

pub fn run_simulation(state : game_state::GameState, player : game_state::Color) -> game_state::End{

    let mut current_state = state;
    while !victory(current_state.win()){
        let current_player = current_state.player;
        let possible_moves = state.legal_moves(current_player);
        if possible_moves.len() < 1{
            break;
        }
        let random_move = choose_random(&possible_moves);
        current_state = current_state.place(&random_move);
    }

    current_state.win()

}

fn get_result_value(result : game_state::End, player : game_state::Color) -> f64{
    match result{
        game_state::End::Tie => 0.5f64,
        game_state::End::Victory(color) =>{
            if color == player{
                1f64
            }
            else{
                0f64
            }
        },
        _ => 0f64
    }
}

fn get_tie_or_win(result : game_state::End, player : game_state::Color) -> i32{
    match result{
        game_state::End::Tie => 1,
        game_state::End::Victory(color) =>{
            if color == player{
                1
            }
            else{
                0
            }
        },
        _ => 0
    }
}

fn state_previous_player(state : &game_state::GameState) -> game_state::Color{
    //helper function because
    match state.player{
        game_state::Color::White => game_state::Color::Black,
        game_state::Color::Black => game_state::Color::White,
        _ => game_state::Color::White
    }
}

pub fn tree_search(root : game_state::GameState) -> game_state::Move{

    //keeps track of visisted states so we know if current state is a leaf
    let mut visited_states : HashSet<game_state::GameState> = std::collections::HashSet::new();
    visited_states.insert(root);
    let mut statistics : HashMap<game_state::GameState, UCTData> = HashMap::new();
    statistics.insert(root, UCTData::new(0f64, 0));

    let current_time = time::precise_time_s();
    //temp
    while time::precise_time_s() - current_time < 0.5f64{
        let current_state = root;

        //selection
        let selected_state = tree_policy(&current_state, &visited_states, &statistics);

        //expand
        if !visited_states.contains(&selected_state.expanded_node){
            statistics.insert(selected_state.expanded_node, UCTData::new(0f64, 0));
            visited_states.insert(selected_state.expanded_node);
        }

        //simulate
        let result = run_simulation(selected_state.expanded_node, root.player);

        //backpropogate
        back_propogate(result, &mut statistics, &selected_state.path);
    }

    let possible_moves = root.legal_moves(root.player).into_iter().map(|x| (x, statistics.get(&root.place(&x)).unwrap())).collect::<Vec<_>>();
    let best_move = optimal_move_most_visisted(&possible_moves);
    let data = statistics.get(&root.place(&best_move)).unwrap();
    println!("Puny human, I have thought through {} variations of this pitiful game, and won or tied in {}% of them", data.num_plays, data.win_percentage() * 100f64);
    return best_move;
    //let possible_states = possible_moves.into_iter().map(|x| root.place(&x)).map(|y| (y, statistics.get(&y).unwrap())).collect::<Vec<_>>();
}

fn optimal_move_highest_win(possible_moves : &Vec<(game_state::Move, &UCTData)>) -> game_state::Move{
    let mut highest_win = 0f64;
    let mut best_move = game_state::Move::white_new(0, 0);
    for &(mv, data) in possible_moves{
        if data.wins > highest_win{
            highest_win = data.wins;
            best_move = mv;
        }
    }
    return best_move;
}

fn optimal_move_most_visisted(possible_moves : &Vec<(game_state::Move, &UCTData)>) -> game_state::Move{
    let mut most_played = 0;
    let mut best_move = game_state::Move::white_new(0, 0);
    for &(mv, data) in possible_moves{
        if data.num_plays > most_played{
            most_played = data.num_plays;
            best_move = mv;
        }
    }
    return best_move;
}

pub fn tree_policy(
    current_state : &game_state::GameState,
    visisted_states : &HashSet<game_state::GameState>,
    stats : &HashMap<game_state::GameState, UCTData>
    ) -> TreePolicyResult{
    //selects a node to simulate

    //represents the states we went through to get to the selected node
    let mut path : Vec<game_state::GameState> = Vec::new();

    let mut current_node = current_state.clone();
    loop{

        path.push(current_node);

        let possible_moves = current_node.legal_moves(current_node.player);

        if possible_moves.len() < 1 || victory(current_node.win()){
            //no legal moves or game ends
            return TreePolicyResult::new(path, current_node);
        }

        //has every possible move been explored?
        let fully_explored = possible_moves.iter().fold(true,
            |acc, x|
            acc && visisted_states.contains(&current_node.place(x))
        );

        //exploration
        if !fully_explored {
            let not_explored = possible_moves.into_iter().filter(
                |x| !visisted_states.contains(&current_node.place(x))
                ).collect::<Vec<_>>();
            let random_choice = choose_random(&not_explored);
            let chosen_node = current_node.place(&random_choice);
            path.push(chosen_node);
            let result = TreePolicyResult::new(path, chosen_node);
            return result;
        }

        //fully explored, so pick a random one and continue
        //would use UCT
        else{
            //sort
            let mut best_move = possible_moves.last().unwrap();
            let mut best_uct = 0f64;
            let total_played = stats.get(&current_node).unwrap().num_plays;
            for possibility in possible_moves.iter(){

                //TODO: switch to pattern matching
                let data = stats.get(&current_node.place(&possibility)).unwrap();
                let uct = ucb1(data.wins, data.num_plays as f64, total_played as f64);
                if(uct > best_uct){
                    best_uct = uct;
                    best_move = possibility;
                }
            }
            let chosen_node = current_node.place(&best_move);
            current_node = chosen_node;
        }
    }
}

pub fn back_propogate(win_value : game_state::End, stats : &mut HashMap<game_state::GameState, UCTData>,
    path : &Vec<game_state::GameState>){
        for node in path.iter(){
            match stats.get_mut(node){
                Some(ref mut stat) =>{
                    stat.wins += get_result_value(win_value, state_previous_player(&node));
                    stat.num_plays += 1;
                    stat.win_tie += get_tie_or_win(win_value, state_previous_player(&node));
                }
                None => ()
            }
        }
}
<commit_msg>commenting, housekeeping<commit_after>use game_state;
use std;
use rand;
use time;
use std::collections::HashSet;
use std::collections::HashMap;

#[derive(Debug, Copy, Clone)]
pub struct UCTData{
    //represents data used by UCB1 to choose the best choice to explored
    //the win-tie field is for better statistics, not actually used
    pub wins : f64,
    pub num_plays : i32,
    pub win_tie : i32
}

impl UCTData{
    fn new(w : f64, n : i32) -> UCTData{
        UCTData{
            wins : w,
            num_plays : n,
            win_tie : 0
        }
    }

    fn win_percentage(&self) -> f64{
        (self.win_tie as f64 / self.num_plays as f64)
    }
}

pub struct TreePolicyResult{
    pub path : Vec<game_state::GameState>,
    pub expanded_node : game_state::GameState
}

impl TreePolicyResult{
    pub fn new(path : Vec<game_state::GameState>,
        expanded_node : game_state::GameState) -> TreePolicyResult{
            TreePolicyResult{
                path : path,
                expanded_node : expanded_node
            }
    }
}

fn ucb1(win_value : f64, number_played : f64, total_played : f64) -> f64{
    //weighs exploration and expected output
    ((2f64 * total_played.ln()) / number_played).sqrt() + win_value / number_played
}

pub fn victory(end : game_state::End) -> bool{
    //simple helper function
    match end{
        game_state::End::Victory(_) => true,
        game_state::End::Tie => true,
        _ => false
    }
}

pub fn choose_random(possible_moves : &Vec<game_state::Move>) -> game_state::Move{
    let random_number = rand::random::<usize>() % possible_moves.len();
    let random_move = possible_moves[random_number].clone();
    return random_move;
}

pub fn run_simulation(state : game_state::GameState, player : game_state::Color) -> game_state::End{
    //from a given state, it will continue to choose random legitimate options until one player wins or ties
    let mut current_state = state;
    while !victory(current_state.win()){
        let current_player = current_state.player;
        let possible_moves = state.legal_moves(current_player);
        if possible_moves.len() < 1{
            break;
        }
        let random_move = choose_random(&possible_moves);
        current_state = current_state.place(&random_move);
    }

    current_state.win()
}

fn get_result_value(result : game_state::End, player : game_state::Color) -> f64{
    //returns the "reward" of each multi-armed bandit
    //a tie is better than a loss, but not as good as a win
    match result{
        game_state::End::Tie => 0.5f64,
        game_state::End::Victory(color) =>{
            if color == player{
                1f64
            }
            else{
                0f64
            }
        },
        _ => 0f64
    }
}

fn get_tie_or_win(result : game_state::End, player : game_state::Color) -> i32{
    //same function, but ties are also one. for the tie-win statistic
    match result{
        game_state::End::Tie => 1,
        game_state::End::Victory(color) =>{
            if color == player{
                1
            }
            else{
                0
            }
        },
        _ => 0
    }
}

fn state_previous_player(state : &game_state::GameState) -> game_state::Color{
    //helper function
    //the board member player represents the player who goes next
    match state.player{
        game_state::Color::White => game_state::Color::Black,
        game_state::Color::Black => game_state::Color::White,
        _ => game_state::Color::White
    }
}

pub fn tree_search(root : game_state::GameState) -> game_state::Move{

    //keeps track of visisted states so we know if current state is a leaf
    let mut visited_states : HashSet<game_state::GameState> = std::collections::HashSet::new();
    visited_states.insert(root);
    let mut statistics : HashMap<game_state::GameState, UCTData> = HashMap::new();
    statistics.insert(root, UCTData::new(0f64, 0));

    let current_time = time::precise_time_s();
    //temp
    while time::precise_time_s() - current_time < 0.5f64{
        let current_state = root;

        //selection
        let selected_state = tree_policy(&current_state, &visited_states, &statistics);

        //expand
        if !visited_states.contains(&selected_state.expanded_node){
            statistics.insert(selected_state.expanded_node, UCTData::new(0f64, 0));
            visited_states.insert(selected_state.expanded_node);
        }

        //simulate
        let result = run_simulation(selected_state.expanded_node, root.player);

        //backpropogate
        back_propogate(result, &mut statistics, &selected_state.path);
    }

    let possible_moves = root.legal_moves(root.player).into_iter().map(|x| (x, statistics.get(&root.place(&x)).unwrap())).collect::<Vec<_>>();
    let best_move = optimal_move_most_visisted(&possible_moves);
    let data = statistics.get(&root.place(&best_move)).unwrap();
    println!("Puny human, I have thought through {} variations of this pitiful game, and won or tied in {}% of them", data.num_plays, data.win_percentage() * 100f64);
    return best_move;
}

fn optimal_move_highest_win(possible_moves : &Vec<(game_state::Move, &UCTData)>) -> game_state::Move{
    //selects the highest winning node as optimal
    let mut highest_win = 0f64;
    let mut best_move = game_state::Move::white_new(0, 0);
    for &(mv, data) in possible_moves{
        if data.wins > highest_win{
            highest_win = data.wins;
            best_move = mv;
        }
    }
    return best_move;
}

fn optimal_move_most_visisted(possible_moves : &Vec<(game_state::Move, &UCTData)>) -> game_state::Move{
    //selects the most visited node as optimal
    let mut most_played = 0;
    let mut best_move = game_state::Move::white_new(0, 0);
    for &(mv, data) in possible_moves{
        if data.num_plays > most_played{
            most_played = data.num_plays;
            best_move = mv;
        }
    }
    return best_move;
}

pub fn tree_policy(
    current_state : &game_state::GameState,
    visisted_states : &HashSet<game_state::GameState>,
    stats : &HashMap<game_state::GameState, UCTData>
    ) -> TreePolicyResult{

    //represents the states we went through to get to the selected node
    //used for backpropogation without an actual tree structure
    let mut path : Vec<game_state::GameState> = Vec::new();

    let mut current_node = current_state.clone();

    loop{

        path.push(current_node);

        let possible_moves = current_node.legal_moves(current_node.player);

        if possible_moves.len() < 1 || victory(current_node.win()){
            //no legal moves or game ends
            return TreePolicyResult::new(path, current_node);
        }

        //has every possible move been explored?
        let fully_explored = possible_moves.iter().fold(true,
            |acc, x|
            acc && visisted_states.contains(&current_node.place(x))
        );

        //if not, exploration
        if !fully_explored {
            //for a node with number played of 0, ucb1 returns infinity
            //in other words unexplored child nodes are always explored at least once
            let not_explored = possible_moves.into_iter().filter(
                |x| !visisted_states.contains(&current_node.place(x))
                ).collect::<Vec<_>>();
            let random_choice = choose_random(&not_explored);
            let chosen_node = current_node.place(&random_choice);
            path.push(chosen_node);
            let result = TreePolicyResult::new(path, chosen_node);
            return result;
        }

        //all child nodes have been simulated at least once, so use ucb1 to select best
        else{
            //sort
            let mut best_move = possible_moves.last().unwrap();
            let mut best_uct = 0f64;
            let total_played = stats.get(&current_node).unwrap().num_plays;
            for possibility in possible_moves.iter(){

                //TODO: switch to pattern matching
                let data = stats.get(&current_node.place(&possibility)).unwrap();
                let uct = ucb1(data.wins, data.num_plays as f64, total_played as f64);
                if(uct > best_uct){
                    best_uct = uct;
                    best_move = possibility;
                }
            }
            let chosen_node = current_node.place(&best_move);
            current_node = chosen_node;
        }
    }
}

pub fn back_propogate(win_value : game_state::End, stats : &mut HashMap<game_state::GameState, UCTData>,
    path : &Vec<game_state::GameState>){
        for node in path.iter(){
            match stats.get_mut(node){
                Some(ref mut stat) =>{
                    stat.wins += get_result_value(win_value, state_previous_player(&node));
                    stat.num_plays += 1;
                    stat.win_tie += get_tie_or_win(win_value, state_previous_player(&node));
                }
                None => ()
            }
        }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(const_fn)]
#![allow(const_err)]

use std::env;
use std::process::{Command, Stdio};

const fn bar() -> usize { 0 - 1 }

fn foo() {
    let _: &'static _ = &bar();
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() > 1 && args[1] == "test" {
        foo();
        return;
    }

    let mut p = Command::new(&args[0])
        .stdout(Stdio::piped())
        .stdin(Stdio::piped())
        .arg("test").output().unwrap();
    assert!(!p.status.success());
}
<commit_msg>don't spawn processes on wasm<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-wasm

#![feature(const_fn)]
#![allow(const_err)]

use std::env;
use std::process::{Command, Stdio};

const fn bar() -> usize { 0 - 1 }

fn foo() {
    let _: &'static _ = &bar();
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() > 1 && args[1] == "test" {
        foo();
        return;
    }

    let mut p = Command::new(&args[0])
        .stdout(Stdio::piped())
        .stdin(Stdio::piped())
        .arg("test").output().unwrap();
    assert!(!p.status.success());
}
<|endoftext|>


<commit_before>use core::ops::{Add, Sub, Mul, Div, Rem, Shl, Shr};

/// Performs addition that returns `None` instead of wrapping around on
/// overflow.
pub trait CheckedAdd: Sized + Add<Self, Output=Self> {
    /// Adds two numbers, checking for overflow. If overflow happens, `None` is
    /// returned.
    fn checked_add(&self, v: &Self) -> Option<Self>;
}

macro_rules! checked_impl {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self, v: &$t) -> Option<$t> {
                <$t>::$method(*self, *v)
            }
        }
    }
}

checked_impl!(CheckedAdd, checked_add, u8);
checked_impl!(CheckedAdd, checked_add, u16);
checked_impl!(CheckedAdd, checked_add, u32);
checked_impl!(CheckedAdd, checked_add, u64);
checked_impl!(CheckedAdd, checked_add, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedAdd, checked_add, u128);

checked_impl!(CheckedAdd, checked_add, i8);
checked_impl!(CheckedAdd, checked_add, i16);
checked_impl!(CheckedAdd, checked_add, i32);
checked_impl!(CheckedAdd, checked_add, i64);
checked_impl!(CheckedAdd, checked_add, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedAdd, checked_add, i128);

/// Performs subtraction that returns `None` instead of wrapping around on underflow.
pub trait CheckedSub: Sized + Sub<Self, Output=Self> {
    /// Subtracts two numbers, checking for underflow. If underflow happens,
    /// `None` is returned.
    fn checked_sub(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedSub, checked_sub, u8);
checked_impl!(CheckedSub, checked_sub, u16);
checked_impl!(CheckedSub, checked_sub, u32);
checked_impl!(CheckedSub, checked_sub, u64);
checked_impl!(CheckedSub, checked_sub, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedSub, checked_sub, u128);

checked_impl!(CheckedSub, checked_sub, i8);
checked_impl!(CheckedSub, checked_sub, i16);
checked_impl!(CheckedSub, checked_sub, i32);
checked_impl!(CheckedSub, checked_sub, i64);
checked_impl!(CheckedSub, checked_sub, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedSub, checked_sub, i128);

/// Performs multiplication that returns `None` instead of wrapping around on underflow or
/// overflow.
pub trait CheckedMul: Sized + Mul<Self, Output=Self> {
    /// Multiplies two numbers, checking for underflow or overflow. If underflow
    /// or overflow happens, `None` is returned.
    fn checked_mul(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedMul, checked_mul, u8);
checked_impl!(CheckedMul, checked_mul, u16);
checked_impl!(CheckedMul, checked_mul, u32);
checked_impl!(CheckedMul, checked_mul, u64);
checked_impl!(CheckedMul, checked_mul, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedMul, checked_mul, u128);

checked_impl!(CheckedMul, checked_mul, i8);
checked_impl!(CheckedMul, checked_mul, i16);
checked_impl!(CheckedMul, checked_mul, i32);
checked_impl!(CheckedMul, checked_mul, i64);
checked_impl!(CheckedMul, checked_mul, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedMul, checked_mul, i128);

/// Performs division that returns `None` instead of panicking on division by zero and instead of
/// wrapping around on underflow and overflow.
pub trait CheckedDiv: Sized + Div<Self, Output=Self> {
    /// Divides two numbers, checking for underflow, overflow and division by
    /// zero. If any of that happens, `None` is returned.
    fn checked_div(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedDiv, checked_div, u8);
checked_impl!(CheckedDiv, checked_div, u16);
checked_impl!(CheckedDiv, checked_div, u32);
checked_impl!(CheckedDiv, checked_div, u64);
checked_impl!(CheckedDiv, checked_div, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedDiv, checked_div, u128);

checked_impl!(CheckedDiv, checked_div, i8);
checked_impl!(CheckedDiv, checked_div, i16);
checked_impl!(CheckedDiv, checked_div, i32);
checked_impl!(CheckedDiv, checked_div, i64);
checked_impl!(CheckedDiv, checked_div, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedDiv, checked_div, i128);

/// Performs an integral remainder that returns `None` instead of panicking on division by zero and
/// instead of wrapping around on underflow and overflow.
pub trait CheckedRem: Sized + Rem<Self, Output = Self> {
    /// Finds the remainder of dividing two numbers, checking for underflow, overflow and division
    /// by zero. If any of that happens, `None` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use num_traits::CheckedRem;
    /// use std::i32::MIN;
    ///
    /// assert_eq!(CheckedRem::checked_rem(&10, &7), Some(3));
    /// assert_eq!(CheckedRem::checked_rem(&10, &-7), Some(3));
    /// assert_eq!(CheckedRem::checked_rem(&-10, &7), Some(-3));
    /// assert_eq!(CheckedRem::checked_rem(&-10, &-7), Some(-3));
    ///
    /// assert_eq!(CheckedRem::checked_rem(&10, &0), None);
    ///
    /// assert_eq!(CheckedRem::checked_rem(&MIN, &1), Some(0));
    /// assert_eq!(CheckedRem::checked_rem(&MIN, &-1), None);
    /// ```
    fn checked_rem(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedRem, checked_rem, u8);
checked_impl!(CheckedRem, checked_rem, u16);
checked_impl!(CheckedRem, checked_rem, u32);
checked_impl!(CheckedRem, checked_rem, u64);
checked_impl!(CheckedRem, checked_rem, usize);

checked_impl!(CheckedRem, checked_rem, i8);
checked_impl!(CheckedRem, checked_rem, i16);
checked_impl!(CheckedRem, checked_rem, i32);
checked_impl!(CheckedRem, checked_rem, i64);
checked_impl!(CheckedRem, checked_rem, isize);

macro_rules! checked_impl_unary {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self) -> Option<$t> {
                <$t>::$method(*self)
            }
        }
    }
}

/// Performs negation that returns `None` if the result can't be represented.
pub trait CheckedNeg: Sized {
    /// Negates a number, returning `None` for results that can't be represented, like signed `MIN`
    /// values that can't be positive, or non-zero unsigned values that can't be negative.
    ///
    /// # Examples
    ///
    /// ```
    /// use num_traits::CheckedNeg;
    /// use std::i32::MIN;
    ///
    /// assert_eq!(CheckedNeg::checked_neg(&1_i32), Some(-1));
    /// assert_eq!(CheckedNeg::checked_neg(&-1_i32), Some(1));
    /// assert_eq!(CheckedNeg::checked_neg(&MIN), None);
    ///
    /// assert_eq!(CheckedNeg::checked_neg(&0_u32), Some(0));
    /// assert_eq!(CheckedNeg::checked_neg(&1_u32), None);
    /// ```
    fn checked_neg(&self) -> Option<Self>;
}

checked_impl_unary!(CheckedNeg, checked_neg, u8);
checked_impl_unary!(CheckedNeg, checked_neg, u16);
checked_impl_unary!(CheckedNeg, checked_neg, u32);
checked_impl_unary!(CheckedNeg, checked_neg, u64);
checked_impl_unary!(CheckedNeg, checked_neg, usize);

checked_impl_unary!(CheckedNeg, checked_neg, i8);
checked_impl_unary!(CheckedNeg, checked_neg, i16);
checked_impl_unary!(CheckedNeg, checked_neg, i32);
checked_impl_unary!(CheckedNeg, checked_neg, i64);
checked_impl_unary!(CheckedNeg, checked_neg, isize);

/// Performs a left shift that returns `None` on overflow.
pub trait CheckedShl: Sized + Shl<u32, Output=Self> {
    /// Shifts a number to the left, checking for overflow. If overflow happens,
    /// `None` is returned.
    ///
    /// ```
    /// use num_traits::CheckedShl;
    ///
    /// let x: u16 = 0x0001;
    ///
    /// assert_eq!(CheckedShl::checked_shl(&x, 0),  Some(0x0001));
    /// assert_eq!(CheckedShl::checked_shl(&x, 1),  Some(0x0002));
    /// assert_eq!(CheckedShl::checked_shl(&x, 15), Some(0x8000));
    /// assert_eq!(CheckedShl::checked_shl(&x, 16), None);
    /// ```
    fn checked_shl(&self, rhs: u32) -> Option<Self>;
}

macro_rules! checked_shift_impl {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self, rhs: u32) -> Option<$t> {
                <$t>::$method(*self, rhs)
            }
        }
    }
}

checked_shift_impl!(CheckedShl, checked_shl, u8);
checked_shift_impl!(CheckedShl, checked_shl, u16);
checked_shift_impl!(CheckedShl, checked_shl, u32);
checked_shift_impl!(CheckedShl, checked_shl, u64);
checked_shift_impl!(CheckedShl, checked_shl, usize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShl, checked_shl, u128);

checked_shift_impl!(CheckedShl, checked_shl, i8);
checked_shift_impl!(CheckedShl, checked_shl, i16);
checked_shift_impl!(CheckedShl, checked_shl, i32);
checked_shift_impl!(CheckedShl, checked_shl, i64);
checked_shift_impl!(CheckedShl, checked_shl, isize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShl, checked_shl, i128);

/// Performs a right shift that returns `None` on overflow.
pub trait CheckedShr: Sized + Shr<u32, Output=Self> {
    /// Shifts a number to the left, checking for overflow. If overflow happens,
    /// `None` is returned.
    ///
    /// ```
    /// use num_traits::CheckedShr;
    ///
    /// let x: u16 = 0x8000;
    ///
    /// assert_eq!(CheckedShr::checked_shr(&x, 0),  Some(0x8000));
    /// assert_eq!(CheckedShr::checked_shr(&x, 1),  Some(0x4000));
    /// assert_eq!(CheckedShr::checked_shr(&x, 15), Some(0x0001));
    /// assert_eq!(CheckedShr::checked_shr(&x, 16), None);
    /// ```
    fn checked_shr(&self, rhs: u32) -> Option<Self>;
}

checked_shift_impl!(CheckedShr, checked_shr, u8);
checked_shift_impl!(CheckedShr, checked_shr, u16);
checked_shift_impl!(CheckedShr, checked_shr, u32);
checked_shift_impl!(CheckedShr, checked_shr, u64);
checked_shift_impl!(CheckedShr, checked_shr, usize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShr, checked_shr, u128);

checked_shift_impl!(CheckedShr, checked_shr, i8);
checked_shift_impl!(CheckedShr, checked_shr, i16);
checked_shift_impl!(CheckedShr, checked_shr, i32);
checked_shift_impl!(CheckedShr, checked_shr, i64);
checked_shift_impl!(CheckedShr, checked_shr, isize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShr, checked_shr, i128);
<commit_msg>impl 128-bit CheckedRem and CheckedNeg<commit_after>use core::ops::{Add, Sub, Mul, Div, Rem, Shl, Shr};

/// Performs addition that returns `None` instead of wrapping around on
/// overflow.
pub trait CheckedAdd: Sized + Add<Self, Output=Self> {
    /// Adds two numbers, checking for overflow. If overflow happens, `None` is
    /// returned.
    fn checked_add(&self, v: &Self) -> Option<Self>;
}

macro_rules! checked_impl {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self, v: &$t) -> Option<$t> {
                <$t>::$method(*self, *v)
            }
        }
    }
}

checked_impl!(CheckedAdd, checked_add, u8);
checked_impl!(CheckedAdd, checked_add, u16);
checked_impl!(CheckedAdd, checked_add, u32);
checked_impl!(CheckedAdd, checked_add, u64);
checked_impl!(CheckedAdd, checked_add, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedAdd, checked_add, u128);

checked_impl!(CheckedAdd, checked_add, i8);
checked_impl!(CheckedAdd, checked_add, i16);
checked_impl!(CheckedAdd, checked_add, i32);
checked_impl!(CheckedAdd, checked_add, i64);
checked_impl!(CheckedAdd, checked_add, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedAdd, checked_add, i128);

/// Performs subtraction that returns `None` instead of wrapping around on underflow.
pub trait CheckedSub: Sized + Sub<Self, Output=Self> {
    /// Subtracts two numbers, checking for underflow. If underflow happens,
    /// `None` is returned.
    fn checked_sub(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedSub, checked_sub, u8);
checked_impl!(CheckedSub, checked_sub, u16);
checked_impl!(CheckedSub, checked_sub, u32);
checked_impl!(CheckedSub, checked_sub, u64);
checked_impl!(CheckedSub, checked_sub, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedSub, checked_sub, u128);

checked_impl!(CheckedSub, checked_sub, i8);
checked_impl!(CheckedSub, checked_sub, i16);
checked_impl!(CheckedSub, checked_sub, i32);
checked_impl!(CheckedSub, checked_sub, i64);
checked_impl!(CheckedSub, checked_sub, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedSub, checked_sub, i128);

/// Performs multiplication that returns `None` instead of wrapping around on underflow or
/// overflow.
pub trait CheckedMul: Sized + Mul<Self, Output=Self> {
    /// Multiplies two numbers, checking for underflow or overflow. If underflow
    /// or overflow happens, `None` is returned.
    fn checked_mul(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedMul, checked_mul, u8);
checked_impl!(CheckedMul, checked_mul, u16);
checked_impl!(CheckedMul, checked_mul, u32);
checked_impl!(CheckedMul, checked_mul, u64);
checked_impl!(CheckedMul, checked_mul, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedMul, checked_mul, u128);

checked_impl!(CheckedMul, checked_mul, i8);
checked_impl!(CheckedMul, checked_mul, i16);
checked_impl!(CheckedMul, checked_mul, i32);
checked_impl!(CheckedMul, checked_mul, i64);
checked_impl!(CheckedMul, checked_mul, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedMul, checked_mul, i128);

/// Performs division that returns `None` instead of panicking on division by zero and instead of
/// wrapping around on underflow and overflow.
pub trait CheckedDiv: Sized + Div<Self, Output=Self> {
    /// Divides two numbers, checking for underflow, overflow and division by
    /// zero. If any of that happens, `None` is returned.
    fn checked_div(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedDiv, checked_div, u8);
checked_impl!(CheckedDiv, checked_div, u16);
checked_impl!(CheckedDiv, checked_div, u32);
checked_impl!(CheckedDiv, checked_div, u64);
checked_impl!(CheckedDiv, checked_div, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedDiv, checked_div, u128);

checked_impl!(CheckedDiv, checked_div, i8);
checked_impl!(CheckedDiv, checked_div, i16);
checked_impl!(CheckedDiv, checked_div, i32);
checked_impl!(CheckedDiv, checked_div, i64);
checked_impl!(CheckedDiv, checked_div, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedDiv, checked_div, i128);

/// Performs an integral remainder that returns `None` instead of panicking on division by zero and
/// instead of wrapping around on underflow and overflow.
pub trait CheckedRem: Sized + Rem<Self, Output = Self> {
    /// Finds the remainder of dividing two numbers, checking for underflow, overflow and division
    /// by zero. If any of that happens, `None` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use num_traits::CheckedRem;
    /// use std::i32::MIN;
    ///
    /// assert_eq!(CheckedRem::checked_rem(&10, &7), Some(3));
    /// assert_eq!(CheckedRem::checked_rem(&10, &-7), Some(3));
    /// assert_eq!(CheckedRem::checked_rem(&-10, &7), Some(-3));
    /// assert_eq!(CheckedRem::checked_rem(&-10, &-7), Some(-3));
    ///
    /// assert_eq!(CheckedRem::checked_rem(&10, &0), None);
    ///
    /// assert_eq!(CheckedRem::checked_rem(&MIN, &1), Some(0));
    /// assert_eq!(CheckedRem::checked_rem(&MIN, &-1), None);
    /// ```
    fn checked_rem(&self, v: &Self) -> Option<Self>;
}

checked_impl!(CheckedRem, checked_rem, u8);
checked_impl!(CheckedRem, checked_rem, u16);
checked_impl!(CheckedRem, checked_rem, u32);
checked_impl!(CheckedRem, checked_rem, u64);
checked_impl!(CheckedRem, checked_rem, usize);
#[cfg(feature = "i128")]
checked_impl!(CheckedRem, checked_rem, u128);

checked_impl!(CheckedRem, checked_rem, i8);
checked_impl!(CheckedRem, checked_rem, i16);
checked_impl!(CheckedRem, checked_rem, i32);
checked_impl!(CheckedRem, checked_rem, i64);
checked_impl!(CheckedRem, checked_rem, isize);
#[cfg(feature = "i128")]
checked_impl!(CheckedRem, checked_rem, i128);

macro_rules! checked_impl_unary {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self) -> Option<$t> {
                <$t>::$method(*self)
            }
        }
    }
}

/// Performs negation that returns `None` if the result can't be represented.
pub trait CheckedNeg: Sized {
    /// Negates a number, returning `None` for results that can't be represented, like signed `MIN`
    /// values that can't be positive, or non-zero unsigned values that can't be negative.
    ///
    /// # Examples
    ///
    /// ```
    /// use num_traits::CheckedNeg;
    /// use std::i32::MIN;
    ///
    /// assert_eq!(CheckedNeg::checked_neg(&1_i32), Some(-1));
    /// assert_eq!(CheckedNeg::checked_neg(&-1_i32), Some(1));
    /// assert_eq!(CheckedNeg::checked_neg(&MIN), None);
    ///
    /// assert_eq!(CheckedNeg::checked_neg(&0_u32), Some(0));
    /// assert_eq!(CheckedNeg::checked_neg(&1_u32), None);
    /// ```
    fn checked_neg(&self) -> Option<Self>;
}

checked_impl_unary!(CheckedNeg, checked_neg, u8);
checked_impl_unary!(CheckedNeg, checked_neg, u16);
checked_impl_unary!(CheckedNeg, checked_neg, u32);
checked_impl_unary!(CheckedNeg, checked_neg, u64);
checked_impl_unary!(CheckedNeg, checked_neg, usize);
#[cfg(feature = "i128")]
checked_impl_unary!(CheckedNeg, checked_neg, u128);

checked_impl_unary!(CheckedNeg, checked_neg, i8);
checked_impl_unary!(CheckedNeg, checked_neg, i16);
checked_impl_unary!(CheckedNeg, checked_neg, i32);
checked_impl_unary!(CheckedNeg, checked_neg, i64);
checked_impl_unary!(CheckedNeg, checked_neg, isize);
#[cfg(feature = "i128")]
checked_impl_unary!(CheckedNeg, checked_neg, i128);

/// Performs a left shift that returns `None` on overflow.
pub trait CheckedShl: Sized + Shl<u32, Output=Self> {
    /// Shifts a number to the left, checking for overflow. If overflow happens,
    /// `None` is returned.
    ///
    /// ```
    /// use num_traits::CheckedShl;
    ///
    /// let x: u16 = 0x0001;
    ///
    /// assert_eq!(CheckedShl::checked_shl(&x, 0),  Some(0x0001));
    /// assert_eq!(CheckedShl::checked_shl(&x, 1),  Some(0x0002));
    /// assert_eq!(CheckedShl::checked_shl(&x, 15), Some(0x8000));
    /// assert_eq!(CheckedShl::checked_shl(&x, 16), None);
    /// ```
    fn checked_shl(&self, rhs: u32) -> Option<Self>;
}

macro_rules! checked_shift_impl {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self, rhs: u32) -> Option<$t> {
                <$t>::$method(*self, rhs)
            }
        }
    }
}

checked_shift_impl!(CheckedShl, checked_shl, u8);
checked_shift_impl!(CheckedShl, checked_shl, u16);
checked_shift_impl!(CheckedShl, checked_shl, u32);
checked_shift_impl!(CheckedShl, checked_shl, u64);
checked_shift_impl!(CheckedShl, checked_shl, usize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShl, checked_shl, u128);

checked_shift_impl!(CheckedShl, checked_shl, i8);
checked_shift_impl!(CheckedShl, checked_shl, i16);
checked_shift_impl!(CheckedShl, checked_shl, i32);
checked_shift_impl!(CheckedShl, checked_shl, i64);
checked_shift_impl!(CheckedShl, checked_shl, isize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShl, checked_shl, i128);

/// Performs a right shift that returns `None` on overflow.
pub trait CheckedShr: Sized + Shr<u32, Output=Self> {
    /// Shifts a number to the left, checking for overflow. If overflow happens,
    /// `None` is returned.
    ///
    /// ```
    /// use num_traits::CheckedShr;
    ///
    /// let x: u16 = 0x8000;
    ///
    /// assert_eq!(CheckedShr::checked_shr(&x, 0),  Some(0x8000));
    /// assert_eq!(CheckedShr::checked_shr(&x, 1),  Some(0x4000));
    /// assert_eq!(CheckedShr::checked_shr(&x, 15), Some(0x0001));
    /// assert_eq!(CheckedShr::checked_shr(&x, 16), None);
    /// ```
    fn checked_shr(&self, rhs: u32) -> Option<Self>;
}

checked_shift_impl!(CheckedShr, checked_shr, u8);
checked_shift_impl!(CheckedShr, checked_shr, u16);
checked_shift_impl!(CheckedShr, checked_shr, u32);
checked_shift_impl!(CheckedShr, checked_shr, u64);
checked_shift_impl!(CheckedShr, checked_shr, usize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShr, checked_shr, u128);

checked_shift_impl!(CheckedShr, checked_shr, i8);
checked_shift_impl!(CheckedShr, checked_shr, i16);
checked_shift_impl!(CheckedShr, checked_shr, i32);
checked_shift_impl!(CheckedShr, checked_shr, i64);
checked_shift_impl!(CheckedShr, checked_shr, isize);
#[cfg(feature = "i128")]
checked_shift_impl!(CheckedShr, checked_shr, i128);
<|endoftext|>


<commit_before>use std::str::Chars;
use super::Span;
use super::token::{Token, TokenSpan, Lit};

pub struct Lexer<'a> {
    chars: Chars<'a>,
    last: Option<char>,
    last_pos: Option<usize>,
    curr: Option<char>,
    curr_pos: Option<usize>,
    next: Option<char>,
    span_pos: Option<usize>,
}

impl<'a> Lexer<'a> {
    pub fn from_query(q: &str) -> Lexer {
        let mut lex = Lexer {
            chars: q.chars(),
            last: None,
            last_pos: None,
            curr: None,
            curr_pos: None,
            next: None,
            span_pos: None,
        };

        lex.bump();
        lex.bump();
        lex
    }

    pub fn next_real(&mut self) -> Result<Option<TokenSpan>, LexError> {
        let tokenspn = try!(self.next());

        let wspace = match tokenspn {
            Some(ref token) => {
                match token.token {
                    Token::Whitespace => true,
                    _ => false,
                }
            }
            _ => false,
        };

        if wspace {
            self.next()
        } else {
            Ok(tokenspn)
        }
    }

    fn bump(&mut self) {
        self.last = self.curr;
        self.curr = self.next;
        self.next = self.chars.next();

        self.last_pos = self.curr_pos;

        match self.next {
            Some(c) => {
                if let Some(n) = self.curr_pos {
                    self.curr_pos = Some(n + c.len_utf8());
                } else {
                    self.curr_pos = Some(c.len_utf8());
                }
            }
            _ => {}
        }
    }

    fn scan_words(&mut self) -> String {
        let mut s = String::new();

        loop {
            match self.curr.unwrap_or(' ') {
                c @ 'a'...'z' |
                c @ 'A'...'Z' |
                c @ '0'...'9' |
                c @ '_' => {
                    s.push(c);
                }
                _ => break,
            }

            self.bump();
        }

        s
    }

    fn scan_nums(&mut self) -> String {
        let mut s = String::new();

        loop {
            match self.curr.unwrap_or(' ') {
                c @ '0'...'9' |
                c @ '.' => {
                    s.push(c);
                }
                _ => break,
            }

            self.bump();
        }

        s
    }

    fn scan_lits(&mut self) -> Result<String, LexError> {
        let mut s = String::new();

        self.bump(); // skip first char of the literal

        loop {
            match self.curr {
                Some('\'') |
                Some('"') => break,

                Some(c) => s.push(c),

                None => return Err(LexError::UnclosedQuote),
            }

            self.bump();
        }
        self.bump();

        Ok(s)
    }

    fn skip_whitespaces(&mut self) {
        loop {
            if Self::is_whitespace(self.curr.unwrap_or('x')) {
                self.bump();
            } else {
                break;
            }
        }
    }

    fn is_whitespace(c: char) -> bool {
        match c {
            ' ' | '\n' | '\t' => true,
            _ => false,
        }
    }

    fn next(&mut self) -> Result<Option<TokenSpan>, LexError> {
        let nextchar = self.next.unwrap_or('\x00').to_lowercase().next().unwrap();

        self.span_pos = self.curr_pos;

        let curr = match self.curr {
            Some(c) => c,
            None => return Ok(None),
        };

        let token = match curr {
            'a'...'z' | 'A'...'Z' => {
                let word = self.scan_words();
                Token::Word(word)
            }

            // TODO: check to see if '.' is not a valid Literal
            '0'...'9' => {
                let n = self.scan_nums();
                if let Ok(i) = n.parse::<i64>() {
                    Token::Literal(Lit::Int(i))
                } else {
                    if let Ok(f) = n.parse::<f64>() {
                        Token::Literal(Lit::Float(f))
                    } else {
                        Token::Unknown
                    }
                }
            }

            ';' => {
                self.bump();
                Token::Semi
            }
            '.' => {
                self.bump();
                Token::Dot
            }
            ',' => {
                self.bump();
                Token::Comma
            }

            '(' => {
                self.bump();
                Token::ParentOP
            }

            ')' => {
                self.bump();
                Token::ParentCL
            }

            // Literals
            '\'' | '"' => {
                let lit = try!(self.scan_lits());
                Token::Literal(Lit::String(lit))
            }

            '=' => {
                self.bump();
                Token::Equ
            }

            // Not Equal
            '!' if nextchar == '=' => {
                self.bump();
                self.bump();

                Token::NEqu
            }
            '<' if nextchar == '>' => {
                self.bump();
                self.bump();

                Token::NEqu
            }

            '>' => {
                self.bump();
                if nextchar == '=' {
                    self.bump();
                    Token::GEThan
                } else {
                    Token::GThan
                }
            }
            '<' => {
                self.bump();
                if nextchar == '=' {
                    self.bump();
                    Token::LEThan
                } else {
                    Token::LThan
                }
            }

            '+' => {
                self.bump();
                Token::Add
            }
            '-' => {
                self.bump();
                Token::Sub
            }
            '/' => {
                self.bump();
                Token::Div
            }
            '%' => {
                self.bump();
                Token::Mod
            }

            '*' => {
                self.bump();
                Token::Star
            }

            c if Self::is_whitespace(c) => {
                self.skip_whitespaces();
                Token::Whitespace
            }

            _ => {
                self.bump();
                Token::Unknown
            }

        };

        Ok(Some(TokenSpan {
            token: token,
            span: Span {
                // TODO: clean up the unwraps
                start: self.span_pos.unwrap(),
                end: self.curr_pos.unwrap(),
            },
        }))
    }
}

#[derive(Debug, PartialEq)]
pub enum LexError {
    UnclosedQuote,
}

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

    #[test]
    fn lexer_simple() {
        compare_lex("select", &[Token::Word("select".to_owned())]);
    }

    #[test]
    fn lexer_medium() {
        let cmds = &[Token::Word("select".to_owned()), Token::Word("from".to_owned())];

        compare_lex("select from", cmds);
    }

    #[test]
    fn lexer_hard() {
        let cmds = &[Token::Word("select".to_owned()),
                     Token::Star,
                     Token::Word("from".to_owned()),
                     Token::Semi];
        compare_lex("select * from;", cmds);
    }

    #[test]
    fn lexer_lit() {
        let cmds = &[Token::Word("insert".to_owned()), Token::Literal(Lit::Int(43)), Token::Semi];
        compare_lex("insert 43;", cmds);

        let cmds = &[Token::Word("insert".to_owned()),
                     Token::Literal(Lit::Float(43.32)),
                     Token::Semi];
        compare_lex("insert 43.32;", cmds);

        let cmds = &[Token::Word("insert".to_owned()),
                     Token::Literal(Lit::String("true".to_owned())),
                     Token::Semi];
        compare_lex("insert \"true\";", cmds);
    }

    #[test]
    fn lexer_parent() {
        let cmds = &[Token::Word("insert".to_owned()),
                     Token::ParentOP,
                     Token::Word("bool_string".to_owned()),
                     Token::ParentCL,
                     Token::Word("VALUES".to_owned()),
                     Token::ParentOP,
                     Token::Literal(Lit::String("true".to_owned())),
                     Token::ParentCL,
                     Token::Semi];
        compare_lex("insert (bool_string) VALUES (\"true\");", cmds);
    }

    fn compare_lex(q: &str, cmds: &[Token]) {
        let mut lex = Lexer::from_query(q);
        let mut index = 0;

        loop {
            let val = match lex.next_real().unwrap() {
                Some(val) => val,
                None => break,
            };
            println!("{:?}", val);

            // println!("{:?}, {:?}", val.token, cmds[index]);
            assert_eq!(val.token, cmds[index]);
            index += 1;
        }
    }
}
<commit_msg>added more to the test<commit_after>use std::str::Chars;
use super::Span;
use super::token::{Token, TokenSpan, Lit};

pub struct Lexer<'a> {
    chars: Chars<'a>,
    last: Option<char>,
    last_pos: Option<usize>,
    curr: Option<char>,
    curr_pos: Option<usize>,
    next: Option<char>,
    span_pos: Option<usize>,
}

impl<'a> Lexer<'a> {
    pub fn from_query(q: &str) -> Lexer {
        let mut lex = Lexer {
            chars: q.chars(),
            last: None,
            last_pos: None,
            curr: None,
            curr_pos: None,
            next: None,
            span_pos: None,
        };

        lex.bump();
        lex.bump();
        lex
    }

    pub fn next_real(&mut self) -> Result<Option<TokenSpan>, LexError> {
        let tokenspn = try!(self.next());

        let wspace = match tokenspn {
            Some(ref token) => {
                match token.token {
                    Token::Whitespace => true,
                    _ => false,
                }
            }
            _ => false,
        };

        if wspace {
            self.next()
        } else {
            Ok(tokenspn)
        }
    }

    fn bump(&mut self) {
        self.last = self.curr;
        self.curr = self.next;
        self.next = self.chars.next();

        self.last_pos = self.curr_pos;

        match self.next {
            Some(c) => {
                if let Some(n) = self.curr_pos {
                    self.curr_pos = Some(n + c.len_utf8());
                } else {
                    self.curr_pos = Some(c.len_utf8());
                }
            }
            _ => {}
        }
    }

    fn scan_words(&mut self) -> String {
        let mut s = String::new();

        loop {
            match self.curr.unwrap_or(' ') {
                c @ 'a'...'z' |
                c @ 'A'...'Z' |
                c @ '0'...'9' |
                c @ '_' => {
                    s.push(c);
                }
                _ => break,
            }

            self.bump();
        }

        s
    }

    fn scan_nums(&mut self) -> String {
        let mut s = String::new();

        loop {
            match self.curr.unwrap_or(' ') {
                c @ '0'...'9' |
                c @ '.' => {
                    s.push(c);
                }
                _ => break,
            }

            self.bump();
        }

        s
    }

    fn scan_lits(&mut self) -> Result<String, LexError> {
        let mut s = String::new();

        self.bump(); // skip first char of the literal

        loop {
            match self.curr {
                Some('\'') |
                Some('"') => break,

                Some(c) => s.push(c),

                None => return Err(LexError::UnclosedQuote),
            }

            self.bump();
        }
        self.bump();

        Ok(s)
    }

    fn skip_whitespaces(&mut self) {
        loop {
            if Self::is_whitespace(self.curr.unwrap_or('x')) {
                self.bump();
            } else {
                break;
            }
        }
    }

    fn is_whitespace(c: char) -> bool {
        match c {
            ' ' | '\n' | '\t' => true,
            _ => false,
        }
    }

    fn next(&mut self) -> Result<Option<TokenSpan>, LexError> {
        let nextchar = self.next.unwrap_or('\x00').to_lowercase().next().unwrap();

        self.span_pos = self.curr_pos;

        let curr = match self.curr {
            Some(c) => c,
            None => return Ok(None),
        };

        let token = match curr {
            'a'...'z' | 'A'...'Z' => {
                let word = self.scan_words();
                Token::Word(word)
            }

            // TODO: check to see if '.' is not a valid Literal
            '0'...'9' => {
                let n = self.scan_nums();
                if let Ok(i) = n.parse::<i64>() {
                    Token::Literal(Lit::Int(i))
                } else {
                    if let Ok(f) = n.parse::<f64>() {
                        Token::Literal(Lit::Float(f))
                    } else {
                        Token::Unknown
                    }
                }
            }

            ';' => {
                self.bump();
                Token::Semi
            }
            '.' => {
                self.bump();
                Token::Dot
            }
            ',' => {
                self.bump();
                Token::Comma
            }

            '(' => {
                self.bump();
                Token::ParentOP
            }

            ')' => {
                self.bump();
                Token::ParentCL
            }

            // Literals
            '\'' | '"' => {
                let lit = try!(self.scan_lits());
                Token::Literal(Lit::String(lit))
            }

            '=' => {
                self.bump();
                Token::Equ
            }

            // Not Equal
            '!' if nextchar == '=' => {
                self.bump();
                self.bump();

                Token::NEqu
            }
            '<' if nextchar == '>' => {
                self.bump();
                self.bump();

                Token::NEqu
            }

            '>' => {
                self.bump();
                if nextchar == '=' {
                    self.bump();
                    Token::GEThan
                } else {
                    Token::GThan
                }
            }
            '<' => {
                self.bump();
                if nextchar == '=' {
                    self.bump();
                    Token::LEThan
                } else {
                    Token::LThan
                }
            }

            '+' => {
                self.bump();
                Token::Add
            }
            '-' => {
                self.bump();
                Token::Sub
            }
            '/' => {
                self.bump();
                Token::Div
            }
            '%' => {
                self.bump();
                Token::Mod
            }

            '*' => {
                self.bump();
                Token::Star
            }

            c if Self::is_whitespace(c) => {
                self.skip_whitespaces();
                Token::Whitespace
            }

            _ => {
                self.bump();
                Token::Unknown
            }

        };

        Ok(Some(TokenSpan {
            token: token,
            span: Span {
                // TODO: clean up the unwraps
                start: self.span_pos.unwrap(),
                end: self.curr_pos.unwrap(),
            },
        }))
    }
}

#[derive(Debug, PartialEq)]
pub enum LexError {
    UnclosedQuote,
}

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

    #[test]
    fn lexer_simple() {
        compare_lex("select", &[Token::Word("select".to_owned())]);
    }

    #[test]
    fn lexer_medium() {
        let cmds = &[Token::Word("select".to_owned()), Token::Word("from".to_owned())];

        compare_lex("select from", cmds);
    }

    #[test]
    fn lexer_hard() {
        let cmds = &[Token::Word("select".to_owned()),
                     Token::Star,
                     Token::Word("from".to_owned()),
                     Token::Semi];
        compare_lex("select * from;", cmds);
    }

    #[test]
    fn lexer_lit() {
        let cmds = &[Token::Word("insert".to_owned()), Token::Literal(Lit::Int(43)), Token::Semi];
        compare_lex("insert 43;", cmds);

        let cmds = &[Token::Word("insert".to_owned()),
                     Token::Literal(Lit::Float(43.32)),
                     Token::Semi];
        compare_lex("insert 43.32;", cmds);

        let cmds = &[Token::Word("insert".to_owned()),
                     Token::Literal(Lit::String("true".to_owned())),
                     Token::Semi];
        compare_lex("insert \"true\";", cmds);
    }

    #[test]
    fn lexer_parent() {
        let cmds = &[Token::Word("insert".to_owned()),
                     Token::Word("into".to_owned()),
                     Token::ParentOP,
                     Token::Word("bool_string".to_owned()),
                     Token::ParentCL,
                     Token::Word("VALUES".to_owned()),
                     Token::ParentOP,
                     Token::Literal(Lit::String("true".to_owned())),
                     Token::ParentCL,
                     Token::Semi];
        compare_lex("insert into (bool_string) VALUES (\"true\");", cmds);
    }

    fn compare_lex(q: &str, cmds: &[Token]) {
        let mut lex = Lexer::from_query(q);
        let mut index = 0;

        loop {
            let val = match lex.next_real().unwrap() {
                Some(val) => val,
                None => break,
            };
            println!("{:?}", val);

            // println!("{:?}, {:?}", val.token, cmds[index]);
            assert_eq!(val.token, cmds[index]);
            index += 1;
        }
    }
}
<|endoftext|>


<commit_before>//! link handles parsing of GPX-spec links.

use errors::*;
use std::io::Read;
use xml::reader::XmlEvent;

use parser::string;
use parser::Context;

use Link;

/// consume consumes a GPX link from the `reader` until it ends.
/// When it returns, the reader will be at the element after the end GPX link
/// tag.
pub fn consume<R: Read>(context: &mut Context<R>) -> Result<Link> {
    let mut link: Link = Default::default();

    while let Some(event) = context.reader.next() {
        match event.chain_err(|| "error while parsing XML")? {
            XmlEvent::StartElement {
                name, attributes, ..
            } => {
                match name.local_name.as_ref() {
                    "text" => link.text = Some(string::consume(context)?),
                    "type" => link._type = Some(string::consume(context)?),
                    "link" => {
                        // retrieve mandatory href attribute
                        let attr = attributes
                            .into_iter()
                            .filter(|attr| attr.name.local_name == "href")
                            .nth(0);

                        let attr = attr.ok_or("no href attribute on link tag".to_owned())?;

                        link.href = attr.value;
                    }
                    child => Err(Error::from(ErrorKind::InvalidChildElement(
                        String::from(child),
                        "link",
                    )))?,
                }
            }

            XmlEvent::EndElement { .. } => {
                return Ok(link);
            }

            _ => {}
        }
    }

    return Err("no end tag for link".into());
}

#[cfg(test)]
mod tests {
    use std::io::BufReader;
    use xml::reader::EventReader;

    use parser::Context;

    use GpxVersion;
    use super::consume;

    #[test]
    fn consume_simple_link() {
        let link = consume!(
            "<link href='http://example.com'><text>hello</text><type>world</type></link>",
            GpxVersion::Gpx11
        );

        assert!(link.is_ok());

        let link = link.unwrap();

        assert_eq!(link.href, "http://example.com");

        assert!(link.text.is_some());
        assert_eq!(link.text.unwrap(), "hello");

        assert!(link._type.is_some());
        assert_eq!(link._type.unwrap(), "world");
    }

    #[test]
    fn consume_barebones() {
        let link = consume!(
            "<link href='http://topografix.com'></link>",
            GpxVersion::Gpx11
        );

        assert!(link.is_ok());

        let link = link.unwrap();

        assert_eq!(link.href, "http://topografix.com");

        assert!(link.text.is_none());
        assert!(link._type.is_none());
    }

    #[test]
    fn consume_no_href() {
        let link = consume!("<link></link>", GpxVersion::Gpx11);

        assert!(link.is_err());
    }
}
<commit_msg>Rewrite link::consume cleaner and more strict - Do not accept arbitrary amounts of opening link tags, must be exactly   one - Do not accept any closing tag as tag that closes the link,   require the name to be the same as of the opening tag - Use new custom errors InvalidClosingTag and MissingClosingTag - Use reader.peek() instead of reader.next() to not consume the opening   tag for deeper parser functions<commit_after>//! link handles parsing of GPX-spec links.

use errors::*;
use std::io::Read;
use xml::reader::XmlEvent;

use parser::string;
use parser::Context;
use parser::verify_starting_tag;

use Link;

/// consume consumes a GPX link from the `reader` until it ends.
/// When it returns, the reader will be at the element after the end GPX link
/// tag.
pub fn consume<R: Read>(context: &mut Context<R>) -> Result<Link> {
    let mut link: Link = Default::default();
    let attributes = verify_starting_tag(context, "link")?;
    let attr = attributes
        .into_iter()
        .filter(|attr| attr.name.local_name == "href")
        .nth(0);

    let attr = attr.ok_or(ErrorKind::InvalidElementLacksAttribute("href", "link"))?;

    link.href = attr.value;

    loop {
        let next_event = {
            if let Some(next) = context.reader.peek() {
                next.clone()
            } else {
                break;
            }
        };

        match next_event.chain_err(|| Error::from("error while parsing link event"))? {
            XmlEvent::StartElement { ref name, .. } => match name.local_name.as_ref() {
                "text" => link.text = Some(string::consume(context)?),
                "type" => link._type = Some(string::consume(context)?),
                child => {
                    bail!(ErrorKind::InvalidChildElement(String::from(child), "link"));
                }
            },
            XmlEvent::EndElement { ref name } => {
                ensure!(
                    name.local_name == "link",
                    ErrorKind::InvalidClosingTag(name.local_name.clone(), "link")
                );
                context.reader.next();
                return Ok(link);
            }
            _ => {
                context.reader.next(); //consume and ignore this event
            }
        }
    }

    bail!(ErrorKind::MissingClosingTag("link"));
}

#[cfg(test)]
mod tests {
    use std::io::BufReader;
    use xml::reader::EventReader;

    use parser::Context;

    use GpxVersion;
    use super::consume;

    #[test]
    fn consume_simple_link() {
        let link = consume!(
            "<link href='http://example.com'><text>hello</text><type>world</type></link>",
            GpxVersion::Gpx11
        );

        assert!(link.is_ok());

        let link = link.unwrap();

        assert_eq!(link.href, "http://example.com");

        assert!(link.text.is_some());
        assert_eq!(link.text.unwrap(), "hello");

        assert!(link._type.is_some());
        assert_eq!(link._type.unwrap(), "world");
    }

    #[test]
    fn consume_barebones() {
        let link = consume!(
            "<link href='http://topografix.com'></link>",
            GpxVersion::Gpx11
        );

        assert!(link.is_ok());

        let link = link.unwrap();

        assert_eq!(link.href, "http://topografix.com");

        assert!(link.text.is_none());
        assert!(link._type.is_none());
    }

    #[test]
    fn consume_no_href() {
        let link = consume!("<link></link>", GpxVersion::Gpx11);

        assert!(link.is_err());
    }
}
<|endoftext|>