<commit_before>use std::collections::HashMap;

use math::*;

use scene::Scene;
use ecs::System;
use super::bounding_volume::*;
use debug_draw;

/// A collision processor that partitions the space into a regular grid.
///
/// # TODO
///
/// - Do something to configure the size of the grid.
#[derive(Debug, Clone)]
pub struct GridCollisionSystem {
    grid: HashMap<GridCell, Vec<BoundingVolumeHierarchy>>,
    cell_size: f32,
}

impl GridCollisionSystem {
    pub fn new() -> GridCollisionSystem {
        GridCollisionSystem {
            grid: HashMap::new(),
            cell_size: 1.0,
        }
    }

    /// Converts a point in world space to its grid cell.
    fn world_to_grid(&self, point: Point) -> GridCell {
        GridCell {
            x: (point.x / self.cell_size).floor() as isize,
            y: (point.y / self.cell_size).floor() as isize,
            z: (point.z / self.cell_size).floor() as isize,
        }
    }
}

impl System for GridCollisionSystem {
    fn update(&mut self, scene: &Scene, _delta: f32) {
        // Clear out grid contents from previous frame, start each frame with an empty grid an
        // rebuilt it rather than trying to update the grid as objects move.
        for (_, mut cell) in &mut self.grid {
            cell.clear();
        }

        let bvh_manager = scene.get_manager::<BoundingVolumeManager>();

        for (bvh, _entity) in bvh_manager.iter() {
            let (min, max) = match bvh.root {
                BoundingVolumeNode::Node { ref volume, left_child: _, right_child: _ } => {
                    match volume {
                        &BoundingVolume::AABB { min, max } => {
                            (min, max)
                        },
                        _ => panic!("Bounding volume hierarchy for entity {:?} does not have an AABB at its root, grid collision is only suported with hierarchies that have an AABB at the root"),
                    }
                },
                BoundingVolumeNode::Leaf(_) => panic!("The root of the bounding volume was a leaf node, which is bad and not okay (and probably shouldn't even be possible :sideeye:)"),
            };

            let grid_cell = self.world_to_grid(min);
            let max_cell = self.world_to_grid(max);

            // Collide against any existing volumes in the
            for test_cell in grid_cell.iter_to(max_cell) {
                if let Some(cell) = self.grid.get(&test_cell) {
                    for other_bvh in cell {
                        if bvh.test(other_bvh) {
                            // Woo, we have a collison.
                            println!("legit collision between {:?} and {:?}", bvh.entity, other_bvh.entity);
                        }
                    }
                }
            }

            if let Some(mut cell) = self.grid.get_mut(&grid_cell) {
                // Collide against stuffs.

                cell.push(bvh.clone());
                continue;
            }

            // This block should be an else branch on the above if block, but the borrow checker
            // isn't smart enough to tell that the borrow on grid has ended. We do a continue at
            // the end of the if block so we can assume if we get here that the cell is not in the
            // grid.
            {
                self.grid.insert(grid_cell, vec![bvh.clone()]);
            }
        }

        // Debug draw the grid.
        for i in -50..50 {
            let offset = i as f32;
            debug_draw::line(
                Point::new(offset * self.cell_size, -50.0 * self.cell_size, 0.0),
                Point::new(offset * self.cell_size,  50.0 * self.cell_size, 0.0));
            debug_draw::line(
                Point::new(-50.0 * self.cell_size, offset * self.cell_size, 0.0),
                Point::new( 50.0 * self.cell_size, offset * self.cell_size, 0.0));
        }
    }
}

/// A wrapper type around a triple of coordinates that uniquely identify a grid cell.
///
/// # Details
///
/// Grid cells are axis-aligned cubes of a regular sice. The coordinates of a grid cell are its min
/// value. This was chosen because of how it simplifies the calculation to find the cell for a
/// given point (`(point / cell_size).floor()`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GridCell {
    pub x: isize,
    pub y: isize,
    pub z: isize,
}

impl GridCell {
    pub fn new(x: isize, y: isize, z: isize) -> GridCell {
        GridCell {
            x: x,
            y: y,
            z: z,
        }
    }

    pub fn iter_to(&self, dest: GridCell) -> GridIter {
        // assert!(self < dest, "start point for grid iter must be less that end point, or use iter_from()");

        GridIter {
            from: *self,
            to:   dest,
            next: *self,
        }
    }
}

pub struct GridIter {
    from: GridCell,
    to:   GridCell,
    next: GridCell,
}

impl Iterator for GridIter {
    type Item = GridCell;

    fn next(&mut self) -> Option<GridCell> {
        let from = self.from;
        let to = self.to;
        let mut next = self.next;

        if next.z > to.z {
            next.z = from.z;
            if next.y > to.y {
                next.y = from.y;
                if next.x > to.x {
                    return None;
                } else {
                    next.x += 1;
                }
            } else {
                next.y += 1;
            }
        } else {
            next.z += 1;
        }

        ::std::mem::swap(&mut self.next, &mut next);
        Some(next)
    }
}
<commit_msg>Detect BVHs in the Same Cell<commit_after>use std::collections::HashMap;

use math::*;

use scene::Scene;
use ecs::{System, Entity};
use super::bounding_volume::*;
use debug_draw;

/// A collision processor that partitions the space into a regular grid.
///
/// # TODO
///
/// - Do something to configure the size of the grid.
#[derive(Debug, Clone)]
pub struct GridCollisionSystem {
    grid: HashMap<GridCell, Vec<Entity>>,
    cell_size: f32,
}

impl GridCollisionSystem {
    pub fn new() -> GridCollisionSystem {
        GridCollisionSystem {
            grid: HashMap::new(),
            cell_size: 1.0,
        }
    }

    /// Converts a point in world space to its grid cell.
    fn world_to_grid(&self, point: Point) -> GridCell {
        GridCell {
            x: (point.x / self.cell_size).floor() as isize,
            y: (point.y / self.cell_size).floor() as isize,
            z: (point.z / self.cell_size).floor() as isize,
        }
    }
}

impl System for GridCollisionSystem {
    fn update(&mut self, scene: &Scene, _delta: f32) {
        println!("GridCollisionSystem::update()");

        // Clear out grid contents from previous frame, start each frame with an empty grid an
        // rebuilt it rather than trying to update the grid as objects move.
        for (_, mut cell) in &mut self.grid {
            cell.clear();
        }

        let bvh_manager = scene.get_manager::<BoundingVolumeManager>();

        for (bvh, entity) in bvh_manager.iter() {
            println!("grid collision test for {:?}", entity);

            let (min, max) = match bvh.root {
                BoundingVolumeNode::Node { ref volume, left_child: _, right_child: _ } => {
                    match volume {
                        &BoundingVolume::AABB { min, max } => {
                            (min, max)
                        },
                        _ => panic!("Bounding volume hierarchy for entity {:?} does not have an AABB at its root, grid collision is only suported with hierarchies that have an AABB at the root"),
                    }
                },
                BoundingVolumeNode::Leaf(_) => panic!("The root of the bounding volume was a leaf node, which is bad and not okay (and probably shouldn't even be possible :sideeye:)"),
            };

            let grid_cell = self.world_to_grid(min);
            let max_cell = self.world_to_grid(max);
            println!("min cell: {:?}, max cell: {:?}", grid_cell, max_cell);

            // Collide against any existing volumes in the
            for test_cell in grid_cell.iter_to(max_cell) {
                println!("testing {:?} in cell {:?}", entity, test_cell);
                if let Some(mut cell) = self.grid.get_mut(&test_cell) {
                    // Check against other volumes.
                    for other_entity in cell.iter() {
                        let other_bvh = bvh_manager.get(*other_entity).unwrap();
                        println!("{:?} and {:?} in the same cell, testing bvhs for collision", bvh.entity, other_entity);
                        if bvh.test(&*other_bvh) {
                            // Woo, we have a collison.
                            println!("legit collision between {:?} and {:?}", bvh.entity, other_bvh.entity);
                        }
                    }

                    // Add to cell.
                    cell.push(entity);
                    continue;
                }

                // This block should be an else branch on the above if block, but the borrow
                // checker isn't smart enough to tell that the borrow on grid has ended. We do a
                // continue at the end of the previous block so we know if we get here we need to
                // add the cell.
                let cell = vec![entity];
                self.grid.insert(test_cell, cell);
            }

            if let Some(mut cell) = self.grid.get_mut(&grid_cell) {
                cell.push(entity);
                println!("Adding {:?} to cell {:?}", entity, grid_cell);
                continue;
            }

            // This block should be an else branch on the above if block, but the borrow checker
            // isn't smart enough to tell that the borrow on grid has ended. We do a continue at
            // the end of the if block so we can assume if we get here that the cell is not in the
            // grid.
            {
                println!("Creating new cell at {:?} for {:?}", grid_cell, entity);
                self.grid.insert(grid_cell, vec![entity]);
            }
        }

        // Debug draw the grid.
        for i in -50..50 {
            let offset = i as f32;
            debug_draw::line(
                Point::new(offset * self.cell_size, -50.0 * self.cell_size, 0.0),
                Point::new(offset * self.cell_size,  50.0 * self.cell_size, 0.0));
            debug_draw::line(
                Point::new(-50.0 * self.cell_size, offset * self.cell_size, 0.0),
                Point::new( 50.0 * self.cell_size, offset * self.cell_size, 0.0));
        }
    }
}

/// A wrapper type around a triple of coordinates that uniquely identify a grid cell.
///
/// # Details
///
/// Grid cells are axis-aligned cubes of a regular sice. The coordinates of a grid cell are its min
/// value. This was chosen because of how it simplifies the calculation to find the cell for a
/// given point (`(point / cell_size).floor()`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GridCell {
    pub x: isize,
    pub y: isize,
    pub z: isize,
}

impl GridCell {
    pub fn new(x: isize, y: isize, z: isize) -> GridCell {
        GridCell {
            x: x,
            y: y,
            z: z,
        }
    }

    pub fn iter_to(&self, dest: GridCell) -> GridIter {
        // assert!(self < dest, "start point for grid iter must be less that end point, or use iter_from()");

        GridIter {
            from: *self,
            to:   dest,
            next: *self,
        }
    }
}

pub struct GridIter {
    from: GridCell,
    to:   GridCell,
    next: GridCell,
}

impl Iterator for GridIter {
    type Item = GridCell;

