<commit_before>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use futures::{
    future::{self, lazy, Future, IntoFuture},
    sync::oneshot,
    Stream,
};
use iml_manager_client::get_client;
use iml_manager_env;
use iml_warp_drive::{
    cache::{self, populate_from_api, populate_from_db, Cache, SharedCache},
    listen,
    locks::{self, create_locks_consumer, Locks},
    users, Message,
};
use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc};
use warp::Filter;

type SharedLocks = Arc<Mutex<Locks>>;

#[derive(Clone)]
struct SharedOneshotSender(Arc<Mutex<Option<oneshot::Sender<()>>>>);

impl SharedOneshotSender {
    fn new(x: oneshot::Sender<()>) -> Self {
        SharedOneshotSender(Arc::new(Mutex::new(Some(x))))
    }
    fn trigger(self) {
        self.0.lock().take().map(|x| x.send(()));
    }
}

fn main() {
    env_logger::builder().default_format_timestamp(false).init();

    // Keep track of all connected users, key is `usize`, value
    // is a event stream sender.
    let user_state: users::SharedUsers = Arc::new(Mutex::new(HashMap::new()));

    let lock_state: SharedLocks = Arc::new(Mutex::new(HashMap::new()));

    let api_cache_state: SharedCache = Arc::new(Mutex::new(Cache::default()));

    // Clone here to allow SSE route to get a ref.
    let user_state2 = Arc::clone(&user_state);
    let lock_state2 = Arc::clone(&lock_state);
    let api_cache_state2 = Arc::clone(&api_cache_state);

    // Handle an error in locks by shutting down
    let (mut exit, valve) = tokio_runtime_shutdown::shared_shutdown();
    let mut exit2 = exit.clone();
    let mut exit3 = exit.clone();
    let valve2 = valve.clone();

    let (tx, rx) = oneshot::channel();
    let tx = SharedOneshotSender::new(tx);
    let tx2 = tx.clone();
    let tx3 = tx.clone();

    let user_state3 = Arc::clone(&user_state);
    let rx = rx.inspect(move |_| users::disconnect_all_users(&user_state3));

    let user_state4 = Arc::clone(&user_state);

    let api_cache_state3 = Arc::clone(&api_cache_state);

    let api_client = get_client().unwrap();

    tokio::run(lazy(move || {
        warp::spawn(
            populate_from_api(Arc::clone(&api_cache_state))
                .map_err(|e| -> failure::Error { e.into() })
                .and_then(|_| iml_postgres::connect().from_err())
                .map(|(client, conn)| {
                    (
                        iml_postgres::shared_client(client),
                        iml_postgres::NotifyStream(conn),
                    )
                })
                .and_then(move |(client, stream)| {
                    let c2 = Arc::clone(&client);

                    let stream = valve.wrap(stream);

                    log::debug!("Cache state {:?}", api_cache_state);

                    warp::spawn(
                        stream
                        .from_err()
                            .for_each(move |msg| -> Box<Future<Item = (), Error = failure::Error> + Send> {
                                let _c3 = &c2;

                                let api_client = api_client.clone();
                                let api_cache_state = Arc::clone(&api_cache_state);
                                let user_state4 = Arc::clone(&user_state4);

                                match msg {
                                    iml_postgres::AsyncMessage::Notification(n) => {
                                        if n.channel() == "table_update" {
                                            let fut = listen::into_db_record(n.payload())
                                                .into_future()
                                                .from_err()
                                                .and_then(|r| {
                                                    cache::db_record_to_change_record(r, api_client)
                                                        .from_err()
                                                })
                                                .map(move |record_change| {
                                                    match record_change.clone() {
                                                        cache::RecordChange::Delete(r) => {
                                                            let removed = api_cache_state.lock().remove_record(&r);

                                                            if removed {
                                                                users::send_message(Message::RecordChange(record_change), Arc::clone(&user_state4));
                                                            }
                                                        }
                                                        cache::RecordChange::Update(r) => {
                                                            api_cache_state.lock().insert_record(r);

                                                            users::send_message(Message::RecordChange(record_change), Arc::clone(&user_state4));
                                                        }
                                                    };
                                                });

                                                Box::new(fut)
                                        } else {
                                            log::warn!("unknown channel: {}", n.channel());

                                            Box::new(future::ok(()))
                                        }
                                    }
                                    iml_postgres::AsyncMessage::Notice(err) => {
                                        log::error!("Error from postgres {}", err);
                                        Box::new(future::err(err).from_err())
                                    }
                                    _ => unreachable!()
                                }
                            })
                            .map_err(move |e| {
                                exit.trigger();
                                tx2.trigger();
                                log::error!("Unhandled error {}", e)
                            }),
                    );

                    let fut = {
                        let c = Arc::clone(&client);
                        let mut c = c.lock();

                        c.simple_query("LISTEN table_update")
                        .for_each(|_| Ok(()))
                        .from_err()
                    };

                    fut.and_then(move |_| {
                        populate_from_db(Arc::clone(&api_cache_state3), client)
                        .from_err()
                    })
                })
                .inspect(|_| log::info!("Started listening to NOTIFY events"))
                .map_err(move |e| {
                    exit2.trigger();
                    tx3.trigger();
                    log::error!("Unhandled error {}", e)
                }),
        );

        warp::spawn(
            iml_rabbit::connect_to_rabbit()
                .and_then(create_locks_consumer)
                .and_then(move |stream| {
                    log::info!("Started consuming locks");

                    let stream = valve2.wrap(stream);

                    stream
                        .for_each(move |message| {
                            log::debug!("got message {:?}", std::str::from_utf8(&message.data));

                            let lock_change: locks::Changes =
                                serde_json::from_slice(&message.data).unwrap();

                            log::debug!("decoded message: {:?}", lock_change);

                            match lock_change {
                                locks::Changes::Locks(l) => {
                                    let mut hm = lock_state.lock();
                                    hm.clear();
                                    hm.extend(l.result);

                                    users::send_message(
                                        Message::Locks(hm.clone()),
                                        Arc::clone(&user_state),
                                    );
                                }
                                locks::Changes::LockChange(l) => {
                                    locks::update_locks(&mut lock_state.lock(), l);

                                    let data = {
                                        let locks = lock_state.lock();
                                        locks.clone()
                                    };

                                    users::send_message(
                                        Message::Locks(data),
                                        Arc::clone(&user_state),
                                    );
                                }
                            };

                            Ok(())
                        })
                        .from_err()
                })
                .map_err(move |err| {
                    exit3.trigger();
                    tx.trigger();
                    log::error!("Unhandled error {}", err)
                }),
        );

        let users = warp::any().map(move || Arc::clone(&user_state2));

        let locks = warp::any().map(move || Arc::clone(&lock_state2));

        let api_cache = warp::any().map(move || Arc::clone(&api_cache_state2));

        // GET -> messages stream
        let routes = warp::get2()
            .and(warp::sse())
            .and(users)
            .and(locks)
            .and(api_cache)
            .map(
                |sse: warp::sse::Sse,
                 users: users::SharedUsers,
                 locks: SharedLocks,
                 api_cache: SharedCache| {
                    // reply using server-sent events
                    let stream = users::user_connected(
                        users,
                        locks.lock().clone(),
                        api_cache.lock().clone(),
                    );
                    sse.reply(warp::sse::keep_alive().stream(stream))
                },
            )
            .with(warp::log("iml-warp-drive::api"));

        log::info!("IML warp drive starting");

        let (_, fut) = warp::serve(routes)
            .bind_with_graceful_shutdown(iml_manager_env::get_warp_drive_addr(), rx);

        fut
    }));
}
<commit_msg>Fix warp-drive to start server after API request finishes. (#1154)<commit_after>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use futures::{
    future::{self, lazy, Future, IntoFuture},
    sync::oneshot,
    Stream,
};
use iml_manager_client::get_client;
use iml_manager_env;
use iml_warp_drive::{
    cache::{self, populate_from_api, populate_from_db, Cache, SharedCache},
    listen,
    locks::{self, create_locks_consumer, Locks},
    users, Message,
};
use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc};
use warp::Filter;

type SharedLocks = Arc<Mutex<Locks>>;

#[derive(Clone)]
struct SharedOneshotSender(Arc<Mutex<Option<oneshot::Sender<()>>>>);

impl SharedOneshotSender {
    fn new(x: oneshot::Sender<()>) -> Self {
        SharedOneshotSender(Arc::new(Mutex::new(Some(x))))
    }
    fn trigger(self) {
        self.0.lock().take().map(|x| x.send(()));
    }
}