    fn next(&mut self) -> Option<GridCell> {
        let from = self.from;
        let to = self.to;
        let mut next = self.next;

        if next.z >= to.z {
            next.z = from.z;
            if next.y >= to.y {
                next.y = from.y;
                if next.x >= to.x {
                    return None;
                } else {
                    next.x += 1;
                }
            } else {
                next.y += 1;
            }
        } else {
            next.z += 1;
        }

        ::std::mem::swap(&mut self.next, &mut next);
        Some(next)
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::f32::{MAX, MIN};
use std::{mem, thread};
use std::sync::{Arc, Mutex, Condvar, RwLock};
use std::sync::mpsc::{self, Receiver, SyncSender};
use std::thread::JoinHandle;

use bootstrap::time::{Timer, TimeMark};
use hash::*;
use math::*;
use stopwatch::Stopwatch;

use ecs::Entity;
use super::bounding_volume::*;

const NUM_WORKERS: usize = 8;
const NUM_WORK_UNITS: usize = 8;

pub type CollisionGrid = HashMap<GridCell, Vec<*const BoundVolume>, FnvHashState>;

/// A collision processor that partitions the space into a regular grid.
///
/// # TODO
///
/// - Do something to configure the size of the grid.
pub struct GridCollisionSystem {
    _workers: Vec<JoinHandle<()>>,
    thread_data: Arc<ThreadData>,
    channel: Receiver<WorkUnit>,
    processed_work: Vec<WorkUnit>,
    pub collisions: HashSet<(Entity, Entity), FnvHashState>,
}

impl GridCollisionSystem {
    pub fn new() -> GridCollisionSystem {
        let thread_data = Arc::new(ThreadData {
            volumes: RwLock::new(Vec::new()),
            pending: (Mutex::new(Vec::new()), Condvar::new()),
        });

        let mut processed_work = Vec::new();
        if NUM_WORK_UNITS == 1 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, MIN, MIN),
                max: Point::new(0.0, 0.0, 0.0),
            }));
        } else if NUM_WORK_UNITS == 2 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::min(),
                max: Point::new(0.0, MAX, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, MIN),
                max: Point::max(),
            }));
        } else if NUM_WORK_UNITS == 4 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::min(),
                max: Point::new(0.0, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, 0.0, MIN),
                max: Point::new(0.0, MAX, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, MIN),
                max: Point::new(MAX, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, 0.0, MIN),
                max: Point::max(),
            }));
        } else if NUM_WORK_UNITS == 8 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, MIN, MIN),
                max: Point::new(0.0, 0.0, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, MIN, 0.0),
                max: Point::new(0.0, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, 0.0, MIN),
                max: Point::new(0.0, MAX, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, 0.0, 0.0),
                max: Point::new(0.0, MAX, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, MIN),
                max: Point::new(MAX, 0.0, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, 0.0),
                max: Point::new(MAX, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, 0.0, MIN),
                max: Point::new(MAX, MAX, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, 0.0, 0.0),
                max: Point::new(MAX, MAX, MAX),
            }));
        } else {
            panic!("unsupported number of workers {}, only 1, 2, 4, or 8 supported", NUM_WORK_UNITS);
        }

        let (sender, receiver) = mpsc::sync_channel(NUM_WORKERS);
        let mut workers = Vec::new();
        for _ in 0..NUM_WORKERS {
            let thread_data = thread_data.clone();
            let sender = sender.clone();
            workers.push(thread::spawn(move || {
                let mut worker = Worker::new(thread_data, sender);
                worker.start();
            }));
        }

        GridCollisionSystem {
            _workers: workers,
            thread_data: thread_data.clone(),
            channel: receiver,
            collisions: HashSet::default(),
            processed_work: processed_work,
        }
    }

    pub fn update(&mut self, bvh_manager: &BoundingVolumeManager) {
        let _stopwatch = Stopwatch::new("Grid Collision System");

        self.collisions.clear();
        let timer = Timer::new();
        let start_time = timer.now();

        let thread_data = &*self.thread_data;

        // Convert all completed work units into pending work units, notifying a worker thread for each one.
        {
            let _stopwatch = Stopwatch::new("Preparing Work Units");

            assert!(
                self.processed_work.len() == NUM_WORK_UNITS,
                "Expected {} complete work units, found {}",
                NUM_WORK_UNITS,
                self.processed_work.len(),
            );
            // Prepare work unit by giving it a copy of the list of volumes.
            {
                let mut volumes = thread_data.volumes.write().unwrap();
                volumes.clone_from(bvh_manager.components());
            }

            let &(ref pending, _) = &thread_data.pending;
            let mut pending = pending.lock().unwrap();

            // Swap all available work units into the pending queue.
            mem::swap(&mut *pending, &mut self.processed_work);
        }

        // Synchronize with worker threads to get them going or whatever.
        {
            let _stopwatch = Stopwatch::new("Synchronizing To Start Workers");
            let &(_, ref condvar) = &thread_data.pending;
            condvar.notify_all();
        }

        // Wait until all work units have been completed and returned.
        let _stopwatch = Stopwatch::new("Running Workers and Merging Results");
        while self.processed_work.len() < NUM_WORK_UNITS {
            // Retrieve each work unit as it becomes available.
            let mut work_unit = self.channel.recv().unwrap();
            work_unit.returned_time = timer.now();

            // Merge results of work unit into total.
            for (collision, _) in work_unit.collisions.drain() {
                self.collisions.insert(collision);
            }
            self.processed_work.push(work_unit);
        }

        println!("\n-- TOP OF GRID UPDATE --");
        println!("Total Time: {}ms", timer.elapsed_ms(start_time));
        for work_unit in &self.processed_work {
            println!(
                "work unit returned: recieved @ {}ms, broadphase @ {}ms, narrowphase @ {}ms, returned @ {}ms",
                timer.duration_ms(work_unit.received_time - start_time),
                timer.duration_ms(work_unit.broadphase_time - start_time),
                timer.duration_ms(work_unit.narrowphase_time - start_time),
                timer.duration_ms(work_unit.returned_time - start_time),
            );
        }
    }
}

impl Clone for GridCollisionSystem {
    /// `GridCollisionSystem` doesn't have any real state between frames, it's only used to reuse
    /// the grid's allocated memory between frames. Therefore to clone it we just invoke
    /// `GridCollisionSystem::new()`.
    fn clone(&self) -> Self {
        GridCollisionSystem::new()
    }
}

#[derive(Debug)]
#[allow(raw_pointer_derive)]
struct WorkUnit {
    collisions: HashMap<(Entity, Entity), (), FnvHashState>, // This should be a HashSet, but HashSet doesn't have a way to get at entries directly.
    bounds: AABB,

    received_time: TimeMark,
    broadphase_time: TimeMark,
    narrowphase_time: TimeMark,
    returned_time: TimeMark,
}

impl WorkUnit {
    fn new(bounds: AABB) -> WorkUnit {
        let timer = Timer::new();
        WorkUnit {
            bounds: bounds,
            collisions: HashMap::default(),
            received_time: timer.now(),
            broadphase_time: timer.now(),
            narrowphase_time: timer.now(),
            returned_time: timer.now(),
        }
    }
}

struct ThreadData {
    volumes: RwLock<Vec<BoundVolume>>,
    pending: (Mutex<Vec<WorkUnit>>, Condvar),
}

struct Worker {
    thread_data: Arc<ThreadData>,
    channel: SyncSender<WorkUnit>,
    grid: HashMap<GridCell, Vec<*const BoundVolume>, FnvHashState>,
    cell_size: f32,

    candidate_collisions: Vec<(*const BoundVolume, *const BoundVolume)>,
}

impl Worker {
    fn new(thread_data: Arc<ThreadData>, channel: SyncSender<WorkUnit>) -> Worker {
        Worker {
            thread_data: thread_data,
            channel: channel,
            grid: HashMap::default(),
            cell_size: 1.0,
            candidate_collisions: Vec::new(),
        }
    }

    fn start(&mut self) {
        let timer = Timer::new();
        loop {
            // Wait until there's pending work, and take the first available one.
            let mut work = {
                let &(ref pending, ref condvar) = &self.thread_data.pending;
                let mut pending = pending.lock().unwrap();
                while pending.len() == 0 {
                    pending = condvar.wait(pending).unwrap();
                }

                pending.pop().unwrap()
            };
            work.received_time = timer.now();

            self.do_broadphase(&work);
            work.broadphase_time = timer.now();

            self.do_narrowphase(&mut work);
            work.narrowphase_time = timer.now();

            // Send completed work back to main thread.
            self.channel.send(work).unwrap();
        }
    }

    fn do_broadphase(&mut self, work: &WorkUnit) {
        // let _stopwatch = Stopwatch::new("Broadphase Testing (Grid Based)");
        let volumes = self.thread_data.volumes.read().unwrap();
        for bvh in &*volumes {
            // Retrieve the AABB at the root of the BVH.
            let aabb = bvh.aabb;

            // Only test volumes that are within the bounds of this work unit's testing area.
            if !aabb.test_aabb(&work.bounds) {
                continue;
            }

            let min_cell = self.world_to_grid(aabb.min);
            let max_cell = self.world_to_grid(aabb.max);

            // Iterate over all grid cells that the AABB touches. Test the BVH against any entities
            // that have already been placed in that cell, then add the BVH to the cell, creating
            // new cells as necessary.
            for test_cell in min_cell.iter_to(max_cell) {
                if let Some(mut cell) = self.grid.get_mut(&test_cell) {
                    // Check against other volumes.
                    for other_bvh in cell.iter().cloned() {
                        self.candidate_collisions.push((bvh, other_bvh));
                    }

                    // Add to existing cell.
                    cell.push(bvh);
                    continue;
                }
                // else
                {
                    let cell = vec![bvh as *const _];
                    self.grid.insert(test_cell, cell);
                }
            }
        }

        // Clear out grid contents from previous frame, start each frame with an empty grid and
        // rebuild it rather than trying to update the grid as objects move.
        for (_, mut cell) in &mut self.grid {
            cell.clear();
        }
    }

    fn do_narrowphase(&mut self, work: &mut WorkUnit) {
        // let _stopwatch = Stopwatch::new("Narrowphase Testing");
        for (bvh, other_bvh) in self.candidate_collisions.drain(0..) {
            let bvh = unsafe { &*bvh };
            let other_bvh = unsafe { &*other_bvh };
            let collision_pair = (bvh.entity, other_bvh.entity);

            // Check if the collision has already been detected before running the
            // collision test since it's potentially very expensive. We get the entry
            // directly, that way we only have to do one hash lookup.
            match work.collisions.entry(collision_pair) {
                Entry::Vacant(vacant_entry) => {
                    // Collision hasn't already been detected, so do the test.
                    if bvh.test(other_bvh) {
                        // Woo, we have a collison.
                        vacant_entry.insert(());
                    }
                },
                _ => {},
            }
        }
    }

    /// Converts a point in world space to its grid cell.
    fn world_to_grid(&self, point: Point) -> GridCell {
        GridCell {
            x: (point.x / self.cell_size).floor() as GridCoord,
            y: (point.y / self.cell_size).floor() as GridCoord,
            z: (point.z / self.cell_size).floor() as GridCoord,
        }
    }
}

/// A wrapper type around a triple of coordinates that uniquely identify a grid cell.
///
/// # Details
///
/// Grid cells are axis-aligned cubes of a regular sice. The coordinates of a grid cell are its min
/// value. This was chosen because of how it simplifies the calculation to find the cell for a
/// given point (`(point / cell_size).floor()`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GridCell {
    pub x: GridCoord,
    pub y: GridCoord,
    pub z: GridCoord,
}

// TODO: Using i16 for the grid coordinate makes the hash lookups substantially faster, but it means
//       we'll have to take extra care when mapping world coordinates to grid coordinates. Points
//       outside the representable range should be wrapped around. This will technically lead to
//       more grid collisions, but extras will be culled quickly by the AABB test so it shouldn't
//       be more of a performance hit than what we gained from converting to using i16s.
pub type GridCoord = i16;

impl GridCell {
    pub fn new(x: GridCoord, y: GridCoord, z: GridCoord) -> GridCell {
        GridCell {
            x: x,
            y: y,
            z: z,
        }
    }

    pub fn iter_to(&self, dest: GridCell) -> GridIter {
        // assert!(self < dest, "start point for grid iter must be less that end point, or use iter_from()");

        GridIter {
            from: *self,
            to:   dest,
            next: *self,
        }
    }
}

pub struct GridIter {
    from: GridCell,
    to:   GridCell,
    next: GridCell,
}

impl Iterator for GridIter {
    type Item = GridCell;

    fn next(&mut self) -> Option<GridCell> {
        let from = self.from;
        let to = self.to;
        let mut next = self.next;

        if next.z >= to.z {
            next.z = from.z;
            if next.y >= to.y {
                next.y = from.y;
                if next.x >= to.x {
                    return None;
                } else {
                    next.x += 1;
                }
            } else {
                next.y += 1;
            }
        } else {
            next.z += 1;
        }

        mem::swap(&mut self.next, &mut next);
        Some(next)
    }
}
<commit_msg>Move Grid Data Into WorkUnit<commit_after>use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::f32::{MAX, MIN};
use std::{mem, thread};
use std::sync::{Arc, Mutex, Condvar, RwLock};
use std::sync::mpsc::{self, Receiver, SyncSender};
use std::thread::JoinHandle;

use bootstrap::time::{Timer, TimeMark};
use hash::*;
use math::*;
use stopwatch::Stopwatch;

use ecs::Entity;
use super::bounding_volume::*;

const NUM_WORKERS: usize = 8;
const NUM_WORK_UNITS: usize = 8;

pub type CollisionGrid = HashMap<GridCell, Vec<*const BoundVolume>, FnvHashState>;

/// A collision processor that partitions the space into a regular grid.
///
/// # TODO
///
/// - Do something to configure the size of the grid.
pub struct GridCollisionSystem {
    _workers: Vec<JoinHandle<()>>,
    thread_data: Arc<ThreadData>,
    channel: Receiver<WorkUnit>,
    processed_work: Vec<WorkUnit>,
    pub collisions: HashSet<(Entity, Entity), FnvHashState>,
}

impl GridCollisionSystem {
    pub fn new() -> GridCollisionSystem {
        let thread_data = Arc::new(ThreadData {
            volumes: RwLock::new(Vec::new()),
            pending: (Mutex::new(Vec::new()), Condvar::new()),
        });

        let mut processed_work = Vec::new();
        if NUM_WORK_UNITS == 1 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, MIN, MIN),
                max: Point::new(0.0, 0.0, 0.0),
            }));
        } else if NUM_WORK_UNITS == 2 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::min(),
                max: Point::new(0.0, MAX, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, MIN),
                max: Point::max(),
            }));
        } else if NUM_WORK_UNITS == 4 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::min(),
                max: Point::new(0.0, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, 0.0, MIN),
                max: Point::new(0.0, MAX, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, MIN),
                max: Point::new(MAX, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, 0.0, MIN),
                max: Point::max(),
            }));
        } else if NUM_WORK_UNITS == 8 {
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, MIN, MIN),
                max: Point::new(0.0, 0.0, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, MIN, 0.0),
                max: Point::new(0.0, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, 0.0, MIN),
                max: Point::new(0.0, MAX, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(MIN, 0.0, 0.0),
                max: Point::new(0.0, MAX, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, MIN),
                max: Point::new(MAX, 0.0, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, MIN, 0.0),
                max: Point::new(MAX, 0.0, MAX),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, 0.0, MIN),
                max: Point::new(MAX, MAX, 0.0),
            }));
            processed_work.push(WorkUnit::new(AABB {
                min: Point::new(0.0, 0.0, 0.0),
                max: Point::new(MAX, MAX, MAX),
            }));
        } else {
            panic!("unsupported number of workers {}, only 1, 2, 4, or 8 supported", NUM_WORK_UNITS);
        }

        let (sender, receiver) = mpsc::sync_channel(NUM_WORKERS);
        let mut workers = Vec::new();
        for _ in 0..NUM_WORKERS {
            let thread_data = thread_data.clone();
            let sender = sender.clone();
            workers.push(thread::spawn(move || {
                let mut worker = Worker::new(thread_data, sender);
                worker.start();
            }));
        }

        GridCollisionSystem {
            _workers: workers,
            thread_data: thread_data.clone(),
            channel: receiver,
            collisions: HashSet::default(),
            processed_work: processed_work,
        }
    }

    pub fn update(&mut self, bvh_manager: &BoundingVolumeManager) {
        let _stopwatch = Stopwatch::new("Grid Collision System");

        self.collisions.clear();
        let timer = Timer::new();
        let start_time = timer.now();

        let thread_data = &*self.thread_data;

        // Convert all completed work units into pending work units, notifying a worker thread for each one.
        {
            let _stopwatch = Stopwatch::new("Preparing Work Units");

            assert!(
                self.processed_work.len() == NUM_WORK_UNITS,
                "Expected {} complete work units, found {}",
                NUM_WORK_UNITS,
                self.processed_work.len(),
            );
            // Prepare work unit by giving it a copy of the list of volumes.
            {
                let mut volumes = thread_data.volumes.write().unwrap();
                volumes.clone_from(bvh_manager.components());
            }

            let &(ref pending, _) = &thread_data.pending;
            let mut pending = pending.lock().unwrap();

            // Swap all available work units into the pending queue.
            mem::swap(&mut *pending, &mut self.processed_work);
        }

        // Synchronize with worker threads to get them going or whatever.
        {
            let _stopwatch = Stopwatch::new("Synchronizing To Start Workers");
            let &(_, ref condvar) = &thread_data.pending;
            condvar.notify_all();
        }

        // Wait until all work units have been completed and returned.
        let _stopwatch = Stopwatch::new("Running Workers and Merging Results");
        while self.processed_work.len() < NUM_WORK_UNITS {
            // Retrieve each work unit as it becomes available.
            let mut work_unit = self.channel.recv().unwrap();
            work_unit.returned_time = timer.now();

            // Merge results of work unit into total.
            for (collision, _) in work_unit.collisions.drain() {
                self.collisions.insert(collision);
            }
            self.processed_work.push(work_unit);
        }

        println!("\n-- TOP OF GRID UPDATE --");
        println!("Total Time: {}ms", timer.elapsed_ms(start_time));
        for work_unit in &self.processed_work {
            println!(
                "work unit returned: recieved @ {}ms, broadphase @ {}ms, narrowphase @ {}ms, returned @ {}ms",
                timer.duration_ms(work_unit.received_time - start_time),
                timer.duration_ms(work_unit.broadphase_time - start_time),
                timer.duration_ms(work_unit.narrowphase_time - start_time),
                timer.duration_ms(work_unit.returned_time - start_time),
            );
        }
    }
}

impl Clone for GridCollisionSystem {
    /// `GridCollisionSystem` doesn't have any real state between frames, it's only used to reuse
    /// the grid's allocated memory between frames. Therefore to clone it we just invoke
    /// `GridCollisionSystem::new()`.
    fn clone(&self) -> Self {
        GridCollisionSystem::new()
    }
}

#[derive(Debug)]
#[allow(raw_pointer_derive)]
struct WorkUnit {
    collisions: HashMap<(Entity, Entity), (), FnvHashState>, // This should be a HashSet, but HashSet doesn't have a way to get at entries directly.
    bounds: AABB,

    grid: HashMap<GridCell, Vec<*const BoundVolume>, FnvHashState>,
    cell_size: f32,

    received_time: TimeMark,
    broadphase_time: TimeMark,
    narrowphase_time: TimeMark,
    returned_time: TimeMark,
}

impl WorkUnit {
    fn new(bounds: AABB) -> WorkUnit {
        let timer = Timer::new();
        WorkUnit {
            bounds: bounds,
            collisions: HashMap::default(),

            grid: HashMap::default(),
            cell_size: 1.0,

            received_time: timer.now(),
            broadphase_time: timer.now(),
            narrowphase_time: timer.now(),
            returned_time: timer.now(),
        }
    }

    /// Converts a point in world space to its grid cell.
    fn world_to_grid(&self, point: Point) -> GridCell {
        GridCell {
            x: (point.x / self.cell_size).floor() as GridCoord,
            y: (point.y / self.cell_size).floor() as GridCoord,
            z: (point.z / self.cell_size).floor() as GridCoord,
        }
    }
}

unsafe impl ::std::marker::Send for WorkUnit {}

struct ThreadData {
    volumes: RwLock<Vec<BoundVolume>>,
    pending: (Mutex<Vec<WorkUnit>>, Condvar),
}

struct Worker {
    thread_data: Arc<ThreadData>,
    channel: SyncSender<WorkUnit>,

    candidate_collisions: Vec<(*const BoundVolume, *const BoundVolume)>,
}

impl Worker {
    fn new(thread_data: Arc<ThreadData>, channel: SyncSender<WorkUnit>) -> Worker {
        Worker {
            thread_data: thread_data,
            channel: channel,
            candidate_collisions: Vec::new(),
        }
    }

    fn start(&mut self) {
        let timer = Timer::new();
        loop {
            // Wait until there's pending work, and take the first available one.
            let mut work = {
                let &(ref pending, ref condvar) = &self.thread_data.pending;
                let mut pending = pending.lock().unwrap();
                while pending.len() == 0 {
                    pending = condvar.wait(pending).unwrap();
                }

                pending.pop().unwrap()
            };
            work.received_time = timer.now();

            self.do_broadphase(&mut work);
            work.broadphase_time = timer.now();

            self.do_narrowphase(&mut work);
            work.narrowphase_time = timer.now();

            // Send completed work back to main thread.
            self.channel.send(work).unwrap();
        }
    }

    fn do_broadphase(&mut self, work: &mut WorkUnit) {
        // let _stopwatch = Stopwatch::new("Broadphase Testing (Grid Based)");
        let volumes = self.thread_data.volumes.read().unwrap();
        for bvh in &*volumes {
            // Retrieve the AABB at the root of the BVH.
            let aabb = bvh.aabb;

            // Only test volumes that are within the bounds of this work unit's testing area.
            if !aabb.test_aabb(&work.bounds) {
                continue;
            }

            let min_cell = work.world_to_grid(aabb.min);
            let max_cell = work.world_to_grid(aabb.max);

            // Iterate over all grid cells that the AABB touches. Test the BVH against any entities
            // that have already been placed in that cell, then add the BVH to the cell, creating
            // new cells as necessary.
            for test_cell in min_cell.iter_to(max_cell) {
                if let Some(mut cell) = work.grid.get_mut(&test_cell) {
                    // Check against other volumes.
                    for other_bvh in cell.iter().cloned() {
                        self.candidate_collisions.push((bvh, other_bvh));
                    }

                    // Add to existing cell.
                    cell.push(bvh);
                    continue;
                }
                // else
                {
                    let cell = vec![bvh as *const _];
                    work.grid.insert(test_cell, cell);
                }
            }
        }

        // Clear out grid contents from previous frame, start each frame with an empty grid and
        // rebuild it rather than trying to update the grid as objects move.
        for (_, mut cell) in &mut work.grid {
            cell.clear();
        }
    }