fn main() {
    env_logger::builder().default_format_timestamp(false).init();

    // Keep track of all connected users, key is `usize`, value
    // is a event stream sender.
    let user_state: users::SharedUsers = Arc::new(Mutex::new(HashMap::new()));

    let lock_state: SharedLocks = Arc::new(Mutex::new(HashMap::new()));

    let api_cache_state: SharedCache = Arc::new(Mutex::new(Cache::default()));

    // Clone here to allow SSE route to get a ref.
    let user_state2 = Arc::clone(&user_state);
    let lock_state2 = Arc::clone(&lock_state);
    let api_cache_state2 = Arc::clone(&api_cache_state);

    // Handle an error in locks by shutting down
    let (mut exit, valve) = tokio_runtime_shutdown::shared_shutdown();
    let mut exit2 = exit.clone();
    let mut exit3 = exit.clone();
    let valve2 = valve.clone();

    let (tx, rx) = oneshot::channel();
    let tx = SharedOneshotSender::new(tx);
    let tx2 = tx.clone();
    let tx3 = tx.clone();

    let user_state3 = Arc::clone(&user_state);
    let rx = rx.inspect(move |_| users::disconnect_all_users(&user_state3));

    let user_state4 = Arc::clone(&user_state);

    let api_cache_state3 = Arc::clone(&api_cache_state);

    let api_client = get_client().unwrap();

    // GET -> messages stream
    let routes = warp::get2()
        .and(warp::sse())
        .and(warp::any().map(move || Arc::clone(&user_state2)))
        .and(warp::any().map(move || Arc::clone(&lock_state2)))
        .and(warp::any().map(move || Arc::clone(&api_cache_state2)))
        .map(
            |sse: warp::sse::Sse,
             users: users::SharedUsers,
             locks: SharedLocks,
             api_cache: SharedCache| {
                // reply using server-sent events
                let stream =
                    users::user_connected(users, locks.lock().clone(), api_cache.lock().clone());
                sse.reply(warp::sse::keep_alive().stream(stream))
            },
        )
        .with(warp::log("iml-warp-drive::api"));

    log::info!("IML warp drive starting");

    let (_, fut) =
        warp::serve(routes).bind_with_graceful_shutdown(iml_manager_env::get_warp_drive_addr(), rx);

    tokio::run(lazy(move || {
        warp::spawn(
            populate_from_api(Arc::clone(&api_cache_state))
                .map_err(|e| -> failure::Error { e.into() })
                .and_then(|_| iml_postgres::connect().from_err())
                .map(|(client, conn)| {
                    (
                        iml_postgres::shared_client(client),
                        iml_postgres::NotifyStream(conn),
                    )
                })
                .and_then(move |(client, stream)| {
                    let c2 = Arc::clone(&client);

                    let stream = valve.wrap(stream);

                    log::debug!("Cache state {:?}", api_cache_state);

                    warp::spawn(fut);

                    warp::spawn(
                        stream
                            .from_err()
                            .for_each(
                                move |msg| -> Box<
                                    dyn Future<Item = (), Error = failure::Error> + Send,
                                > {
                                    let _c3 = &c2;

                                    let api_client = api_client.clone();
                                    let api_cache_state = Arc::clone(&api_cache_state);
                                    let user_state4 = Arc::clone(&user_state4);

                                    match msg {
                                        iml_postgres::AsyncMessage::Notification(n) => {
                                            if n.channel() == "table_update" {
                                                let fut = listen::into_db_record(n.payload())
                                                    .into_future()
                                                    .from_err()
                                                    .and_then(|r| {
                                                        cache::db_record_to_change_record(
                                                            r, api_client,
                                                        )
                                                        .from_err()
                                                    })
                                                    .map(move |record_change| {
                                                        match record_change.clone() {
                                                            cache::RecordChange::Delete(r) => {
                                                                let removed = api_cache_state
                                                                    .lock()
                                                                    .remove_record(&r);

                                                                if removed {
                                                                    users::send_message(
                                                                        Message::RecordChange(
                                                                            record_change,
                                                                        ),
                                                                        Arc::clone(&user_state4),
                                                                    );
                                                                }
                                                            }
                                                            cache::RecordChange::Update(r) => {
                                                                api_cache_state
                                                                    .lock()
                                                                    .insert_record(r);

                                                                users::send_message(
                                                                    Message::RecordChange(
                                                                        record_change,
                                                                    ),
                                                                    Arc::clone(&user_state4),
                                                                );
                                                            }
                                                        };
                                                    });

                                                Box::new(fut)
                                            } else {
                                                log::warn!("unknown channel: {}", n.channel());

                                                Box::new(future::ok(()))
                                            }
                                        }
                                        iml_postgres::AsyncMessage::Notice(err) => {
                                            log::error!("Error from postgres {}", err);
                                            Box::new(future::err(err).from_err())
                                        }
                                        _ => unreachable!(),
                                    }
                                },
                            )
                            .map_err(move |e| {
                                exit.trigger();
                                tx2.trigger();
                                log::error!("Unhandled error {}", e)
                            }),
                    );

                    let fut = {
                        let c = Arc::clone(&client);
                        let mut c = c.lock();

                        c.simple_query("LISTEN table_update")
                            .for_each(|_| Ok(()))
                            .from_err()
                    };

                    fut.and_then(move |_| {
                        populate_from_db(Arc::clone(&api_cache_state3), client).from_err()
                    })
                })
                .inspect(|_| log::info!("Started listening to NOTIFY events"))
                .map_err(move |e| {
                    exit2.trigger();
                    tx3.trigger();
                    log::error!("Unhandled error {}", e)
                }),
        );

        iml_rabbit::connect_to_rabbit()
            .and_then(create_locks_consumer)
            .and_then(move |stream| {
                log::info!("Started consuming locks");

                let stream = valve2.wrap(stream);

                stream
                    .for_each(move |message| {
                        log::debug!("got message {:?}", std::str::from_utf8(&message.data));

                        let lock_change: locks::Changes =
                            serde_json::from_slice(&message.data).unwrap();

                        log::debug!("decoded message: {:?}", lock_change);

                        match lock_change {
                            locks::Changes::Locks(l) => {
                                let mut hm = lock_state.lock();
                                hm.clear();
                                hm.extend(l.result);

                                users::send_message(
                                    Message::Locks(hm.clone()),
                                    Arc::clone(&user_state),
                                );
                            }
                            locks::Changes::LockChange(l) => {
                                locks::update_locks(&mut lock_state.lock(), l);

                                let data = {
                                    let locks = lock_state.lock();
                                    locks.clone()
                                };

                                users::send_message(Message::Locks(data), Arc::clone(&user_state));
                            }
                        };

                        Ok(())
                    })
                    .from_err()
            })
            .map_err(move |err| {
                exit3.trigger();
                tx.trigger();
                log::error!("Unhandled error {}", err)
            })
    }));
}
<|endoftext|>


<commit_before><commit_msg>Want to start adding variables to the language. These will go in the execution environment.<commit_after><|endoftext|>