    fn do_narrowphase(&mut self, work: &mut WorkUnit) {
        // let _stopwatch = Stopwatch::new("Narrowphase Testing");
        for (bvh, other_bvh) in self.candidate_collisions.drain(0..) {
            let bvh = unsafe { &*bvh };
            let other_bvh = unsafe { &*other_bvh };
            let collision_pair = (bvh.entity, other_bvh.entity);

            // Check if the collision has already been detected before running the
            // collision test since it's potentially very expensive. We get the entry
            // directly, that way we only have to do one hash lookup.
            match work.collisions.entry(collision_pair) {
                Entry::Vacant(vacant_entry) => {
                    // Collision hasn't already been detected, so do the test.
                    if bvh.test(other_bvh) {
                        // Woo, we have a collison.
                        vacant_entry.insert(());
                    }
                },
                _ => {},
            }
        }
    }
}

/// A wrapper type around a triple of coordinates that uniquely identify a grid cell.
///
/// # Details
///
/// Grid cells are axis-aligned cubes of a regular sice. The coordinates of a grid cell are its min
/// value. This was chosen because of how it simplifies the calculation to find the cell for a
/// given point (`(point / cell_size).floor()`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GridCell {
    pub x: GridCoord,
    pub y: GridCoord,
    pub z: GridCoord,
}

// TODO: Using i16 for the grid coordinate makes the hash lookups substantially faster, but it means
//       we'll have to take extra care when mapping world coordinates to grid coordinates. Points
//       outside the representable range should be wrapped around. This will technically lead to
//       more grid collisions, but extras will be culled quickly by the AABB test so it shouldn't
//       be more of a performance hit than what we gained from converting to using i16s.
pub type GridCoord = i16;

impl GridCell {
    pub fn new(x: GridCoord, y: GridCoord, z: GridCoord) -> GridCell {
        GridCell {
            x: x,
            y: y,
            z: z,
        }
    }

    pub fn iter_to(&self, dest: GridCell) -> GridIter {
        // assert!(self < dest, "start point for grid iter must be less that end point, or use iter_from()");

        GridIter {
            from: *self,
            to:   dest,
            next: *self,
        }
    }
}

pub struct GridIter {
    from: GridCell,
    to:   GridCell,
    next: GridCell,
}

impl Iterator for GridIter {
    type Item = GridCell;

    fn next(&mut self) -> Option<GridCell> {
        let from = self.from;
        let to = self.to;
        let mut next = self.next;

        if next.z >= to.z {
            next.z = from.z;
            if next.y >= to.y {
                next.y = from.y;
                if next.x >= to.x {
                    return None;
                } else {
                    next.x += 1;
                }
            } else {
                next.y += 1;
            }
        } else {
            next.z += 1;
        }

        mem::swap(&mut self.next, &mut next);
        Some(next)
    }
}
<|endoftext|>


<commit_before>
extern crate num;
use num::complex::Complex;

#[derive(Debug)]
struct ModelSingle {
    num_kernels: usize,
    kernels: Vec<Complex<f64>>,
}

#[derive(Debug)]
struct ModelArray {
    num_models: usize,
    models: Vec<ModelSingle>,
}

#[derive(Debug)]
struct KernelIterator<'a> {
    num_kernels: usize,
    state: (usize, usize),
    ref_mod: &'a ModelArray,
    full_rank: Vec<usize>,
    reduced_rank: Vec<usize>,
}

impl<'b> KernelIterator<'b> {
    fn new(num_kernels: usize, ref_mod: &'b ModelArray) -> KernelIterator {
        let num_models = ref_mod.num_models;
        let mut full_rank     = vec![0; num_models];
        let mut reduced_rank  = vec![0; num_models];
        let mut total_kernels = 0;
        for i in 0..num_models {
            let nkernels = ref_mod.models[i].num_kernels;
            full_rank[i] = nkernels;
            total_kernels += nkernels;
        }
        let delta = total_kernels - num_kernels;

        println!("total_kernels: {} num_kernels: {} delta: {}",
                 total_kernels, num_kernels, delta);

        for i in 0..num_models {
            let nkernels = full_rank[i];
            let fraction = nkernels as f64 / total_kernels as f64;
            reduced_rank[i] = nkernels - ((delta as f64 * fraction) as usize);
        }

        KernelIterator { num_kernels: num_kernels, state : (0, 0), ref_mod: ref_mod,
                         full_rank: full_rank, reduced_rank: reduced_rank}
    }
}

impl<'c> Iterator for KernelIterator<'c> {
    type Item = Vec<Complex<f64>>;

    fn next(&mut self) -> Option<Vec<Complex<f64>>> {
        None
    }
}

fn main() {
    println!("This is a kernel_iterator test");

    let kq1 = ModelSingle{num_kernels: 21, kernels: Vec::new()};
    let kq2 = ModelSingle{num_kernels: 75, kernels: Vec::new()};
    let kq3 = ModelSingle{num_kernels: 121, kernels: Vec::new()};

    let mut mod_array = ModelArray {num_models: 3, models: Vec::new()};
    mod_array.models.push(kq1);
    mod_array.models.push(kq2);
    mod_array.models.push(kq3);

    println!("mod_array is constructed and properly initialized: {:?}", mod_array);

    let mut ki = KernelIterator::new(17, &mod_array);

    println!("KernelIterator is constructed and properly initialized:m {:?}", ki);
}
<commit_msg>`valid_state()` and `advance_state()`.<commit_after>
extern crate num;
use num::complex::Complex;

#[derive(Debug)]
struct ModelSingle {
    num_kernels: usize,
    kernels: Vec<Complex<f64>>,
}

#[derive(Debug)]
struct ModelArray {
    num_models: usize,
    models: Vec<ModelSingle>,
}

#[derive(Debug)]
struct KernelIterator<'a> {
    num_kernels: usize,
    state: (usize, usize),
    ref_mod: &'a ModelArray,
    full_rank: Vec<usize>,
    reduced_rank: Vec<usize>,
}

impl<'b> KernelIterator<'b> {
    fn new(num_kernels: usize, ref_mod: &'b ModelArray) -> KernelIterator {
        let num_models = ref_mod.num_models;
        let mut full_rank     = vec![0; num_models];
        let mut reduced_rank  = vec![0; num_models];
        let mut total_kernels = 0;
        for i in 0..num_models {
            let nkernels = ref_mod.models[i].num_kernels;
            full_rank[i] = nkernels;
            total_kernels += nkernels;
        }
        let delta = total_kernels - num_kernels;

        println!("total_kernels: {} num_kernels: {} delta: {}",
                 total_kernels, num_kernels, delta);

        for i in 0..num_models {
            let nkernels = full_rank[i];
            let fraction = nkernels as f64 / total_kernels as f64;
            reduced_rank[i] = nkernels - ((delta as f64 * fraction) as usize);
        }

        KernelIterator { num_kernels: num_kernels, state : (0, 0), ref_mod: ref_mod,
                         full_rank: full_rank, reduced_rank: reduced_rank}
    }

    fn valid_state(&self) -> bool {
        let (model_index, kernel_index) = self.state;
        model_index < self.ref_mod.num_models && kernel_index < self.full_rank[model_index]
    }

    fn advance_state(&mut self) -> bool {
        let (model_index, kernel_index) = self.state;
        if kernel_index+1 < self.reduced_rank[model_index] {
            self.state = (model_index, kernel_index + 1);
            true
        } else if model_index < self.ref_mod.num_models {
            self.state = (model_index + 1, kernel_index);
            true
        } else {
            false
        }
    }
}

impl<'c> Iterator for KernelIterator<'c> {
    type Item = Vec<Complex<f64>>;

    fn next(&mut self) -> Option<Vec<Complex<f64>>> {
        let (model_index, kernel_index) = self.state;
        let m = self.ref_mod;
        //if
        None
    }
}

fn main() {
    println!("This is a kernel_iterator test");

    let kq1 = ModelSingle{num_kernels: 21, kernels: Vec::new()};
    let kq2 = ModelSingle{num_kernels: 75, kernels: Vec::new()};
    let kq3 = ModelSingle{num_kernels: 121, kernels: Vec::new()};

    let mut mod_array = ModelArray {num_models: 3, models: Vec::new()};
    mod_array.models.push(kq1);
    mod_array.models.push(kq2);
    mod_array.models.push(kq3);

    println!("mod_array is constructed and properly initialized: {:?}", mod_array);

    let mut ki = KernelIterator::new(17, &mod_array);

    println!("KernelIterator is constructed and properly initialized: {:?}", ki);

    let mut done = false;
    while !done {
        let kernel = ki.next();
        match kernel {
            Some(_) => done = false,
            None    => done = true
        }
    }
}

<|endoftext|>


<commit_before>extern crate serde_derive;

use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::iter::TakeWhile;
use std::path::{Path, PathBuf};

use chrono::{DateTime, Utc};
use clap::{App, Arg, ArgMatches, SubCommand};
use glob::glob;
use serde_derive::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Clone)]
struct RepoConfig {
    path: String,
    #[serde(default = "default_false")]
    commit: bool,
    #[serde(default = "default_false")]
    sync_updates: bool,
}

fn default_false() -> bool {
    false
}

#[derive(Deserialize, Debug)]
struct Supfile {
    repo: RepoConfig,
    editor: Option<String>,
}

impl Supfile {
    pub fn from(str: &str) -> Result<Self, toml::de::Error> {
        toml::from_str(&str)
    }

    pub fn find() -> PathBuf {
        // TODO: pull in XDG crate for paths
        let default_dir = "~/.config/sup/";

        ["./", default_dir]
            .iter()
            .map(|dir| Path::new(dir).join("Supfile"))
            .find(|p| p.exists())
            .unwrap_or_else(|| {
                // TODO: offer to create path somehow
                panic!("can't find supfile")
            })
    }
}

#[derive(Deserialize, Serialize)]
enum TaskState {
    Todo,
    InProgress,
    Complete,
    Canceled,
    Deferred { until: DateTime<Utc> },
}

#[derive(Deserialize, Serialize)]
enum UpdateKind {
    Note,
    Task(TaskState),
}

#[derive(Deserialize, Serialize)]
enum Action {
    Create {
        task: String,
        notes: Option<String>,
        tags: Vec<String>,
    },
    EditTags {
        add: Option<Vec<String>>,
        remove: Option<Vec<String>>,
    },
    EditTask(String),
    EditNotes(Option<String>),
    EditState(TaskState),
}

#[derive(Deserialize, Serialize)]
struct UpdateEntry {
    id: String,
    timestamp: DateTime<Utc>,
    action: Action,
    tags: Vec<String>,
}

impl UpdateEntry {
    fn new(line: &str) -> Self {
        unimplemented!()
    }
}

#[derive(Debug)]
struct SupdateLog {
    name: String,
    file: BufReader<File>,
}

impl SupdateLog {
    fn new(path: &Path) -> Self {
        let name = path
            .file_name()
            .expect("need file, not directory")
            .to_string_lossy()
            .into();

        let file = File::open(path).expect("could not open file");
        // TODO: Support versioning in line

        SupdateLog {
            name,
            file: BufReader::new(file),
        }
    }

    fn is_rollup(&self) -> bool {
        self.name.ends_with("0000_rollup.sup")
    }
}

impl Iterator for SupdateLog {
    type Item = UpdateEntry;