<commit_before><commit_msg>signal: add SignalKind Hash/Eq impls and c_int conversion (#4540)<commit_after><|endoftext|>


<commit_before>use bullet::BulletDrawable;
use cgmath;
use cgmath::{Angle, Deg, Matrix4, Point2, Point3, Vector3};
use character::CharacterDrawable;
use game::constants::{RESOLUTION_Y, TERRAIN_OBJECTS, TILE_WIDTH, TILES_PCS_H, TILES_PCS_W, VIEW_DISTANCE};
use gfx::{Factory, format::Rgba8, handle::ShaderResourceView, Resources, texture};
use gfx_app::mouse_controls::MouseInputState;
use graphics::orientation::Orientation;
use image;
use shaders::{Position, Projection};
use std::io::Cursor;
use terrain_object::TerrainObjectDrawable;
use zombie::ZombieDrawable;
use gfx_app::ColorFormat;

pub mod camera;
pub mod orientation;

#[derive(Debug, Default)]
pub struct DeltaTime(pub f64);

#[derive(Clone, Debug, Default)]
pub struct Dimensions {
  pub window_width: f32,
  pub window_height: f32,
  pub hidpi_factor: f32,
}

impl Dimensions {
  pub fn new(window_width: f32, window_height: f32, hidpi_factor: f32) -> Dimensions {
    Dimensions {
      window_width,
      window_height,
      hidpi_factor,
    }
  }

  pub fn get_view_matrix() -> Matrix4<f32> {
    Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    )
  }

  pub fn world_to_projection(&self, input: &camera::CameraInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, input.distance),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.window_width / self.window_height;
    Projection {
      model: view.into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(75.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2::new(point.x, RESOLUTION_Y as f32 - point.y)
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> u32 {
  let theta = cgmath::Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let angle = match theta {
    Deg(i) => i
  };
  let a = angle.floor() as i32;
  if a < 0 { (360 + a) as u32 } else { a as u32 }
}

pub fn direction_movement(direction: u32) -> Point2<f32> {
  let f_direction = direction as f32;
  Point2::new((Angle::cos(Deg(f_direction)) * 100.0).round() / 100.0,
              (Angle::sin(Deg(f_direction)) * 100.0).round() / 100.0)
}

pub fn get_orientation(angle_in_degrees: u32) -> Orientation {
  match angle_in_degrees {
    345 ... 360 | 0 ... 22 => Orientation::Right,
    23 ... 68 => Orientation::UpRight,
    69 ... 114 => Orientation::Up,
    115 ... 160 => Orientation::UpLeft,
    161 ... 206 => Orientation::Left,
    207 ... 252 => Orientation::DownLeft,
    253 ... 298 => Orientation::Down,
    299 ... 344 => Orientation::DownRight,
    _ => unreachable!("Invalid orientation")
  }
}

pub fn get_orientation_from_center(mouse_input: &MouseInputState, dim: &Dimensions) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2::new(dim.window_width / 2.0 * dim.hidpi_factor, dim.window_height / 2.0 * dim.hidpi_factor);
    let dir = direction(start_point, flip_y_axel(end_point_gl));
    get_orientation(dir)
  } else {
    Orientation::Right
  }
}

pub fn overlaps(area: Position, el: Position, width: f32, height: f32) -> bool {
  area.position[0] - width < el.position[0] &&
    area.position[0] + width > el.position[0] &&
    area.position[1] - height < el.position[1] &&
    area.position[1] + height > el.position[1]
}

fn is_within_map_borders(point: Point2<f32>) -> bool {
  point.x > 0.0 && point.x < 63.0 && point.y > 0.0 && point.y < 63.0
}

pub fn can_move(screen_pos: Position) -> bool {
  let tile_width = TILE_WIDTH;
  let x_coord = screen_pos.position[0];
  let y_coord = screen_pos.position[1];
  let point = Point2::new(
    (x_coord / tile_width + y_coord / tile_width).round() + 31.0,
    (y_coord / tile_width - x_coord / tile_width).round() + 32.0);
  is_within_map_borders(point)
}

pub fn is_not_terrain_object(pos: Point2<usize>) -> bool {
  !TERRAIN_OBJECTS.iter().any(|e| (e[0] == pos.x) && (e[1] == pos.y))
}

fn is_map_tile(pos: Point2<usize>) -> bool {
  pos.x >= 1usize && pos.y >= 1usize && pos.x < TILES_PCS_W && pos.y < TILES_PCS_H
}

pub fn can_move_to_tile(screen_pos: Position) -> bool {
  let pos = coords_to_tile(screen_pos);
  is_not_terrain_object(pos) && is_map_tile(pos)
}

pub fn coords_to_tile(position: Position) -> Point2<usize> {
  let tile_width = TILE_WIDTH;
  let pos = Point2 {
    x: -position.position[0],
    y: position.position[1] + 1500.0,
  };
  Point2 {
    x : (pos.x / tile_width + (pos.y / tile_width)) as usize,
    y: (pos.y / tile_width - (pos.x / tile_width)) as usize,
  }
}

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> ShaderResourceView<R, [f32; 4]> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  match factory.create_texture_immutable_u8::<Rgba8>(kind, texture::Mipmap::Provided, &[&img]) {
    Ok(val) => val.1,
    Err(e) => panic!("Couldn't load texture {:?}", e)
  }
}

pub fn load_raw_texture<R, F>(factory: &mut F, data: &[u8], size: Point2<i32>) -> ShaderResourceView<R, [f32; 4]>
                      where R: Resources, F: Factory<R> {
  let kind = texture::Kind::D2(size.x as texture::Size, size.y as texture::Size, texture::AaMode::Single);
  let mipmap = texture::Mipmap::Provided;
  match factory
    .create_texture_immutable_u8::<ColorFormat>(kind, mipmap, &[data]) {
    Ok(val) => val.1,
    Err(e) => panic!("Couldn't load texture {:?}", e)
  }
}

pub enum Drawables {
  Bullet(BulletDrawable),
  Character(CharacterDrawable),
  TerrainHouse(TerrainObjectDrawable),
  TerrainTree(TerrainObjectDrawable),
  Zombie(ZombieDrawable),
}

pub trait DrawOrder {
  fn get_zindex(&self) -> (f32, Drawables, &Self);
}
<commit_msg>Refactor imports in graphics module<commit_after>use bullet::BulletDrawable;
use cgmath;
use cgmath::{Angle, Deg, Matrix4, Point2, Point3, Vector3};
use character::CharacterDrawable;
use game::constants::{RESOLUTION_Y, TERRAIN_OBJECTS, TILE_WIDTH, TILES_PCS_H, TILES_PCS_W, VIEW_DISTANCE};
use gfx::{Factory, format::Rgba8, handle::ShaderResourceView, Resources, texture};
use gfx_app::{ColorFormat, mouse_controls::MouseInputState};
use graphics::orientation::Orientation;
use image;
use shaders::{Position, Projection};
use std::io::Cursor;
use terrain_object::TerrainObjectDrawable;
use zombie::ZombieDrawable;

pub mod camera;
pub mod orientation;

#[derive(Debug, Default)]
pub struct DeltaTime(pub f64);

#[derive(Clone, Debug, Default)]
pub struct Dimensions {
  pub window_width: f32,
  pub window_height: f32,
  pub hidpi_factor: f32,
}

impl Dimensions {
  pub fn new(window_width: f32, window_height: f32, hidpi_factor: f32) -> Dimensions {
    Dimensions {
      window_width,
      window_height,
      hidpi_factor,
    }
  }

  pub fn get_view_matrix() -> Matrix4<f32> {
    Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    )
  }

  pub fn world_to_projection(&self, input: &camera::CameraInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, input.distance),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.window_width / self.window_height;
    Projection {
      model: view.into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(75.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2::new(point.x, RESOLUTION_Y as f32 - point.y)
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> u32 {
  let theta = cgmath::Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let angle = match theta {
    Deg(i) => i
  };
  let a = angle.floor() as i32;
  if a < 0 { (360 + a) as u32 } else { a as u32 }
}

pub fn direction_movement(direction: u32) -> Point2<f32> {
  let f_direction = direction as f32;
  Point2::new((Angle::cos(Deg(f_direction)) * 100.0).round() / 100.0,
              (Angle::sin(Deg(f_direction)) * 100.0).round() / 100.0)
}

pub fn get_orientation(angle_in_degrees: u32) -> Orientation {
  match angle_in_degrees {
    345 ... 360 | 0 ... 22 => Orientation::Right,
    23 ... 68 => Orientation::UpRight,
    69 ... 114 => Orientation::Up,
    115 ... 160 => Orientation::UpLeft,
    161 ... 206 => Orientation::Left,
    207 ... 252 => Orientation::DownLeft,
    253 ... 298 => Orientation::Down,
    299 ... 344 => Orientation::DownRight,
    _ => unreachable!("Invalid orientation")
  }
}

pub fn get_orientation_from_center(mouse_input: &MouseInputState, dim: &Dimensions) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2::new(dim.window_width / 2.0 * dim.hidpi_factor, dim.window_height / 2.0 * dim.hidpi_factor);
    let dir = direction(start_point, flip_y_axel(end_point_gl));
    get_orientation(dir)
  } else {
    Orientation::Right
  }
}

pub fn overlaps(area: Position, el: Position, width: f32, height: f32) -> bool {
  area.position[0] - width < el.position[0] &&
    area.position[0] + width > el.position[0] &&
    area.position[1] - height < el.position[1] &&
    area.position[1] + height > el.position[1]
}

fn is_within_map_borders(point: Point2<f32>) -> bool {
  point.x > 0.0 && point.x < 63.0 && point.y > 0.0 && point.y < 63.0
}

pub fn can_move(screen_pos: Position) -> bool {
  let tile_width = TILE_WIDTH;
  let x_coord = screen_pos.position[0];
  let y_coord = screen_pos.position[1];
  let point = Point2::new(
    (x_coord / tile_width + y_coord / tile_width).round() + 31.0,
    (y_coord / tile_width - x_coord / tile_width).round() + 32.0);
  is_within_map_borders(point)
}

pub fn is_not_terrain_object(pos: Point2<usize>) -> bool {
  !TERRAIN_OBJECTS.iter().any(|e| (e[0] == pos.x) && (e[1] == pos.y))
}

fn is_map_tile(pos: Point2<usize>) -> bool {
  pos.x >= 1usize && pos.y >= 1usize && pos.x < TILES_PCS_W && pos.y < TILES_PCS_H
}

pub fn can_move_to_tile(screen_pos: Position) -> bool {
  let pos = coords_to_tile(screen_pos);
  is_not_terrain_object(pos) && is_map_tile(pos)
}

pub fn coords_to_tile(position: Position) -> Point2<usize> {
  let tile_width = TILE_WIDTH;
  let pos = Point2 {
    x: -position.position[0],
    y: position.position[1] + 1500.0,
  };
  Point2 {
    x : (pos.x / tile_width + (pos.y / tile_width)) as usize,
    y: (pos.y / tile_width - (pos.x / tile_width)) as usize,
  }
}

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> ShaderResourceView<R, [f32; 4]> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  match factory.create_texture_immutable_u8::<Rgba8>(kind, texture::Mipmap::Provided, &[&img]) {
    Ok(val) => val.1,
    Err(e) => panic!("Couldn't load texture {:?}", e)
  }
}