    fn next(&mut self) -> Option<UpdateEntry> {
        let mut line = String::new();
        return match self.file.read_line(&mut line) {
            // End of file
            Ok(0) => None,

            Ok(_) => {
                println!("line = {:?}", line);
                Some(UpdateEntry::new(&line))
            }

            Err(e) => {
                println!("Failed to read line: {:?}", e);
                None
            }
        };
    }
}

/**
 * A Repository represents a directory containing SupdateLogs.
 *
 * The directory structure is something like this:
 *
 * repo/
 *   2019/
 *     08/
 *       20190800_rollup.sup
 *       20190801_235959.sup
 *       20190802_003030.sup
 *       20190802_123030.sup
 *
 * Rollups are special SupdateLogs which contain the collapsed content
 * of earlier entries, to put a bound on the number of entries which
 * must be read in order to construct the current state.
 */

struct Repository {
    config: RepoConfig,
}

impl Repository {
    fn new(cfg: RepoConfig) -> Self {
        // TODO: Handle error, return Result
        if !Path::new(&cfg.path).exists() {
            panic!("given repository does not exist")
        }

        Repository { config: cfg }
    }

    fn build_rollup(&self, updates: Vec<SupdateLog>) -> Rollup {
        // Only need to go until the last rollup
        let updates: Vec<&SupdateLog> =
            updates.iter().take_while(|it| !it.is_rollup()).collect();

        // Reverse ordering so that we consume oldest elements first.
        for update in updates.iter().rev() {
            println!("reading: {:?}", update)
        }

        Rollup {}
    }

    fn read_updates(&self) -> Vec<SupdateLog> {
        let path = Path::new(&self.config.path).join("*/*/**.sup");

        // TODO: This is really wasteful. Make this an iterator
        // Get list of all files in the repo
        let mut updates = glob(path.to_str().unwrap())
            .expect("glob failed")
            .filter_map(|f| f.map(|p| SupdateLog::new(&p)).ok())
            .collect::<Vec<SupdateLog>>();

        // We want sorted (chronological, desc) order, based on file name.
        updates.sort_by(|a, b| b.name.cmp(&a.name));
        return updates;
    }
}

struct Rollup {}

struct SupApp {
    matches: ArgMatches<'static>,
}

impl SupApp {
    fn new() -> Self {
        SupApp {
            matches: Self::parse_args(),
        }
    }

    fn parse_args<'a>() -> ArgMatches<'a> {
        App::new("sup")
            .version("0.0.0")
            .about("Keep track of what's up.")
            .arg(
                Arg::with_name("config")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .takes_value(true)
                    .help("Set custom location for Supfile"),
            )
            .subcommand(
                SubCommand::with_name("show")
                    .about("asdf")
                    .arg(Arg::with_name("DATE").takes_value(true).help("asdf")),
            )
            .subcommand(
                SubCommand::with_name("serve")
                    .about("launch web interface")
                    .arg(
                        Arg::with_name("port")
                            .short("p")
                            .long("port")
                            .takes_value(true),
                    ),
            )
            .get_matches()
    }

    fn load_config(&self) -> Result<Supfile, Box<dyn Error>> {
        let path = self
            .matches
            .value_of("config")
            .map(|p| Path::new(p).to_path_buf())
            .unwrap_or_else(Supfile::find);

        let bytes = std::fs::read_to_string(path.as_os_str())?;
        Ok(Supfile::from(&bytes)?)
    }

    fn run(&self) -> Result<(), Box<dyn Error>> {
        let cfg = self.load_config()?;
        let repo = Repository::new(cfg.repo.clone());

        match self.matches.subcommand() {
            ("show", Some(m)) => println!("TODO: show {:?}", m),
            ("serve", Some(m)) => println!("TODO: serve {:?}", m),
            _ => {
                println!("add update");

                run_add_update(&cfg)?;
            }
        };

        Ok(())
    }
}

fn run_add_update(_cfg: &Supfile) -> Result<(), Box<dyn Error>> {
    Ok(())
}

// TODO: Investiage this instead of Box<Error>
// https://github.com/rust-lang-nursery/failure
fn main() {
    let app = SupApp::new();
    match app.run() {
        Err(e) => println!("oh no! {:?}", e),
        Ok(_) => (),
    };
}
<commit_msg>Roll updates into task struct<commit_after>extern crate serde_derive;

use std::collections::{BTreeMap, HashSet};
use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};

use chrono::{DateTime, Utc};
use clap::{App, Arg, ArgMatches, SubCommand};
use glob::glob;
use serde_derive::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Clone)]
struct RepoConfig {
    path: String,
    #[serde(default = "default_false")]
    commit: bool,
    #[serde(default = "default_false")]
    sync_updates: bool,
}

fn default_false() -> bool {
    false
}

#[derive(Deserialize, Debug)]
struct Supfile {
    repo: RepoConfig,
    editor: Option<String>,
}

impl Supfile {
    pub fn from(str: &str) -> Result<Self, toml::de::Error> {
        toml::from_str(&str)
    }