pub fn load_raw_texture<R, F>(factory: &mut F, data: &[u8], size: Point2<i32>) -> ShaderResourceView<R, [f32; 4]>
                      where R: Resources, F: Factory<R> {
  let kind = texture::Kind::D2(size.x as texture::Size, size.y as texture::Size, texture::AaMode::Single);
  let mipmap = texture::Mipmap::Provided;
  match factory
    .create_texture_immutable_u8::<ColorFormat>(kind, mipmap, &[data]) {
    Ok(val) => val.1,
    Err(e) => panic!("Couldn't load texture {:?}", e)
  }
}

pub enum Drawables {
  Bullet(BulletDrawable),
  Character(CharacterDrawable),
  TerrainHouse(TerrainObjectDrawable),
  TerrainTree(TerrainObjectDrawable),
  Zombie(ZombieDrawable),
}

pub trait DrawOrder {
  fn get_zindex(&self) -> (f32, Drawables, &Self);
}
<|endoftext|>


<commit_before>use std::fmt::{self, Display, Formatter};
use std::default::Default;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::property::Property;
use super::class::{self, Class};
use rustwlc::types::KeyMod;
use xcb::ffi::xproto::xcb_button_t;

#[derive(Clone, Debug)]
pub struct ButtonState {
    button: xcb_button_t,
    modifiers: KeyMod
}

#[derive(Clone, Debug)]
pub struct Button<'lua>(Table<'lua>);

impl <'lua> Button<'lua> {
    pub fn button(&self) -> rlua::Result<Value<'lua>> {
        let button = self.0.get::<_, ButtonState>("data")?;
        Ok(Value::Integer(button.button as _))
    }

    pub fn set_button(&self, new_val: xcb_button_t) -> rlua::Result<()> {
        let mut button = self.0.get::<_, ButtonState>("data")?;
        button.button = new_val;
        self.0.set("data", button)?;
        Ok(())
    }

    pub fn modifiers(&self) -> rlua::Result<KeyMod> {
        let button = self.0.get::<_, ButtonState>("data")?;
        Ok(button.modifiers)
    }

    pub fn set_modifiers(&self, mods: Table<'lua>) -> rlua::Result<()> {
        use ::lua::mods_to_rust;
        let mut button = self.0.get::<_, ButtonState>("data")?;
        button.modifiers = mods_to_rust(mods)?;
        self.0.set("data", button)?;
        Ok(())
    }
}

impl <'lua> ToLua<'lua> for Button<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl <'lua> Objectable<'lua, Button<'lua>, ButtonState> for Button<'lua> {
    fn _wrap(table: Table<'lua>) -> Button {
        Button(table)
    }

    fn get_table(self) -> Table<'lua> {
        self.0
    }
}

impl Display for ButtonState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Button: {:p}", self)
    }
}

impl Default for ButtonState {
    fn default() -> Self {
        ButtonState {
            button: xcb_button_t::default(),
            modifiers: KeyMod::empty()
        }
    }
}

impl UserData for ButtonState {}

/// Makes a new button stored in a table beside its signals
pub fn allocator(lua: &Lua) -> rlua::Result<Object> {
    let meta = lua.create_table();
    let class = class::button_class(lua)?;
    Ok(Button::new(lua, class)?
       .add_to_meta(meta)?
       .build())
}

pub fn new<'lua>(lua: &'lua Lua, _table: Table<'lua>)
                 -> rlua::Result<Object<'lua>> {
    allocator(lua)
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::new(lua, Some(allocator), None, None)?
        .method("__call".into(), lua.create_function(new))?
        .property(Property::new("button".into(),
                                Some(lua.create_function(set_button)),
                                Some(lua.create_function(get_button)),
                                Some(lua.create_function(set_button))))?
        .property(Property::new("modifiers".into(),
                                Some(lua.create_function(set_modifiers)),
                                Some(lua.create_function(get_modifiers)),
                                Some(lua.create_function(set_modifiers))))?
        .save_class("button")?
        .build()
}

// TODO Try to see if I can make this pass in an Object,
// or even better a Button

fn set_button<'lua>(_: &'lua Lua, (table, val): (Table, Value))
                    -> rlua::Result<Value<'lua>> {
    use rlua::Value::*;
    let button = Button::cast(table.into())?;
    match val {
        Number(num) => button.set_button(num as _)?,
        Integer(num) => button.set_button(num as _)?,
        _ => button.set_button(xcb_button_t::default())?
    }
    Ok(Value::Nil)
}

fn get_button<'lua>(_: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    Button::cast(table.into())?.button()
}

fn set_modifiers<'lua>(_: &'lua Lua, (table, modifiers): (Table, Table))
                       -> rlua::Result<Value<'lua>> {
    let button = Button::cast(table.into())?;
    button.set_modifiers(modifiers)?;
    Ok(Value::Nil)
}

fn get_modifiers<'lua>(lua: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    use ::lua::mods_to_lua;
    mods_to_lua(lua, Button::cast(table.into())?.modifiers()?).map(Value::Table)
}

#[cfg(test)]
mod test {
    use rlua::{self, Table, Lua};
    use super::super::button;
    use super::super::object;

    #[test]
    fn button_object_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("button0", button::allocator(&lua).unwrap());
        lua.globals().set("button1", button::allocator(&lua).unwrap());
        lua.eval(r#"
assert(button0.button == 0)
assert(button1.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button1.button == 0)
assert(button0.button == 3)
"#, None).unwrap()
    }

    #[test]
    fn button_class_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.connect_signal("test", function(button) button.button = 2 end)
a_button.emit_signal("test")
assert(a_button.button == 2)
"#, None).unwrap()
    }

    #[test]
    fn button_property_test() {
        let lua = Lua::new();
        let button_class = button::init(&lua).unwrap();
        assert_eq!(button_class.properties().unwrap().len().unwrap(), 2);
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.button = 5
assert(a_button.button == 5)
"#, None).unwrap()
    }

    #[test]
    fn button_remove_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
button0 = button()
assert(button0.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button0.button == 3)
button0.button = 0
button0.disconnect_signal("test")
button0.emit_signal("test")
assert(button0.button == 0)
"#, None).unwrap()
    }

    #[test]
    fn button_emit_signal_multiple_args() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap());
        lua.eval(r#"
 assert(a_button.button == 0)
 a_button.connect_signal("test", function(button, num) button.button = num end)
 a_button.emit_signal("test", 5)
 assert(a_button.button == 5)
 a_button.emit_signal("test", 0)
 assert(a_button.button == 0)
 "#, None).unwrap()
    }

    #[test]
    fn button_modifiers_test() {
        use rustwlc::*;
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap());
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        assert_eq!(button.modifiers().unwrap(), KeyMod::empty());
        lua.eval::<()>(r#"
a_button.modifiers = { "Caps" }
"#, None).unwrap();
        assert_eq!(button.modifiers().unwrap(), MOD_CAPS);
    }

    #[test]
    /// Tests that setting the button index property updates the
    /// callback for all instances of button
    fn button_index_property() {
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap());
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        lua.eval::<()>(r#"
hit = false
button.set_index_miss_handler(function(button)
    hit = true
    return 5
end)
assert(not hit)
a = button.button
assert(a ~= 5)
assert(not hit)
a = a_button.aoeu
assert(hit)
assert(a == 5)
a = nil
hit = false
a = button().aoeu
assert(hit)
assert(a == 5)
"#, None).unwrap()
    }
}

<commit_msg>Added test for multiple modifiers<commit_after>use std::fmt::{self, Display, Formatter};
use std::default::Default;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::property::Property;
use super::class::{self, Class};
use rustwlc::types::KeyMod;
use xcb::ffi::xproto::xcb_button_t;

#[derive(Clone, Debug)]
pub struct ButtonState {
    button: xcb_button_t,
    modifiers: KeyMod
}

#[derive(Clone, Debug)]
pub struct Button<'lua>(Table<'lua>);

impl <'lua> Button<'lua> {
    pub fn button(&self) -> rlua::Result<Value<'lua>> {
        let button = self.0.get::<_, ButtonState>("data")?;
        Ok(Value::Integer(button.button as _))
    }

    pub fn set_button(&self, new_val: xcb_button_t) -> rlua::Result<()> {
        let mut button = self.0.get::<_, ButtonState>("data")?;
        button.button = new_val;
        self.0.set("data", button)?;
        Ok(())
    }

    pub fn modifiers(&self) -> rlua::Result<KeyMod> {
        let button = self.0.get::<_, ButtonState>("data")?;
        Ok(button.modifiers)
    }

    pub fn set_modifiers(&self, mods: Table<'lua>) -> rlua::Result<()> {
        use ::lua::mods_to_rust;
        let mut button = self.0.get::<_, ButtonState>("data")?;
        button.modifiers = mods_to_rust(mods)?;
        self.0.set("data", button)?;
        Ok(())
    }
}

impl <'lua> ToLua<'lua> for Button<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl <'lua> Objectable<'lua, Button<'lua>, ButtonState> for Button<'lua> {
    fn _wrap(table: Table<'lua>) -> Button {
        Button(table)
    }

    fn get_table(self) -> Table<'lua> {
        self.0
    }
}

impl Display for ButtonState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Button: {:p}", self)
    }
}

impl Default for ButtonState {
    fn default() -> Self {
        ButtonState {
            button: xcb_button_t::default(),
            modifiers: KeyMod::empty()
        }
    }
}

impl UserData for ButtonState {}

/// Makes a new button stored in a table beside its signals
pub fn allocator(lua: &Lua) -> rlua::Result<Object> {
    let meta = lua.create_table();
    let class = class::button_class(lua)?;
    Ok(Button::new(lua, class)?
       .add_to_meta(meta)?
       .build())
}

pub fn new<'lua>(lua: &'lua Lua, _table: Table<'lua>)
                 -> rlua::Result<Object<'lua>> {
    allocator(lua)
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::new(lua, Some(allocator), None, None)?
        .method("__call".into(), lua.create_function(new))?
        .property(Property::new("button".into(),
                                Some(lua.create_function(set_button)),
                                Some(lua.create_function(get_button)),
                                Some(lua.create_function(set_button))))?
        .property(Property::new("modifiers".into(),
                                Some(lua.create_function(set_modifiers)),
                                Some(lua.create_function(get_modifiers)),
                                Some(lua.create_function(set_modifiers))))?
        .save_class("button")?
        .build()
}

// TODO Try to see if I can make this pass in an Object,
// or even better a Button

fn set_button<'lua>(_: &'lua Lua, (table, val): (Table, Value))
                    -> rlua::Result<Value<'lua>> {
    use rlua::Value::*;
    let button = Button::cast(table.into())?;
    match val {
        Number(num) => button.set_button(num as _)?,
        Integer(num) => button.set_button(num as _)?,
        _ => button.set_button(xcb_button_t::default())?
    }
    // TODO Emit property
    Ok(Value::Nil)
}

fn get_button<'lua>(_: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    Button::cast(table.into())?.button()
}

fn set_modifiers<'lua>(_: &'lua Lua, (table, modifiers): (Table, Table))
                       -> rlua::Result<Value<'lua>> {
    let button = Button::cast(table.into())?;
    button.set_modifiers(modifiers)?;
    // TODO Emit property
    Ok(Value::Nil)
}

fn get_modifiers<'lua>(lua: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    use ::lua::mods_to_lua;
    mods_to_lua(lua, Button::cast(table.into())?.modifiers()?).map(Value::Table)
}

#[cfg(test)]
mod test {
    use rlua::{self, Table, Lua};
    use super::super::button;
    use super::super::object;

    #[test]
    fn button_object_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("button0", button::allocator(&lua).unwrap());
        lua.globals().set("button1", button::allocator(&lua).unwrap());
        lua.eval(r#"
assert(button0.button == 0)
assert(button1.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button1.button == 0)
assert(button0.button == 3)
"#, None).unwrap()
    }

    #[test]
    fn button_class_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.connect_signal("test", function(button) button.button = 2 end)
a_button.emit_signal("test")
assert(a_button.button == 2)
"#, None).unwrap()
    }

    #[test]
    fn button_property_test() {
        let lua = Lua::new();
        let button_class = button::init(&lua).unwrap();
        assert_eq!(button_class.properties().unwrap().len().unwrap(), 2);
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.button = 5
assert(a_button.button == 5)
"#, None).unwrap()
    }

    #[test]
    fn button_remove_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
button0 = button()
assert(button0.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button0.button == 3)
button0.button = 0
button0.disconnect_signal("test")
button0.emit_signal("test")
assert(button0.button == 0)
"#, None).unwrap()
    }

    #[test]
    fn button_emit_signal_multiple_args() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap());
        lua.eval(r#"
 assert(a_button.button == 0)
 a_button.connect_signal("test", function(button, num) button.button = num end)
 a_button.emit_signal("test", 5)
 assert(a_button.button == 5)
 a_button.emit_signal("test", 0)
 assert(a_button.button == 0)
 "#, None).unwrap()
    }

    #[test]
    fn button_modifiers_test() {
        use rustwlc::*;
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap());
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        assert_eq!(button.modifiers().unwrap(), KeyMod::empty());
        lua.eval::<()>(r#"
a_button.modifiers = { "Caps" }
"#, None).unwrap();
        assert_eq!(button.modifiers().unwrap(), MOD_CAPS);
    }

    #[test]
    fn button_multiple_modifiers_test() {
        use rustwlc::*;
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap());
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        assert_eq!(button.modifiers().unwrap(), KeyMod::empty());
        lua.eval::<()>(r#"
a_button.modifiers = { "Caps", "Mod2" }
"#, None).unwrap();
        assert_eq!(button.modifiers().unwrap(), MOD_CAPS | MOD_MOD2);
    }

    #[test]
    /// Tests that setting the button index property updates the
    /// callback for all instances of button
    fn button_index_property() {
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap());
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        lua.eval::<()>(r#"
hit = false
button.set_index_miss_handler(function(button)
    hit = true
    return 5
end)
assert(not hit)
a = button.button
assert(a ~= 5)
assert(not hit)
a = a_button.aoeu
assert(hit)
assert(a == 5)
a = nil
hit = false
a = button().aoeu
assert(hit)
assert(a == 5)
"#, None).unwrap()
    }
}

<|endoftext|>


<commit_before>// Copyright © 2017 Cormac O'Brien.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

extern crate env_logger;
extern crate log;
extern crate nom;
extern crate richter;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::collections::HashSet;

use richter::bsp;
use richter::entity::Entity;
use richter::pak::Pak;
use richter::parse;
use richter::progs;

use nom::IResult;

fn main() {
    env_logger::init().unwrap();
    let mut pak = Pak::new();
    pak.add("pak0.pak").unwrap();

    let (functions, mut globals, mut entity_list) = progs::load(pak.open("progs.dat").unwrap())
        .unwrap();

    let (bsp, ent_string) = bsp::load(pak.open("maps/e1m1.bsp").unwrap()).unwrap();

    let maps = match parse::entity_maps(ent_string.as_bytes()) {
        IResult::Done(_, m) => m,
        _ => panic!("parse failed"),
    };

    for m in maps {
        entity_list.alloc_from_map(m).unwrap();
    }

    progs::validate(&functions, &mut globals, &mut entity_list);
}
<commit_msg>Update progs-test to new bsp API<commit_after>// Copyright © 2017 Cormac O'Brien.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

extern crate env_logger;
extern crate log;
extern crate nom;
extern crate richter;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::collections::HashSet;

use richter::bsp;
use richter::entity::Entity;
use richter::pak::Pak;
use richter::parse;
use richter::progs;

use nom::IResult;

fn main() {
    env_logger::init().unwrap();
    let mut pak = Pak::new();
    pak.add("pak0.pak").unwrap();

    let (functions, mut globals, mut entity_list) = progs::load(pak.open("progs.dat").unwrap())
        .unwrap();

    let (world_model, sub_models, ent_string) = bsp::load(pak.open("maps/e1m1.bsp").unwrap())
        .unwrap();

    let maps = match parse::entity_maps(ent_string.as_bytes()) {
        IResult::Done(_, m) => m,
        _ => panic!("parse failed"),
    };

    for m in maps {
        entity_list.alloc_from_map(m).unwrap();
    }

    progs::validate(&functions, &mut globals, &mut entity_list);
}
<|endoftext|>


<commit_before>use std::{convert};
use std::io::{Cursor, Read};

use byteorder::{self, ReadBytesExt};

#[derive(Debug, PartialEq, Eq)]
pub enum DeserializationError {
    UnexpectedTag,
    ShortData,
    ExtraData,
    IntegerOverflow,
    InvalidValue,
}

impl convert::From<byteorder::Error> for DeserializationError {
    fn from(e: byteorder::Error) -> DeserializationError {
        return match e {
            byteorder::Error::UnexpectedEOF => DeserializationError::ShortData,
            _ => panic!("Unexpected error!"),
        }
    }
}

pub type DeserializationResult<T> = Result<T, DeserializationError>;

pub struct Deserializer {
    reader: Cursor<Vec<u8>>,
}

impl Deserializer {
    pub fn new(data: Vec<u8>) -> Deserializer {
        return Deserializer{
            reader: Cursor::new(data),
        }
    }

    fn _read_length(&mut self) -> DeserializationResult<usize> {
        let b = try!(self.reader.read_u8());
        // TODO: handle lengths greater than 128
        assert!(b & 0x80 == 0);
        return Ok((b & 0x7f) as usize);
    }

    fn _read_with_tag<T, F>(&mut self, expected_tag: u8, body: F) -> DeserializationResult<T>
            where F: Fn(Vec<u8>) -> DeserializationResult<T> {
        let tag = try!(self.reader.read_u8());
        // TODO: only some of the bits in the first byte are for the tag
        if tag != expected_tag {
            return Err(DeserializationError::UnexpectedTag);
        }
        let length = try!(self._read_length());
        let mut data = vec![0; length];
        let n = self.reader.read(&mut data).unwrap();
        if n != length {
            return Err(DeserializationError::ShortData);
        }
        return body(data);
    }