    pub fn find() -> PathBuf {
        // TODO: pull in XDG crate for paths
        let default_dir = "~/.config/sup/";

        ["./", default_dir]
            .iter()
            .map(|dir| Path::new(dir).join("Supfile"))
            .find(|p| p.exists())
            .unwrap_or_else(|| {
                // TODO: offer to create path somehow
                panic!("can't find supfile")
            })
    }
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
enum TaskState {
    Todo,
    InProgress,
    Complete,
    Canceled,
    Deferred { until: DateTime<Utc> },
}

#[derive(Deserialize, Serialize)]
enum Action {
    Create {
        task: String,
        notes: Option<String>,
        tags: Vec<String>,
    },
    EditTags {
        add: Vec<String>,
        remove: Vec<String>,
    },
    EditTask(String),
    EditNotes(Option<String>),
    EditState(TaskState),
}

#[derive(Deserialize, Serialize)]
struct TaskUpdate {
    id: String,
    timestamp: DateTime<Utc>,
    action: Action,
}

impl TaskUpdate {
    fn new(line: &str) -> Self {
        unimplemented!()
    }
}

#[derive(Clone, Debug)]
struct Task {
    id: String,
    created_at: DateTime<Utc>,
    completed_at: Option<DateTime<Utc>>,
    state: TaskState,
    task: String,
    notes: Option<String>,
    tags: HashSet<String>,
}

impl Task {
    fn apply(&mut self, update: &TaskUpdate) {
        match &update.action {
            Action::Create { task, notes, tags } => {
                self.task = task.clone();
                self.notes = notes.clone();
                for tag in tags.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTags { add, remove } => {
                for tag in remove.iter() {
                    self.tags.remove(tag);
                }

                for tag in add.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTask(task) => self.task = task.clone(),
            Action::EditNotes(notes) => self.notes = notes.clone(),
            Action::EditState(state) => {
                self.state = state.clone();
                if state == &TaskState::Complete {
                    self.completed_at = Some(update.timestamp);
                }
            }
        }
    }
}

impl From<&TaskUpdate> for Task {
    fn from(update: &TaskUpdate) -> Self {
        let mut task = Task {
            id: update.id.clone(),
            created_at: update.timestamp.clone(),
            completed_at: None,
            state: TaskState::Todo,
            task: "untitled".to_string(),
            notes: None,
            tags: HashSet::new(),
        };

        task.apply(update);
        return task;
    }
}

#[derive(Debug)]
struct SupdateLog {
    name: String,
    file: BufReader<File>,
}

impl SupdateLog {
    fn new(path: &Path) -> Self {
        let name = path
            .file_name()
            .expect("need file, not directory")
            .to_string_lossy()
            .into();

        let file = File::open(path).expect("could not open file");
        // TODO: Support versioning in line

        SupdateLog {
            name,
            file: BufReader::new(file),
        }
    }
}

impl Iterator for SupdateLog {
    type Item = TaskUpdate;

    fn next(&mut self) -> Option<TaskUpdate> {
        let mut line = String::new();
        return match self.file.read_line(&mut line) {
            // End of file
            Ok(0) => None,

            Ok(_) => {
                println!("line = {:?}", line);
                Some(TaskUpdate::new(&line))
            }

            Err(e) => {
                println!("Failed to read line: {:?}", e);
                None
            }
        };
    }
}

/**
 * A Repository represents a directory containing SupdateLogs.
 *
 * The directory structure is something like this:
 *
 * repo/
 *   2019/
 *     08/
 *       20190801_235959.sup
 *       20190802_003030.sup
 *       20190802_123030.sup
 *
 * TODO: Support concept of `rollup` files to put an upper limit on
 * how far we need to look back to fully reconstruct the state of
 * everything.
 */

struct Repository {
    config: RepoConfig,
}

impl Repository {
    fn new(cfg: RepoConfig) -> Self {
        // TODO: Handle error, return Result
        if !Path::new(&cfg.path).exists() {
            panic!("given repository does not exist")
        }

        Repository { config: cfg }
    }

    fn build_journal(&self, updates: Vec<SupdateLog>) -> Journal {
        // Reverse ordering so that we consume oldest elements first.
        for update in updates.iter().rev() {
            println!("reading: {:?}", update)
        }

        Journal {
            entries: BTreeMap::new(),
        }
    }

    fn read_updates(&self) -> Vec<SupdateLog> {
        let path = Path::new(&self.config.path).join("*/*/**.sup");

        // TODO: This is really wasteful. Make this an iterator
        // Get list of all files in the repo
        let mut updates = glob(path.to_str().unwrap())
            .expect("glob failed")
            .filter_map(|f| f.map(|p| SupdateLog::new(&p)).ok())
            .collect::<Vec<SupdateLog>>();

        // We want sorted (chronological, desc) order, based on file name.
        updates.sort_by(|a, b| b.name.cmp(&a.name));
        return updates;
    }
}

/**
 * A collection of Updates.
 *
 * Represents the cumulative, "rolled up" set of task updates.
 */
struct Journal {
    /// id -> task
    entries: BTreeMap<String, Task>,
}

impl Journal {
    fn new() -> Self {
        return Self {
            entries: BTreeMap::new(),
        };
    }

    fn add(&mut self, update: &TaskUpdate) {
        let task = self.entries.get_mut(&update.id);

        match task {
            Some(t) => t.apply(update),
            None => {
                self.entries.insert(update.id.clone(), Task::from(update));
            }
        }
    }
}

struct SupApp {
    matches: ArgMatches<'static>,
}

impl SupApp {
    fn new() -> Self {
        Self {
            matches: Self::parse_args(),
        }
    }

    fn parse_args<'a>() -> ArgMatches<'a> {
        App::new("sup")
            .version("0.0.0")
            .about("Keep track of what's up.")
            .arg(
                Arg::with_name("config")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .takes_value(true)
                    .help("Set custom location for Supfile"),
            )
            .subcommand(
                SubCommand::with_name("show")
                    .about("asdf")
                    .arg(Arg::with_name("DATE").takes_value(true).help("asdf")),
            )
            .subcommand(
                SubCommand::with_name("serve")
                    .about("launch web interface")
                    .arg(
                        Arg::with_name("port")
                            .short("p")
                            .long("port")
                            .takes_value(true),
                    ),
            )
            .get_matches()
    }

    fn load_config(&self) -> Result<Supfile, Box<dyn Error>> {
        let path = self
            .matches
            .value_of("config")
            .map(|p| Path::new(p).to_path_buf())
            .unwrap_or_else(Supfile::find);

        let bytes = std::fs::read_to_string(path.as_os_str())?;
        Ok(Supfile::from(&bytes)?)
    }

    fn run(&self) -> Result<(), Box<dyn Error>> {
        let cfg = self.load_config()?;
        let repo = Repository::new(cfg.repo.clone());

        match self.matches.subcommand() {
            ("show", Some(m)) => println!("TODO: show {:?}", m),
            ("serve", Some(m)) => println!("TODO: serve {:?}", m),
            _ => {
                println!("add update");

                run_add_update(&cfg)?;
            }
        };

        Ok(())
    }
}

fn run_add_update(_cfg: &Supfile) -> Result<(), Box<dyn Error>> {
    Ok(())
}

// TODO: Investiage this instead of Box<Error>
// https://github.com/rust-lang-nursery/failure
fn main() {
    let app = SupApp::new();
    match app.run() {
        Err(e) => println!("oh no! {:?}", e),
        Ok(_) => (),
    };
}
<|endoftext|>


<commit_before>//! Utility functions for working with identifiers.

use heck::{CamelCase, SnakeCase};

/// Converts a `camelCase` or `SCREAMING_SNAKE_CASE` identifier to a `lower_snake` case Rust field
/// identifier.
pub fn to_snake(s: &str) -> String {
    let mut ident = s.to_snake_case();

    // Use a raw identifier if the identifier matches a Rust keyword:
    // https://doc.rust-lang.org/reference/keywords.html.
    match ident.as_str() {
        // 2015 strict keywords.
        | "as" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern" | "false"
        | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut"
        | "pub" | "ref" | "return" | "self" | "static" | "struct" | "super" | "trait" | "true"
        | "type" | "unsafe" | "use" | "where" | "while"
        // 2018 strict keywords.
        | "dyn"
        // 2015 reserved keywords.
        | "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" | "priv" | "typeof"
        | "unsized" | "virtual" | "yield"
        // 2018 reserved keywords.
        | "async" | "await" | "try" => ident.insert_str(0, "r#"),
        _ => (),
    }
    ident
}

/// Converts a `snake_case` identifier to an `UpperCamel` case Rust type identifier.
pub fn to_upper_camel(s: &str) -> String {
    let mut ident = s.to_camel_case();

    // Use a raw identifier if the identifier matches a Rust keyword:
    // https://doc.rust-lang.org/reference/keywords.html.
    if ident == "Self" {
        ident.insert_str(0, "r#");
    }
    ident
}

/// Matches a 'matcher' against a fully qualified identifier.
pub fn match_ident(matcher: &str, msg: &str, field: Option<&str>) -> bool {
    assert_eq!(b'.', msg.as_bytes()[0]);

    if matcher.is_empty() {
        return false;
    } else if matcher == "." {
        return true;
    }

    let match_paths = matcher.split('.').collect::<Vec<_>>();
    let field_paths = {
        let mut paths = msg.split('.').collect::<Vec<_>>();
        if let Some(field) = field {
            paths.push(field);
        }
        paths
    };

    if &matcher[..1] == "." {
        // Prefix match.
        if match_paths.len() > field_paths.len() {
            false
        } else {
            match_paths[..] == field_paths[..match_paths.len()]
        }
    // Suffix match.
    } else if match_paths.len() > field_paths.len() {
        false
    } else {
        match_paths[..] == field_paths[field_paths.len() - match_paths.len()..]
    }
}

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

    #[test]
    fn test_to_snake() {
        assert_eq!("foo_bar", &to_snake("FooBar"));
        assert_eq!("foo_bar_baz", &to_snake("FooBarBAZ"));
        assert_eq!("foo_bar_baz", &to_snake("FooBarBAZ"));
        assert_eq!("xml_http_request", &to_snake("XMLHttpRequest"));
        assert_eq!("r#while", &to_snake("While"));
        assert_eq!("fuzz_buster", &to_snake("FUZZ_BUSTER"));
        assert_eq!("foo_bar_baz", &to_snake("foo_bar_baz"));
        assert_eq!("fuzz_buster", &to_snake("FUZZ_buster"));
        assert_eq!("fuzz", &to_snake("_FUZZ"));
        assert_eq!("fuzz", &to_snake("_fuzz"));
        assert_eq!("fuzz", &to_snake("_Fuzz"));
        assert_eq!("fuzz", &to_snake("FUZZ_"));
        assert_eq!("fuzz", &to_snake("fuzz_"));
        assert_eq!("fuzz", &to_snake("Fuzz_"));
        assert_eq!("fuz_z", &to_snake("FuzZ_"));

        // From test_messages_proto3.proto.
        assert_eq!("fieldname1", &to_snake("fieldname1"));
        assert_eq!("field_name2", &to_snake("field_name2"));
        assert_eq!("field_name3", &to_snake("_field_name3"));
        assert_eq!("field_name4", &to_snake("field__name4_"));
        assert_eq!("field0name5", &to_snake("field0name5"));
        assert_eq!("field_0_name6", &to_snake("field_0_name6"));
        assert_eq!("field_name7", &to_snake("fieldName7"));
        assert_eq!("field_name8", &to_snake("FieldName8"));
        assert_eq!("field_name9", &to_snake("field_Name9"));
        assert_eq!("field_name10", &to_snake("Field_Name10"));

        // TODO(withoutboats/heck#3)
        //assert_eq!("field_name11", &to_snake("FIELD_NAME11"));
        assert_eq!("field_name12", &to_snake("FIELD_name12"));
        assert_eq!("field_name13", &to_snake("__field_name13"));
        assert_eq!("field_name14", &to_snake("__Field_name14"));
        assert_eq!("field_name15", &to_snake("field__name15"));
        assert_eq!("field_name16", &to_snake("field__Name16"));
        assert_eq!("field_name17", &to_snake("field_name17__"));
        assert_eq!("field_name18", &to_snake("Field_name18__"));
    }

    #[test]
    fn test_to_upper_camel() {
        assert_eq!("", &to_upper_camel(""));
        assert_eq!("F", &to_upper_camel("F"));
        assert_eq!("Foo", &to_upper_camel("FOO"));
        assert_eq!("FooBar", &to_upper_camel("FOO_BAR"));
        assert_eq!("FooBar", &to_upper_camel("_FOO_BAR"));
        assert_eq!("FooBar", &to_upper_camel("FOO_BAR_"));
        assert_eq!("FooBar", &to_upper_camel("_FOO_BAR_"));
        assert_eq!("FuzzBuster", &to_upper_camel("fuzzBuster"));
        assert_eq!("FuzzBuster", &to_upper_camel("FuzzBuster"));
        assert_eq!("r#Self", &to_upper_camel("self"));
    }

    #[test]
    fn test_match_ident() {
        // Prefix matches
        assert!(match_ident(".", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(".foo", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(".foo.bar", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(".foo.bar.Baz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(
            ".foo.bar.Baz.buzz",
            ".foo.bar.Baz",
            Some("buzz")
        ));

        assert!(!match_ident(".fo", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident(".foo.", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident(".buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident(".Baz.buzz", ".foo.bar.Baz", Some("buzz")));

        // Suffix matches
        assert!(match_ident("buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident("Baz.buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident("bar.Baz.buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(
            "foo.bar.Baz.buzz",
            ".foo.bar.Baz",
            Some("buzz")
        ));

        assert!(!match_ident("buz", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident("uz", ".foo.bar.Baz", Some("buzz")));

        // Type names
        assert!(match_ident("Baz", ".foo.bar.Baz", None));
        assert!(match_ident(".", ".foo.bar.Baz", None));
        assert!(match_ident(".foo.bar", ".foo.bar.Baz", None));
        assert!(match_ident(".foo.bar.Baz", ".foo.bar.Baz", None));
        assert!(!match_ident(".fo", ".foo.bar.Baz", None));
        assert!(!match_ident(".buzz.Baz", ".foo.bar.Baz", None));
    }
}
<commit_msg>Fix for unsupported keyword raw identifiers (#180)<commit_after>//! Utility functions for working with identifiers.

use heck::{CamelCase, SnakeCase};

/// Converts a `camelCase` or `SCREAMING_SNAKE_CASE` identifier to a `lower_snake` case Rust field
/// identifier.
pub fn to_snake(s: &str) -> String {
    let mut ident = s.to_snake_case();

    // Use a raw identifier if the identifier matches a Rust keyword:
    // https://doc.rust-lang.org/reference/keywords.html.
    match ident.as_str() {
        // 2015 strict keywords.
        | "as" | "break" | "const" | "continue" | "else" | "enum" | "false"
        | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut"
        | "pub" | "ref" | "return" | "static" | "struct" | "trait" | "true"
        | "type" | "unsafe" | "use" | "where" | "while"
        // 2018 strict keywords.
        | "dyn"
        // 2015 reserved keywords.
        | "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" | "priv" | "typeof"
        | "unsized" | "virtual" | "yield"
        // 2018 reserved keywords.
        | "async" | "await" | "try" => ident.insert_str(0, "r#"),
        // the following keywords are not supported as raw identifiers and are therefore suffixed with an underscore.
        "self" | "super" | "extern" | "crate" => ident += "_",
        _ => (),
    }
    ident
}

/// Converts a `snake_case` identifier to an `UpperCamel` case Rust type identifier.
pub fn to_upper_camel(s: &str) -> String {
    let mut ident = s.to_camel_case();

    // Suffix an underscore for the `Self` Rust keyword as it is not allowed as raw identifier.
    if ident == "Self" {
        ident += "_";
    }
    ident
}

/// Matches a 'matcher' against a fully qualified identifier.
pub fn match_ident(matcher: &str, msg: &str, field: Option<&str>) -> bool {
    assert_eq!(b'.', msg.as_bytes()[0]);

    if matcher.is_empty() {
        return false;
    } else if matcher == "." {
        return true;
    }

    let match_paths = matcher.split('.').collect::<Vec<_>>();
    let field_paths = {
        let mut paths = msg.split('.').collect::<Vec<_>>();
        if let Some(field) = field {
            paths.push(field);
        }
        paths
    };

    if &matcher[..1] == "." {
        // Prefix match.
        if match_paths.len() > field_paths.len() {
            false
        } else {
            match_paths[..] == field_paths[..match_paths.len()]
        }
    // Suffix match.
    } else if match_paths.len() > field_paths.len() {
        false
    } else {
        match_paths[..] == field_paths[field_paths.len() - match_paths.len()..]
    }
}

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

    #[test]
    fn test_to_snake() {
        assert_eq!("foo_bar", &to_snake("FooBar"));
        assert_eq!("foo_bar_baz", &to_snake("FooBarBAZ"));
        assert_eq!("foo_bar_baz", &to_snake("FooBarBAZ"));
        assert_eq!("xml_http_request", &to_snake("XMLHttpRequest"));
        assert_eq!("r#while", &to_snake("While"));
        assert_eq!("fuzz_buster", &to_snake("FUZZ_BUSTER"));
        assert_eq!("foo_bar_baz", &to_snake("foo_bar_baz"));
        assert_eq!("fuzz_buster", &to_snake("FUZZ_buster"));
        assert_eq!("fuzz", &to_snake("_FUZZ"));
        assert_eq!("fuzz", &to_snake("_fuzz"));
        assert_eq!("fuzz", &to_snake("_Fuzz"));
        assert_eq!("fuzz", &to_snake("FUZZ_"));
        assert_eq!("fuzz", &to_snake("fuzz_"));
        assert_eq!("fuzz", &to_snake("Fuzz_"));
        assert_eq!("fuz_z", &to_snake("FuzZ_"));

        // From test_messages_proto3.proto.
        assert_eq!("fieldname1", &to_snake("fieldname1"));
        assert_eq!("field_name2", &to_snake("field_name2"));
        assert_eq!("field_name3", &to_snake("_field_name3"));
        assert_eq!("field_name4", &to_snake("field__name4_"));
        assert_eq!("field0name5", &to_snake("field0name5"));
        assert_eq!("field_0_name6", &to_snake("field_0_name6"));
        assert_eq!("field_name7", &to_snake("fieldName7"));
        assert_eq!("field_name8", &to_snake("FieldName8"));
        assert_eq!("field_name9", &to_snake("field_Name9"));
        assert_eq!("field_name10", &to_snake("Field_Name10"));

        // TODO(withoutboats/heck#3)
        //assert_eq!("field_name11", &to_snake("FIELD_NAME11"));
        assert_eq!("field_name12", &to_snake("FIELD_name12"));
        assert_eq!("field_name13", &to_snake("__field_name13"));
        assert_eq!("field_name14", &to_snake("__Field_name14"));
        assert_eq!("field_name15", &to_snake("field__name15"));
        assert_eq!("field_name16", &to_snake("field__Name16"));
        assert_eq!("field_name17", &to_snake("field_name17__"));
        assert_eq!("field_name18", &to_snake("Field_name18__"));
    }

    #[test]
    fn test_to_snake_raw_keyword() {
        assert_eq!("r#as", &to_snake("as"));
        assert_eq!("r#break", &to_snake("break"));
        assert_eq!("r#const", &to_snake("const"));
        assert_eq!("r#continue", &to_snake("continue"));
        assert_eq!("r#else", &to_snake("else"));
        assert_eq!("r#enum", &to_snake("enum"));
        assert_eq!("r#false", &to_snake("false"));
        assert_eq!("r#fn", &to_snake("fn"));
        assert_eq!("r#for", &to_snake("for"));
        assert_eq!("r#if", &to_snake("if"));
        assert_eq!("r#impl", &to_snake("impl"));
        assert_eq!("r#in", &to_snake("in"));
        assert_eq!("r#let", &to_snake("let"));
        assert_eq!("r#loop", &to_snake("loop"));
        assert_eq!("r#match", &to_snake("match"));
        assert_eq!("r#mod", &to_snake("mod"));
        assert_eq!("r#move", &to_snake("move"));
        assert_eq!("r#mut", &to_snake("mut"));
        assert_eq!("r#pub", &to_snake("pub"));
        assert_eq!("r#ref", &to_snake("ref"));
        assert_eq!("r#return", &to_snake("return"));
        assert_eq!("r#static", &to_snake("static"));
        assert_eq!("r#struct", &to_snake("struct"));
        assert_eq!("r#trait", &to_snake("trait"));
        assert_eq!("r#true", &to_snake("true"));
        assert_eq!("r#type", &to_snake("type"));
        assert_eq!("r#unsafe", &to_snake("unsafe"));
        assert_eq!("r#use", &to_snake("use"));
        assert_eq!("r#where", &to_snake("where"));
        assert_eq!("r#while", &to_snake("while"));
        assert_eq!("r#dyn", &to_snake("dyn"));
        assert_eq!("r#abstract", &to_snake("abstract"));
        assert_eq!("r#become", &to_snake("become"));
        assert_eq!("r#box", &to_snake("box"));
        assert_eq!("r#do", &to_snake("do"));
        assert_eq!("r#final", &to_snake("final"));
        assert_eq!("r#final", &to_snake("final"));
        assert_eq!("r#macro", &to_snake("macro"));
        assert_eq!("r#override", &to_snake("override"));
        assert_eq!("r#priv", &to_snake("priv"));
        assert_eq!("r#typeof", &to_snake("typeof"));
        assert_eq!("r#unsized", &to_snake("unsized"));
        assert_eq!("r#virtual", &to_snake("virtual"));
        assert_eq!("r#yield", &to_snake("yield"));
        assert_eq!("r#async", &to_snake("async"));
        assert_eq!("r#await", &to_snake("await"));
        assert_eq!("r#try", &to_snake("try"));
    }

    #[test]
    fn test_to_snake_non_raw_keyword() {
        assert_eq!("self_", &to_snake("self"));
        assert_eq!("super_", &to_snake("super"));
        assert_eq!("extern_", &to_snake("extern"));
        assert_eq!("crate_", &to_snake("crate"));
    }

    #[test]
    fn test_to_upper_camel() {
        assert_eq!("", &to_upper_camel(""));
        assert_eq!("F", &to_upper_camel("F"));
        assert_eq!("Foo", &to_upper_camel("FOO"));
        assert_eq!("FooBar", &to_upper_camel("FOO_BAR"));
        assert_eq!("FooBar", &to_upper_camel("_FOO_BAR"));
        assert_eq!("FooBar", &to_upper_camel("FOO_BAR_"));
        assert_eq!("FooBar", &to_upper_camel("_FOO_BAR_"));
        assert_eq!("FuzzBuster", &to_upper_camel("fuzzBuster"));
        assert_eq!("FuzzBuster", &to_upper_camel("FuzzBuster"));
        assert_eq!("Self_", &to_upper_camel("self"));
    }

    #[test]
    fn test_match_ident() {
        // Prefix matches
        assert!(match_ident(".", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(".foo", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(".foo.bar", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(".foo.bar.Baz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(
            ".foo.bar.Baz.buzz",
            ".foo.bar.Baz",
            Some("buzz")
        ));

        assert!(!match_ident(".fo", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident(".foo.", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident(".buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident(".Baz.buzz", ".foo.bar.Baz", Some("buzz")));

        // Suffix matches
        assert!(match_ident("buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident("Baz.buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident("bar.Baz.buzz", ".foo.bar.Baz", Some("buzz")));
        assert!(match_ident(
            "foo.bar.Baz.buzz",
            ".foo.bar.Baz",
            Some("buzz")
        ));

        assert!(!match_ident("buz", ".foo.bar.Baz", Some("buzz")));
        assert!(!match_ident("uz", ".foo.bar.Baz", Some("buzz")));

        // Type names
        assert!(match_ident("Baz", ".foo.bar.Baz", None));
        assert!(match_ident(".", ".foo.bar.Baz", None));
        assert!(match_ident(".foo.bar", ".foo.bar.Baz", None));
        assert!(match_ident(".foo.bar.Baz", ".foo.bar.Baz", None));
        assert!(!match_ident(".fo", ".foo.bar.Baz", None));
        assert!(!match_ident(".buzz.Baz", ".foo.bar.Baz", None));
    }
}
<|endoftext|>


<commit_before>
struct Decision {
    x: usize,
    r: usize,
}

impl Decision {
    pub fn new(x: usize, r: usize) -> Decision {
        Decision {
            x: x,
            r: r,
        }
    }

    pub fn axis_x(&self) -> usize {
        self.x
    }

    pub fn child_x(&self) -> usize {
        match self.r {
            0 => self.x,
            1 => self.x + 1,
            2 => self.x,
            3 => self.x - 1,
            _ => {
                assert!(false, "unexpected r={}", self.r);
                unreachable!()
            }
        }
    }

    pub fn rot(&self) -> usize {
        self.r
    }

    pub fn valid(&self) -> bool {
        if self.x <= 0 || 6 < self.x || self.r <= 0 || 4 <= self.r {
            return false;
        }
        if (self.x == 1 && self.r == 3) || (self.x == 6 && self.r == 1) {
            return false;
        }

        true
    }
}
<commit_msg>Add Decision test<commit_after>pub struct Decision {
    x: usize,
    r: usize,
}

impl Decision {
    pub fn new(x: usize, r: usize) -> Decision {
        Decision {
            x: x,
            r: r,
        }
    }

    pub fn axis_x(&self) -> usize {
        self.x
    }

    pub fn child_x(&self) -> usize {
        match self.r {
            0 => self.x,
            1 => self.x + 1,
            2 => self.x,
            3 => self.x - 1,
            _ => {
                assert!(false, "unexpected r={}", self.r);
                unreachable!()
            }
        }
    }

    pub fn rot(&self) -> usize {
        self.r
    }

    pub fn valid(&self) -> bool {
        if self.x <= 0 || 6 < self.x || 4 <= self.r {
            return false;
        }
        if (self.x == 1 && self.r == 3) || (self.x == 6 && self.r == 1) {
            return false;
        }

        true
    }
}

#[cfg(test)]
mod tests {
    use decision::Decision;

    #[test]
    fn test_xr() {
        let d10 = Decision::new(3, 0);
        let d11 = Decision::new(3, 1);
        let d12 = Decision::new(3, 2);
        let d13 = Decision::new(3, 3);

        assert_eq!(3, d10.axis_x());
        assert_eq!(3, d10.child_x());
        assert_eq!(0, d10.rot());

        assert_eq!(3, d11.axis_x());
        assert_eq!(4, d11.child_x());
        assert_eq!(1, d11.rot());

        assert_eq!(3, d12.axis_x());
        assert_eq!(3, d12.child_x());
        assert_eq!(2, d12.rot());

        assert_eq!(3, d13.axis_x());
        assert_eq!(2, d13.child_x());
        assert_eq!(3, d13.rot());
    }

    #[test]
    fn test_valid() {
        assert!(Decision::new(1, 0).valid());
        assert!(Decision::new(1, 1).valid());
        assert!(Decision::new(1, 2).valid());
        assert!(!Decision::new(1, 3).valid());
        assert!(!Decision::new(1, 4).valid());

        assert!(Decision::new(2, 0).valid());
        assert!(Decision::new(2, 1).valid());
        assert!(Decision::new(2, 2).valid());
        assert!(Decision::new(2, 3).valid());
        assert!(!Decision::new(2, 4).valid());

        assert!(Decision::new(6, 0).valid());
        assert!(!Decision::new(6, 1).valid());
        assert!(Decision::new(6, 2).valid());
        assert!(Decision::new(6, 3).valid());
        assert!(!Decision::new(6, 4).valid());
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate env_logger;
extern crate log;
extern crate serde_json;
extern crate webdriver;

use env_logger::LogBuilder;
use log::LogLevelFilter;
use std::env;
use webdriver::DriverSession;
use webdriver::firefox::GeckoDriver;
use webdriver::messages::ExecuteCmd;

#[test]
fn test_file() {
    init_logging();

    let gecko = GeckoDriver::new().unwrap();
    let mut sess = DriverSession::new(gecko).unwrap();

    // `cargo test` starts with current directory set to the crate root.
    let crate_root =
        std::env::current_dir().unwrap()
        .to_str().unwrap().to_owned();
    let test_url = format!("file://{crate}/tests/integration_test.html", crate = crate_root);

    sess.go(&test_url).unwrap();
    let url = sess.get_current_url().unwrap();
    assert_eq!(url, test_url);

    let title = sess.get_title().unwrap();
    assert_eq!(title, "Test page title");

    sess.back().unwrap();
    sess.forward().unwrap();
    sess.refresh().unwrap();
    sess.get_page_source().unwrap();

    sess.get_cookies().unwrap();
    sess.get_window_handle().unwrap();
    {
        let handles = sess.get_window_handles().unwrap();
        assert_eq!(handles.len(), 1);
    }

    {
        // Test execute return
        let exec_json = sess.execute(ExecuteCmd {
            script: "return 2 + 2;".to_owned(),
            args: vec![],
        }).unwrap();
        let exec_int = serde_json::from_value::<i64>(exec_json).unwrap();
        assert_eq!(exec_int, 4);
    }

    {
        // Test execute handling an exception
        let exec_res = sess.execute(ExecuteCmd {
            script: "throw 'SomeException';".to_owned(),
            args: vec![],
        });
        assert!(exec_res.is_err());
        let err = exec_res.err().unwrap();
        let err = match err {
            webdriver::Error::WebDriverError(e) => e,
            _ => panic!("Unexpected error variant: {:#?}", err),
        };
        assert_eq!(err.error, "javascript error");
        assert_eq!(err.message, "SomeException");
    }

    {
        // Test execute async
        let exec_json = sess.execute_async(ExecuteCmd {
            script: "let resolve = arguments[0];\n\
                     setTimeout(() => resolve(4), 1000);".to_owned(),
            args: vec![],
        }).unwrap();
        let exec_int = serde_json::from_value::<i64>(exec_json).unwrap();
        assert_eq!(exec_int, 4);
    }

    sess.close_window().unwrap();
}

fn init_logging() {
    let mut builder = LogBuilder::new();
    builder.filter(None, LogLevelFilter::Info);

    if let Ok(ev) = env::var("RUST_LOG") {
       builder.parse(&ev);
    }

    builder.init().unwrap();
}

mod youtube_integration_test {
    use webdriver::firefox::GeckoDriver;
    use webdriver::messages::LocationStrategy;
    use webdriver::DriverSession;

    #[test]
    #[ignore]
    fn test() {
        let gecko = GeckoDriver::new().unwrap();
        let mut sess = DriverSession::new(gecko).unwrap();
        sess.go("https://www.youtube.com/watch?v=dQw4w9WgXcQ").unwrap();
        sess.get_current_url().unwrap();
        sess.back().unwrap();
        sess.forward().unwrap();
        sess.refresh().unwrap();
        sess.get_page_source().unwrap();

        {
            let el = sess.find_element("a", LocationStrategy::Css).unwrap();
            el.attribute("href").unwrap();
            el.css_value("color").unwrap();
            el.text().unwrap();
            assert_eq!(el.name().unwrap(), "a");

            let imgs = sess.find_elements("img", LocationStrategy::Css).unwrap();
            for img in &imgs {
                println!("{}", img.attribute("src").unwrap());
            }

            sess.get_cookies().unwrap();
            sess.get_title().unwrap();
            sess.get_window_handle().unwrap();
            let handles = sess.get_window_handles().unwrap();
            assert_eq!(handles.len(), 1);
        }
        sess.close_window().unwrap();
    }
}
<commit_msg>Integration test includes firefox path (not just command name); new geckodriver seems to require it.<commit_after>#![deny(warnings)]

extern crate env_logger;
extern crate log;
extern crate serde_json;
extern crate webdriver;

use env_logger::LogBuilder;
use log::LogLevelFilter;
use std::env;
use webdriver::DriverSession;
use webdriver::firefox::GeckoDriver;
use webdriver::messages::ExecuteCmd;

#[test]
fn test_file() {
    init_logging();

    // TODO: Perhaps calculate path from PATH environment variable.
    let gecko = GeckoDriver::with_binary("/usr/bin/firefox").unwrap();
    let mut sess = DriverSession::new(gecko).unwrap();

    // `cargo test` starts with current directory set to the crate root.
    let crate_root =
        std::env::current_dir().unwrap()
        .to_str().unwrap().to_owned();
    let test_url = format!("file://{crate}/tests/integration_test.html", crate = crate_root);

    sess.go(&test_url).unwrap();
    let url = sess.get_current_url().unwrap();
    assert_eq!(url, test_url);

    let title = sess.get_title().unwrap();
    assert_eq!(title, "Test page title");

    sess.back().unwrap();
    sess.forward().unwrap();
    sess.refresh().unwrap();
    sess.get_page_source().unwrap();

    sess.get_cookies().unwrap();
    sess.get_window_handle().unwrap();
    {
        let handles = sess.get_window_handles().unwrap();
        assert_eq!(handles.len(), 1);
    }

    {
        // Test execute return
        let exec_json = sess.execute(ExecuteCmd {
            script: "return 2 + 2;".to_owned(),
            args: vec![],
        }).unwrap();
        let exec_int = serde_json::from_value::<i64>(exec_json).unwrap();
        assert_eq!(exec_int, 4);
    }

    {
        // Test execute handling an exception
        let exec_res = sess.execute(ExecuteCmd {
            script: "throw 'SomeException';".to_owned(),
            args: vec![],
        });
        assert!(exec_res.is_err());
        let err = exec_res.err().unwrap();
        let err = match err {
            webdriver::Error::WebDriverError(e) => e,
            _ => panic!("Unexpected error variant: {:#?}", err),
        };
        assert_eq!(err.error, "javascript error");
        assert_eq!(err.message, "SomeException");
    }

    {
        // Test execute async
        let exec_json = sess.execute_async(ExecuteCmd {
            script: "let resolve = arguments[0];\n\
                     setTimeout(() => resolve(4), 1000);".to_owned(),
            args: vec![],
        }).unwrap();
        let exec_int = serde_json::from_value::<i64>(exec_json).unwrap();
        assert_eq!(exec_int, 4);
    }

    sess.close_window().unwrap();
}

fn init_logging() {
    let mut builder = LogBuilder::new();
    builder.filter(None, LogLevelFilter::Info);

    if let Ok(ev) = env::var("RUST_LOG") {
       builder.parse(&ev);
    }

    builder.init().unwrap();
}

mod youtube_integration_test {
    use webdriver::firefox::GeckoDriver;
    use webdriver::messages::LocationStrategy;
    use webdriver::DriverSession;

    #[test]
    #[ignore]
    fn test() {
        let gecko = GeckoDriver::new().unwrap();
        let mut sess = DriverSession::new(gecko).unwrap();
        sess.go("https://www.youtube.com/watch?v=dQw4w9WgXcQ").unwrap();
        sess.get_current_url().unwrap();
        sess.back().unwrap();
        sess.forward().unwrap();
        sess.refresh().unwrap();
        sess.get_page_source().unwrap();

        {
            let el = sess.find_element("a", LocationStrategy::Css).unwrap();
            el.attribute("href").unwrap();
            el.css_value("color").unwrap();
            el.text().unwrap();
            assert_eq!(el.name().unwrap(), "a");

            let imgs = sess.find_elements("img", LocationStrategy::Css).unwrap();
            for img in &imgs {
                println!("{}", img.attribute("src").unwrap());
            }

            sess.get_cookies().unwrap();
            sess.get_title().unwrap();
            sess.get_window_handle().unwrap();
            let handles = sess.get_window_handles().unwrap();
            assert_eq!(handles.len(), 1);
        }
        sess.close_window().unwrap();
    }
}
<|endoftext|>


<commit_before>#![allow(unused_must_use)]

extern crate regex;
extern crate time;

pub mod fs;
pub mod cmd;
pub mod log;

use std::env;
use std::io;

fn main() {
  let version: u8 = 13;  // Version that gets printed into the PBO filename

  // List of map presets
  let mut fp_main = include!("../maps/fp_main.rs");
  let mut fp_ww2 = include!("../maps/fp_ww2.rs");
  let mut worlds: Vec<&str> = Vec::new();

  // Determine what preset
  let mut buffer = String::new();
  println!("Please Enter your Preset: \n1 = Normal FP Modpack Maps\n2 = WW2 Maps\n3 = FP Modpack + WW2 Maps");
  io::stdin().read_line(&mut buffer).unwrap();
  println!("A: {}", &buffer);

  match buffer.as_ref() {
    "1" =>  worlds.append(&mut fp_main),
    "2" =>  worlds.append(&mut fp_ww2),
    "3" =>  {
              worlds.append(&mut fp_main);
              worlds.append(&mut fp_ww2);
            },
    _   =>  {},
  }

  let cwd: String = get_cwd();
  for map in worlds {
    compile(cwd.as_ref(), version, map);
  }
}

/// Get Current working directory, where the binary was executed.
fn get_cwd() -> String {
  let pathbuf = env::current_dir().unwrap();
  let path: &str = pathbuf.to_str().unwrap();
  format!("{}\\", path.to_string())
}

/// Function that Generates the new Filename with version info and map postfix
/// and call to PBOConsole for the Basic Medical Mission.
fn compile(path: &str,version: u8, map: &str) {
  let dirpath: String = format!("{}FP_ComfyDowntime.VR", path);
  let newpath: String = format!("{}FP_ComfyDowntime_{}.{}.pbo", path, version, map);
  cmd::native::run("makepbo", &["-N", "-P", dirpath.as_ref(), newpath.as_ref()]);
}
<commit_msg>Map Selection is now functional.<commit_after>#![allow(unused_must_use)]

extern crate regex;
extern crate time;

pub mod fs;
pub mod cmd;
pub mod log;

use std::env;
use std::io;

fn main() {
  let version: u8 = 13;  // Version that gets printed into the PBO filename

  // List of map presets
  let mut fp_main = include!("../maps/fp_main.rs");
  let mut fp_ww2 = include!("../maps/fp_ww2.rs");
  let mut worlds: Vec<&str> = Vec::new();

  // Determine what preset
  let mut buffer = String::new();
  {
    println!("Please Enter your Preset: \n1 = Normal FP Modpack Maps\n2 = WW2 Maps\n3 = FP Modpack + WW2 Maps");
    io::stdin().read_line(&mut buffer).unwrap();
    buffer = buffer.replace("\r", "");
    buffer = buffer.replace("\n", "");

    match buffer.as_ref() {
      "1" =>  {worlds.append(&mut fp_main)},
      "2" =>  {worlds.append(&mut fp_ww2)},
      "3" =>  {
                worlds.append(&mut fp_main);
                worlds.append(&mut fp_ww2);
              },
      _   =>  {},
    }
  }

  let cwd: String = get_cwd();
  for map in worlds {
    compile(cwd.as_ref(), version, map);
  }
}

/// Get Current working directory, where the binary was executed.
fn get_cwd() -> String {
  let pathbuf = env::current_dir().unwrap();
  let path: &str = pathbuf.to_str().unwrap();
  format!("{}\\", path.to_string())
}

/// Function that Generates the new Filename with version info and map postfix
/// and call to PBOConsole for the Basic Medical Mission.
fn compile(path: &str,version: u8, map: &str) {
  let dirpath: String = format!("{}FP_ComfyDowntime.VR", path);
  let newpath: String = format!("{}FP_ComfyDowntime_{}.{}.pbo", path, version, map);
  cmd::native::run("makepbo", &["-N", "-P", dirpath.as_ref(), newpath.as_ref()]);
}
<|endoftext|>