    pub fn finish(self) -> DeserializationResult<()> {
        if self.reader.position() as usize != self.reader.get_ref().len() {
            return Err(DeserializationError::ExtraData);
        }
        return Ok(());
    }

    pub fn read_bool(&mut self) -> DeserializationResult<bool> {
        return self._read_with_tag(1, |data| {
            if data == b"\x00" {
                return Ok(false);
            } else if data == b"\xff" {
                return Ok(true)
            } else {
                return Err(DeserializationError::InvalidValue);
            }
        });
    }

    pub fn read_int(&mut self) -> DeserializationResult<i64> {
        return self._read_with_tag(2, |data| {
            if data.len() > 8 {
                return Err(DeserializationError::IntegerOverflow);
            }
            if data.len() > 1 {
                match (data[0], data[1] & 0x80) {
                    (0xff, 0x80) | (0x00, 0x00) => return Err(DeserializationError::InvalidValue),
                    _ => {},
                }
            }
            let mut ret = 0;
            for b in data.iter() {
                ret <<= 8;
                ret |= *b as i64;
            }
            // Shift up and down in order to sign extend the result.
            ret <<= 64 - data.len() * 8;
            ret >>= 64 - data.len() * 8;
            return Ok(ret);
        });
    }
}

pub fn from_vec<F, T>(data: Vec<u8>, f: F) -> DeserializationResult<T>
        where F: Fn(&mut Deserializer) -> DeserializationResult<T> {
    let mut deserializer = Deserializer::new(data);
    let result = try!(f(&mut deserializer));
    try!(deserializer.finish());
    return Ok(result);
}

#[cfg(test)]
mod tests {
    use std::{fmt};

    use super::{Deserializer, DeserializationError, DeserializationResult, from_vec};

    fn assert_deserializes<T, F>(values: Vec<(DeserializationResult<T>, Vec<u8>)>, f: F)
            where T: Eq + fmt::Debug, F: Fn(&mut Deserializer) -> DeserializationResult<T> {
        for (expected, value) in values {
            let result = from_vec(value, &f);
            assert_eq!(result, expected);
        }
    }
    #[test]
    fn test_read_extra_data() {
        assert_deserializes(vec![
            (Err(DeserializationError::ExtraData), b"\x00".to_vec()),
        ], |_| {
            return Ok(());
        });
    }

    #[test]
    fn test_read_bool() {
        assert_deserializes(vec![
            (Ok(true), b"\x01\x01\xff".to_vec()),
            (Ok(false), b"\x01\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x01\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\x00\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\xff\x01".to_vec()),
        ], |deserializer| {
            return deserializer.read_bool();
        });
    }

    #[test]
    fn test_read_int() {
        assert_deserializes(vec![
            (Ok(0), b"\x02\x01\x00".to_vec()),
            (Ok(127), b"\x02\x01\x7f".to_vec()),
            (Ok(128), b"\x02\x02\x00\x80".to_vec()),
            (Ok(256), b"\x02\x02\x01\x00".to_vec()),
            (Ok(-128), b"\x02\x01\x80".to_vec()),
            (Ok(-129), b"\x02\x02\xff\x7f".to_vec()),
            (Ok(-256), b"\x02\x02\xff\x00".to_vec()),
            (Err(DeserializationError::UnexpectedTag), b"\x03".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02\x02\x00".to_vec()),
            (Err(DeserializationError::ShortData), b"".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02".to_vec()),
            (
                Err(DeserializationError::IntegerOverflow),
                b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()
            ),
            (Err(DeserializationError::InvalidValue), b"\x02\x05\x00\x00\x00\x00\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x02\x02\xff\x80".to_vec()),
        ], |deserializer| {
            return deserializer.read_int();
        });
    }
}
<commit_msg>added a test for maxint<commit_after>use std::{convert};
use std::io::{Cursor, Read};

use byteorder::{self, ReadBytesExt};

#[derive(Debug, PartialEq, Eq)]
pub enum DeserializationError {
    UnexpectedTag,
    ShortData,
    ExtraData,
    IntegerOverflow,
    InvalidValue,
}

impl convert::From<byteorder::Error> for DeserializationError {
    fn from(e: byteorder::Error) -> DeserializationError {
        return match e {
            byteorder::Error::UnexpectedEOF => DeserializationError::ShortData,
            _ => panic!("Unexpected error!"),
        }
    }
}

pub type DeserializationResult<T> = Result<T, DeserializationError>;

pub struct Deserializer {
    reader: Cursor<Vec<u8>>,
}

impl Deserializer {
    pub fn new(data: Vec<u8>) -> Deserializer {
        return Deserializer{
            reader: Cursor::new(data),
        }
    }

    fn _read_length(&mut self) -> DeserializationResult<usize> {
        let b = try!(self.reader.read_u8());
        // TODO: handle lengths greater than 128
        assert!(b & 0x80 == 0);
        return Ok((b & 0x7f) as usize);
    }

    fn _read_with_tag<T, F>(&mut self, expected_tag: u8, body: F) -> DeserializationResult<T>
            where F: Fn(Vec<u8>) -> DeserializationResult<T> {
        let tag = try!(self.reader.read_u8());
        // TODO: only some of the bits in the first byte are for the tag
        if tag != expected_tag {
            return Err(DeserializationError::UnexpectedTag);
        }
        let length = try!(self._read_length());
        let mut data = vec![0; length];
        let n = self.reader.read(&mut data).unwrap();
        if n != length {
            return Err(DeserializationError::ShortData);
        }
        return body(data);
    }

    pub fn finish(self) -> DeserializationResult<()> {
        if self.reader.position() as usize != self.reader.get_ref().len() {
            return Err(DeserializationError::ExtraData);
        }
        return Ok(());
    }

    pub fn read_bool(&mut self) -> DeserializationResult<bool> {
        return self._read_with_tag(1, |data| {
            if data == b"\x00" {
                return Ok(false);
            } else if data == b"\xff" {
                return Ok(true)
            } else {
                return Err(DeserializationError::InvalidValue);
            }
        });
    }

    pub fn read_int(&mut self) -> DeserializationResult<i64> {
        return self._read_with_tag(2, |data| {
            if data.len() > 8 {
                return Err(DeserializationError::IntegerOverflow);
            }
            if data.len() > 1 {
                match (data[0], data[1] & 0x80) {
                    (0xff, 0x80) | (0x00, 0x00) => return Err(DeserializationError::InvalidValue),
                    _ => {},
                }
            }
            let mut ret = 0;
            for b in data.iter() {
                ret <<= 8;
                ret |= *b as i64;
            }
            // Shift up and down in order to sign extend the result.
            ret <<= 64 - data.len() * 8;
            ret >>= 64 - data.len() * 8;
            return Ok(ret);
        });
    }
}

pub fn from_vec<F, T>(data: Vec<u8>, f: F) -> DeserializationResult<T>
        where F: Fn(&mut Deserializer) -> DeserializationResult<T> {
    let mut deserializer = Deserializer::new(data);
    let result = try!(f(&mut deserializer));
    try!(deserializer.finish());
    return Ok(result);
}

#[cfg(test)]
mod tests {
    use std::{fmt};

    use super::{Deserializer, DeserializationError, DeserializationResult, from_vec};

    fn assert_deserializes<T, F>(values: Vec<(DeserializationResult<T>, Vec<u8>)>, f: F)
            where T: Eq + fmt::Debug, F: Fn(&mut Deserializer) -> DeserializationResult<T> {
        for (expected, value) in values {
            let result = from_vec(value, &f);
            assert_eq!(result, expected);
        }
    }
    #[test]
    fn test_read_extra_data() {
        assert_deserializes(vec![
            (Err(DeserializationError::ExtraData), b"\x00".to_vec()),
        ], |_| {
            return Ok(());
        });
    }

    #[test]
    fn test_read_bool() {
        assert_deserializes(vec![
            (Ok(true), b"\x01\x01\xff".to_vec()),
            (Ok(false), b"\x01\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x01\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\x00\x00".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x01\x02\xff\x01".to_vec()),
        ], |deserializer| {
            return deserializer.read_bool();
        });
    }

    #[test]
    fn test_read_int() {
        assert_deserializes(vec![
            (Ok(0), b"\x02\x01\x00".to_vec()),
            (Ok(127), b"\x02\x01\x7f".to_vec()),
            (Ok(128), b"\x02\x02\x00\x80".to_vec()),
            (Ok(256), b"\x02\x02\x01\x00".to_vec()),
            (Ok(-128), b"\x02\x01\x80".to_vec()),
            (Ok(-129), b"\x02\x02\xff\x7f".to_vec()),
            (Ok(-256), b"\x02\x02\xff\x00".to_vec()),
            (Ok(9223372036854775807), b"\x02\x08\x7f\xff\xff\xff\xff\xff\xff\xff".to_vec()),
            (Err(DeserializationError::UnexpectedTag), b"\x03".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02\x02\x00".to_vec()),
            (Err(DeserializationError::ShortData), b"".to_vec()),
            (Err(DeserializationError::ShortData), b"\x02".to_vec()),
            (
                Err(DeserializationError::IntegerOverflow),
                b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()
            ),
            (Err(DeserializationError::InvalidValue), b"\x02\x05\x00\x00\x00\x00\x01".to_vec()),
            (Err(DeserializationError::InvalidValue), b"\x02\x02\xff\x80".to_vec()),
        ], |deserializer| {
            return deserializer.read_int();
        });
    }
}
<|endoftext|>


<commit_before>use std::slice::Iter;
use std::ascii::AsciiExt;
use std::path::{Path, PathBuf};

use super::super::compiler::{Arg, CommandInfo, CompilationTask, Scope, InputKind, OutputKind};
use super::super::utils::filter;

pub fn create_task(command: CommandInfo, args: &[String]) -> Result<CompilationTask, String> {
	match parse_arguments(args) {
		Ok(parsed_args) => {
			// Source file name.
			let input_source = match filter(&parsed_args, |arg:&Arg|->Option<PathBuf> {
				match *arg {
					Arg::Input{ref kind, ref file, ..} if *kind == InputKind::Source => {Some(Path::new(file).to_path_buf())}
					_ => {None}
				}
			}).as_slice() {
				[] => {return Err(format!("Can't find source file path."));}
				[ref v] => v.clone(),
				v => {return Err(format!("Found too many source files: {:?}", v));}
			};
			// Precompiled header file name.
			let precompiled_file = match filter(&parsed_args, |arg:&Arg|->Option<PathBuf>{
				match *arg {
					Arg::Input{ref kind, ref file, ..} if *kind == InputKind::Precompiled => {Some(Path::new(file).to_path_buf())}
					_ => {None}
				}
			}).as_slice() {
				[] => None,
				[ref v] => Some(v.clone()),
				v => {return Err(format!("Found too many precompiled header files: {:?}", v));}
			};
			// Precompiled header file name.
			let marker_precompiled;
			let input_precompiled;
			let output_precompiled;
			match filter(&parsed_args, |arg:&Arg|->Option<(bool, String)>{
				match *arg {
					Arg::Input{ref kind, ref file, ..} if *kind == InputKind::Marker => Some((true, file.clone())),
					Arg::Output{ref kind, ref file, ..} if *kind == OutputKind::Marker => Some((false, file.clone())),
					_ => None
				}
			}).as_slice() {
				[] => {
					marker_precompiled=None;
					input_precompiled=None;
					output_precompiled=None;
				}
				[(ref input, ref path)] => {
					marker_precompiled=if path.len() > 0 {Some(path.clone())} else {None};
					let precompiled_path = match precompiled_file {
						Some(v) => v,
						None => Path::new(path).with_extension(".pch").to_path_buf()
					};
					if *input {
						output_precompiled=None;
						input_precompiled=Some(precompiled_path);
					} else {
						input_precompiled=None;
						output_precompiled=Some(precompiled_path);
					}
				}
				v => {
					return Err(format!("Found too many precompiled header markers: {:?}", v));
				}
			};
			// Output object file name.
			let output_object = match filter(&parsed_args, |arg:&Arg|->Option<PathBuf> {
				match *arg {
					Arg::Output{ref kind, ref file, ..} if *kind == OutputKind::Object => Some(Path::new(file).to_path_buf()),
					_ => None
				}
			}).as_slice() {
				[] => input_source.with_extension("obj"),
				[ref v] => v.clone(),
				v => {
					return Err(format!("Found too many output object files: {:?}", v));
				}
			};
			// Language
			let language: String;
			match filter(&parsed_args, |arg:&Arg|->Option<String>{
				match arg {
					&Arg::Param{ref flag, ref value, ..} if *flag == "T" => Some(value.clone()),
					_ => None
				}
			}).as_slice() {
				[]  => {
				match input_source.extension() {
					Some(extension) => {
						match extension.to_str() {
							Some(e) if e.eq_ignore_ascii_case("cpp") => {language = "P".to_string();}
							Some(e) if e.eq_ignore_ascii_case("c") => {language = "C".to_string();}
							_ => {return Err(format!("Can't detect file language by extension: {:?}", input_source));}
						}
					}
					_ => {return Err(format!("Can't detect file language by extension: {:?}", input_source));}
				}
			}
			[ref v] => {
				match v.as_slice() {
						"P" | "C" => {language = v.clone();}
						_ => { return Err(format!("Unknown source language type: {}", v));}
					}
				}
				v => {
					return Err(format!("Found too many output object files: {:?}", v));
				}
			};

			Ok(CompilationTask{
				command: command,
				args: parsed_args,
				language: language,
				input_source: input_source,
				input_precompiled: input_precompiled,
				output_object: output_object,
				output_precompiled: output_precompiled,
				marker_precompiled: marker_precompiled,
			})
		}
			Err(e) => {Err(e)}
		}
}

fn parse_arguments(args: &[String]) -> Result<Vec<Arg>, String> {
	let mut result: Vec<Arg> = Vec::new();
	let mut errors: Vec<String> = Vec::new();
	let mut iter = args.iter();
	loop {
		match parse_argument(&mut iter) {
			Some(parse_result) => {
				match parse_result {
					Ok(arg) => {result.push(arg);}
					Err(e) => {errors.push(e);}
				}
			}
			None => {
				break;
			}
		}
	}
	if errors.len() > 0 {
		return Err(format!("Found unknown command line arguments: {:?}", errors))
	}
	Ok(result)
}

fn parse_argument(iter: &mut  Iter<String>) -> Option<Result<Arg, String>> {
	match iter.next() {
		Some(arg) => Some(
			if has_param_prefix(arg) {
				let flag = arg[1..].as_slice();
				match is_spaceable_param(flag) {
					Some((prefix, scope)) => {
						if flag == prefix {
							match iter.next() {
								Some(value) if !has_param_prefix(value) => Ok(Arg::Param{scope: scope, flag:prefix.to_string(), value:value.to_string()}),
								_ => Err(arg.to_string())
							}
						} else {
							Ok(Arg::Param{scope: scope, flag:prefix.to_string(), value:flag[prefix.len()..].to_string()})
						}
					}
					None => {
						match flag {
							"c" => Ok(Arg::Flag{scope: Scope::Ignore, flag:flag.to_string()}),
							"bigobj" | "nologo" => Ok(Arg::Flag{scope: Scope::Compiler, flag:flag.to_string()}),
							s if s.starts_with("T") => Ok(Arg::Flag{scope: Scope::Ignore, flag:flag.to_string()}),
							s if s.starts_with("O") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("G") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("RTC") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("Z") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("d2Zi+") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("MD") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("MT") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("EH") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("fp:") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("errorReport:") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("Fo") => Ok(Arg::Output{kind:OutputKind::Object, flag:"Fo".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("Fp") => Ok(Arg::Input{kind:InputKind::Precompiled, flag:"Fp".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("Yc") => Ok(Arg::Output{kind:OutputKind::Marker, flag:"Yc".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("Yu") => Ok(Arg::Input{kind:InputKind::Marker, flag:"Yu".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("FI") => Ok(Arg::Param{scope: Scope::Preprocessor, flag:"FI".to_string(), value:s[2..].to_string()}),
							_ => Err(arg.to_string())
						}
					}
				}
			} else {
				Ok(Arg::Input{kind:InputKind::Source, flag:String::new(), file:arg.to_string()})
		}),
		None => None
	}
}

fn is_spaceable_param(flag: &str) -> Option<(&str, Scope)> {
	for prefix in ["I", "D"].iter() {
		if flag.starts_with(*prefix) {
			return Some((*prefix, Scope::Preprocessor));
		}
	}
	for prefix in ["W", "wd", "we", "wo", "w"].iter() {
		if flag.starts_with(*prefix) {
			return Some((*prefix, Scope::Compiler));
		}
	}
	None
}

fn has_param_prefix(arg: &String) -> bool {
	arg.starts_with("/") || arg.starts_with("-")
}

#[test]
fn test_parse_argument() {
	use super::super::wincmd;

	assert_eq!(
		parse_arguments(&wincmd::parse("/c /Yusample.h /Fpsample.h.pch /Fosample.cpp.o /DTEST /D TEST2 sample.cpp").as_slice()).unwrap(),
		[
			Arg::Flag { scope: Scope::Ignore, flag: "c".to_string()},
			Arg::Input { kind: InputKind::Marker, flag: "Yu".to_string(), file: "sample.h".to_string()},
			Arg::Input { kind: InputKind::Precompiled, flag: "Fp".to_string(), file: "sample.h.pch".to_string()},
			Arg::Output { kind: OutputKind::Object, flag: "Fo".to_string(), file: "sample.cpp.o".to_string()},
			Arg::Param { scope: Scope::Preprocessor, flag: "D".to_string(), value: "TEST".to_string()},
			Arg::Param { scope: Scope::Preprocessor, flag: "D".to_string(), value: "TEST2".to_string()},
			Arg::Input { kind: InputKind::Source, flag: "".to_string(), file: "sample.cpp".to_string()}
		]
	)
}
<commit_msg>Fix /T compiler flag processing<commit_after>use std::slice::Iter;
use std::ascii::AsciiExt;
use std::path::{Path, PathBuf};

use super::super::compiler::{Arg, CommandInfo, CompilationTask, Scope, InputKind, OutputKind};
use super::super::utils::filter;

pub fn create_task(command: CommandInfo, args: &[String]) -> Result<CompilationTask, String> {
	match parse_arguments(args) {
		Ok(parsed_args) => {
			// Source file name.
			let input_source = match filter(&parsed_args, |arg:&Arg|->Option<PathBuf> {
				match *arg {
					Arg::Input{ref kind, ref file, ..} if *kind == InputKind::Source => {Some(Path::new(file).to_path_buf())}
					_ => {None}
				}
			}).as_slice() {
				[] => {return Err(format!("Can't find source file path."));}
				[ref v] => v.clone(),
				v => {return Err(format!("Found too many source files: {:?}", v));}
			};
			// Precompiled header file name.
			let precompiled_file = match filter(&parsed_args, |arg:&Arg|->Option<PathBuf>{
				match *arg {
					Arg::Input{ref kind, ref file, ..} if *kind == InputKind::Precompiled => {Some(Path::new(file).to_path_buf())}
					_ => {None}
				}
			}).as_slice() {
				[] => None,
				[ref v] => Some(v.clone()),
				v => {return Err(format!("Found too many precompiled header files: {:?}", v));}
			};
			// Precompiled header file name.
			let marker_precompiled;
			let input_precompiled;
			let output_precompiled;
			match filter(&parsed_args, |arg:&Arg|->Option<(bool, String)>{
				match *arg {
					Arg::Input{ref kind, ref file, ..} if *kind == InputKind::Marker => Some((true, file.clone())),
					Arg::Output{ref kind, ref file, ..} if *kind == OutputKind::Marker => Some((false, file.clone())),
					_ => None
				}
			}).as_slice() {
				[] => {
					marker_precompiled=None;
					input_precompiled=None;
					output_precompiled=None;
				}
				[(ref input, ref path)] => {
					marker_precompiled=if path.len() > 0 {Some(path.clone())} else {None};
					let precompiled_path = match precompiled_file {
						Some(v) => v,
						None => Path::new(path).with_extension(".pch").to_path_buf()
					};
					if *input {
						output_precompiled=None;
						input_precompiled=Some(precompiled_path);
					} else {
						input_precompiled=None;
						output_precompiled=Some(precompiled_path);
					}
				}
				v => {
					return Err(format!("Found too many precompiled header markers: {:?}", v));
				}
			};
			// Output object file name.
			let output_object = match filter(&parsed_args, |arg:&Arg|->Option<PathBuf> {
				match *arg {
					Arg::Output{ref kind, ref file, ..} if *kind == OutputKind::Object => Some(Path::new(file).to_path_buf()),
					_ => None
				}
			}).as_slice() {
				[] => input_source.with_extension("obj"),
				[ref v] => v.clone(),
				v => {
					return Err(format!("Found too many output object files: {:?}", v));
				}
			};
			// Language
			let language: String;
			match filter(&parsed_args, |arg:&Arg|->Option<String>{
				match arg {
					&Arg::Param{ref flag, ref value, ..} if *flag == "T" => Some(value.clone()),
					_ => None
				}
			}).as_slice() {
				[]  => {
				match input_source.extension() {
					Some(extension) => {
						match extension.to_str() {
							Some(e) if e.eq_ignore_ascii_case("cpp") => {language = "P".to_string();}
							Some(e) if e.eq_ignore_ascii_case("c") => {language = "C".to_string();}
							_ => {return Err(format!("Can't detect file language by extension: {:?}", input_source));}
						}
					}
					_ => {return Err(format!("Can't detect file language by extension: {:?}", input_source));}
				}
			}
			[ref v] => {
				match v.as_slice() {
						"P" | "C" => {language = v.clone();}
						_ => { return Err(format!("Unknown source language type: {}", v));}
					}
				}
				v => {
					return Err(format!("Found too many output object files: {:?}", v));
				}
			};

			Ok(CompilationTask{
				command: command,
				args: parsed_args,
				language: language,
				input_source: input_source,
				input_precompiled: input_precompiled,
				output_object: output_object,
				output_precompiled: output_precompiled,
				marker_precompiled: marker_precompiled,
			})
		}
			Err(e) => {Err(e)}
		}
}

fn parse_arguments(args: &[String]) -> Result<Vec<Arg>, String> {
	let mut result: Vec<Arg> = Vec::new();
	let mut errors: Vec<String> = Vec::new();
	let mut iter = args.iter();
	loop {
		match parse_argument(&mut iter) {
			Some(parse_result) => {
				match parse_result {
					Ok(arg) => {result.push(arg);}
					Err(e) => {errors.push(e);}
				}
			}
			None => {
				break;
			}
		}
	}
	if errors.len() > 0 {
		return Err(format!("Found unknown command line arguments: {:?}", errors))
	}
	Ok(result)
}

fn parse_argument(iter: &mut  Iter<String>) -> Option<Result<Arg, String>> {
	match iter.next() {
		Some(arg) => Some(
			if has_param_prefix(arg) {
				let flag = arg[1..].as_slice();
				match is_spaceable_param(flag) {
					Some((prefix, scope)) => {
						if flag == prefix {
							match iter.next() {
								Some(value) if !has_param_prefix(value) => Ok(Arg::Param{scope: scope, flag:prefix.to_string(), value:value.to_string()}),
								_ => Err(arg.to_string())
							}
						} else {
							Ok(Arg::Param{scope: scope, flag:prefix.to_string(), value:flag[prefix.len()..].to_string()})
						}
					}
					None => {
						match flag {
							"c" => Ok(Arg::Flag{scope: Scope::Ignore, flag:flag.to_string()}),
							"bigobj" | "nologo" => Ok(Arg::Flag{scope: Scope::Compiler, flag:flag.to_string()}),
							s if s.starts_with("T") => Ok(Arg::Param{scope: Scope::Ignore, flag:"T".to_string(), value: s[1..].to_string()}),
							s if s.starts_with("O") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("G") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("RTC") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("Z") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("d2Zi+") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("MD") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("MT") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("EH") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("fp:") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("errorReport:") => Ok(Arg::Flag{scope: Scope::Shared, flag:flag.to_string()}),
							s if s.starts_with("Fo") => Ok(Arg::Output{kind:OutputKind::Object, flag:"Fo".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("Fp") => Ok(Arg::Input{kind:InputKind::Precompiled, flag:"Fp".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("Yc") => Ok(Arg::Output{kind:OutputKind::Marker, flag:"Yc".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("Yu") => Ok(Arg::Input{kind:InputKind::Marker, flag:"Yu".to_string(), file:s[2..].to_string()}),
							s if s.starts_with("FI") => Ok(Arg::Param{scope: Scope::Preprocessor, flag:"FI".to_string(), value:s[2..].to_string()}),
							_ => Err(arg.to_string())
						}
					}
				}
			} else {
				Ok(Arg::Input{kind:InputKind::Source, flag:String::new(), file:arg.to_string()})
		}),
		None => None
	}
}

fn is_spaceable_param(flag: &str) -> Option<(&str, Scope)> {
	for prefix in ["I", "D"].iter() {
		if flag.starts_with(*prefix) {
			return Some((*prefix, Scope::Preprocessor));
		}
	}
	for prefix in ["W", "wd", "we", "wo", "w"].iter() {
		if flag.starts_with(*prefix) {
			return Some((*prefix, Scope::Compiler));
		}
	}
	None
}

fn has_param_prefix(arg: &String) -> bool {
	arg.starts_with("/") || arg.starts_with("-")
}

#[test]
fn test_parse_argument() {
	use super::super::wincmd;

	assert_eq!(
		parse_arguments(&wincmd::parse("/TP /c /Yusample.h /Fpsample.h.pch /Fosample.cpp.o /DTEST /D TEST2 sample.cpp").as_slice()).unwrap(),
		[
			Arg::Param { scope: Scope::Ignore, flag: "T".to_string(), value: "P".to_string()},
			Arg::Flag { scope: Scope::Ignore, flag: "c".to_string()},
			Arg::Input { kind: InputKind::Marker, flag: "Yu".to_string(), file: "sample.h".to_string()},
			Arg::Input { kind: InputKind::Precompiled, flag: "Fp".to_string(), file: "sample.h.pch".to_string()},
			Arg::Output { kind: OutputKind::Object, flag: "Fo".to_string(), file: "sample.cpp.o".to_string()},
			Arg::Param { scope: Scope::Preprocessor, flag: "D".to_string(), value: "TEST".to_string()},
			Arg::Param { scope: Scope::Preprocessor, flag: "D".to_string(), value: "TEST2".to_string()},
			Arg::Input { kind: InputKind::Source, flag: "".to_string(), file: "sample.cpp".to_string()}
		]
	)
}
<|endoftext|>