<commit_before>// Reference: http://www.redblobgames.com/pathfinding/a-star/introduction.html

use std::collections::BinaryHeap;
use std::collections::HashMap;
use std::cmp::Ordering;
use std::cmp;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Position {
    pub x: usize,
    pub y: usize
}

// Used to maintain a priority queue of positions to explore.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct PositionPriority {
    minimum_cost: usize,
    position: Position,
}

// The priority queue depends on `Ord`.
impl Ord for PositionPriority {
    // Reverse the comparison so the position with the lowest cost is
    // explored first.
    fn cmp(&self, other: &PositionPriority) -> Ordering {
        other.minimum_cost.cmp(&self.minimum_cost)
    }
}

impl PartialOrd for PositionPriority {
    fn partial_cmp(&self, other: &PositionPriority) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[inline]
fn min_distance_heuristic(p1: Position, p2: Position) -> usize {
    let x_diff = match p1.x > p2.x {
        true => p1.x - p2.x,
        false => p2.x - p1.x,
    };
    let y_diff = match p1.y > p2.y {
        true => p1.y - p2.y,
        false => p2.y - p1.y,
    };

    x_diff + y_diff
}

#[inline]
fn neighbours(grid: &Vec<Vec<char>>, pos: Position) -> Vec<Position> {
    let mut v = Vec::new();

    if pos.x > 0 && grid[pos.y][pos.x-1] == ' ' {
        v.push(Position{x: pos.x-1, y: pos.y});
    }
    if pos.x + 1 < grid[pos.y].len() && grid[pos.y][pos.x+1] == ' ' {
        v.push(Position{x: pos.x+1, y: pos.y});
    }

    if pos.y > 0 && pos.x < grid[pos.y-1].len() && grid[pos.y-1][pos.x] == ' ' {
        v.push(Position{x: pos.x, y: pos.y-1});
    }
    if pos.y + 1 < grid.len() &&
       pos.x < grid[pos.y+1].len() &&
       grid[pos.y+1][pos.x] == ' ' {
        v.push(Position{x: pos.x, y: pos.y+1});
    }
    v
}

/// Generate the optimal path from start to goal given a mapping from positions
/// to the position that lead to that position.
#[inline]
fn reconstruct(came_from: &HashMap<Position, Position>,
               start: Position,
               goal: Position) -> Vec<Position> {
    let mut current = goal;
    let mut path = vec![current];
    while current != start {
        current = came_from[&current];
        path.push(current);
    }
    path.reverse();
    path
}

/// Find the optimal path from a starting position to a goal position. Return
/// None if no path is possible. The input grid is a 2-dimensional grid of
/// characters. Any character other than a space is considered an impassable
/// obstacle.
///
/// # Examples
///
/// ```
/// use bestpath::{find_path, Position};
///
/// let grid = vec![vec![' ', '*', '*', ' '],
///                 vec![' ', ' ', ' ', '*'],
///                 vec!['*', '*', ' ', ' '],
///                 vec![' ', '*', ' ', ' ']];
/// assert_eq!(Some(vec![Position{x: 0, y: 0},
///                      Position{x: 0, y: 1},
///                      Position{x: 1, y: 1},
///                      Position{x: 2, y: 1},
///                      Position{x: 2, y: 2}]),
///            find_path(&grid, Position{x: 0, y: 0}, Position{x: 2, y: 2}));
/// assert_eq!(None,
///            find_path(&grid, Position{x: 0, y: 0}, Position{x: 3, y: 0}));
/// ```
#[inline]
pub fn find_path(grid: &Vec<Vec<char>>, start: Position, goal: Position)
        -> Option<Vec<Position>>  {
    let mut frontier = BinaryHeap::new();
    frontier.push(PositionPriority { minimum_cost: 0, position: start });

    let mut came_from = HashMap::new();
    let mut cost_so_far = HashMap::new();
    cost_so_far.insert(start, 0);

    while let Some(PositionPriority { minimum_cost, position }) = frontier.pop() {
        if position == goal {
            return Some(reconstruct(&came_from, start, goal));
        }
        let cost = cost_so_far[&position];

        for next_position in neighbours(grid, position) {
            let new_cost = cost + 1;
            if let Some(existing_cost) = cost_so_far.get(&next_position) {
                if new_cost >= *existing_cost {
                    continue;
                }
            }
            cost_so_far.insert(next_position, new_cost);
            frontier.push(PositionPriority {
                minimum_cost: new_cost +
                    min_distance_heuristic(goal, next_position),
                position: next_position });
            came_from.insert(next_position, position);
        }
    }
    None
}

/// Generate a copy of the given grid with the path filled in. The starting
/// position is shown with a '@' , the goal postiion with a 'X' and all other
/// points on the path with a 'o'.
///
/// # Examples
///
/// ```
/// use bestpath::{find_path, format_path_map, Position};
///
/// let grid = vec![vec![' ', ' ', ' ', ' '],
///                 vec![' ', '*', ' ', '*'],
///                 vec!['*', '*', ' ', ' '],
///                 vec![' ', '*', ' ', ' ']];
/// let path = find_path(&grid,
///                      Position{x: 0, y: 1},
///                      Position{x: 3, y: 2}).unwrap();
/// assert_eq!(vec![vec!['o', 'o', 'o', ' '],
///                 vec!['@', '*', 'o', '*'],
///                 vec!['*', '*', 'o', 'X'],
///                 vec![' ', '*', ' ', ' ']],
///            format_path_map(&grid, &path));
/// ```
#[inline]
pub fn format_path_map(grid: &Vec<Vec<char>>, path: &Vec<Position>)
        -> Vec<Vec<char>> {
    let mut m = grid.clone();
    let last_path = path.len() - 1;
    for (i, p) in path.iter().enumerate() {
        m[p.y][p.x] = match i {
            0 => '@',
            _ if i == last_path => 'X',
            _ => 'o',
        }
    }
    m
}

#[allow(dead_code)]
fn print_grid(grid: &Vec<Vec<char>>) {
    println!("");
    for row in grid {
        for ch in row {
            print!(" {} ", ch);
        }
        println!("");
    }
}

#[test]
fn no_path() {
    let map =
        vec![vec![' ', ' ', '█', ' ', ' ', ' ', ' '],
             vec!['█', '█', ' ', ' ', '█', '█', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█'],
             vec![' ', ' ', ' ', '█', ' ', '█', ' '],
             vec![' ', ' ', ' ', ' ', ' ', '█', ' '],
             vec!['█', '█', '█', '█', '█', '█', '█'],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map, Position { x: 0, y: 0}, Position { x: 6, y: 6});
    assert_eq!(None, path);
}

#[test]
fn multiple_complex_paths_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['█', '█', ' ', '█', '█', ' ', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█'],
             vec![' ', '█', ' ', '█', ' ', ' ', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█'],
             vec![' ', '█', '█', ' ', ' ', ' ', ' '],
             vec!['█', '█', ' ', ' ', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', '█', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 6, y: 8}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', 'o', 'o', 'o', 'o', 'o', ' '],
             vec!['█', '█', ' ', '█', '█', 'o', ' '],
             vec![' ', '█', ' ', '█', '█', 'o', '█'],
             vec![' ', '█', ' ', '█', ' ', 'o', ' '],
             vec![' ', '█', ' ', '█', '█', 'o', '█'],
             vec![' ', '█', '█', ' ', ' ', 'o', 'o'],
             vec!['█', '█', ' ', ' ', '█', '█', 'o'],
             vec![' ', ' ', ' ', ' ', '█', '█', 'o'],
             vec![' ', ' ', ' ', ' ', '█', ' ', 'X']],
        path_map);
}

#[test]
fn verticle_map() {
    let map =
        vec![vec![' '],
             vec![' '],
             vec![' '],
             vec![' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 0, y: 3}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@'],
             vec!['o'],
             vec!['o'],
             vec!['X']],
        path_map);
}

#[test]
fn horizontal_map() {
    let map =
        vec![vec![' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 3, y: 0}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', 'o', 'o', 'X']],
        path_map);
}

#[test]
fn straight_diagonal_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 6, y: 6}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(13, path.len())  // There are many equivalent paths.
}

#[test]
fn single_walled_path_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['█', '█', '█', '█', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', '█', '█', '█', '█', '█', '█'],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['█', '█', '█', '█', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 0, y: 6}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['█', '█', '█', '█', '█', '█', 'o'],
             vec!['o', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['o', '█', '█', '█', '█', '█', '█'],
             vec!['o', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['█', '█', '█', '█', '█', '█', 'o'],
             vec!['X', 'o', 'o', 'o', 'o', 'o', 'o']],
        path_map);
}

#[test]
fn spiral_wall_path_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', '█', '█', '█', '█', '█', '█', '█'],
             vec![' ', '█', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', '█', ' ', '█', '█', '█', '█', ' '],
             vec![' ', '█', ' ', '█', ' ', ' ', '█', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█', ' '],
             vec![' ', '█', ' ', ' ', ' ', ' ', '█', ' '],
             vec![' ', '█', '█', '█', '█', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 4, y: 4}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['o', '█', '█', '█', '█', '█', '█', '█'],
             vec!['o', '█', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['o', '█', 'o', '█', '█', '█', '█', 'o'],
             vec!['o', '█', 'o', '█', 'X', 'o', '█', 'o'],
             vec!['o', '█', 'o', '█', '█', 'o', '█', 'o'],
             vec!['o', '█', 'o', 'o', 'o', 'o', '█', 'o'],
             vec!['o', '█', '█', '█', '█', '█', '█', 'o'],
             vec!['o', 'o', 'o', 'o', 'o', 'o', 'o', 'o']],
        path_map);
}
<commit_msg>Make it possible to solve the "no_path" using diagonal movement.<commit_after>// Reference: http://www.redblobgames.com/pathfinding/a-star/introduction.html

use std::collections::BinaryHeap;
use std::collections::HashMap;
use std::cmp::Ordering;
use std::cmp;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Position {
    pub x: usize,
    pub y: usize
}

// Used to maintain a priority queue of positions to explore.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct PositionPriority {
    minimum_cost: usize,
    position: Position,
}

// The priority queue depends on `Ord`.
impl Ord for PositionPriority {
    // Reverse the comparison so the position with the lowest cost is
    // explored first.
    fn cmp(&self, other: &PositionPriority) -> Ordering {
        other.minimum_cost.cmp(&self.minimum_cost)
    }
}

impl PartialOrd for PositionPriority {
    fn partial_cmp(&self, other: &PositionPriority) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[inline]
fn min_distance_heuristic(p1: Position, p2: Position) -> usize {
    let x_diff = match p1.x > p2.x {
        true => p1.x - p2.x,
        false => p2.x - p1.x,
    };
    let y_diff = match p1.y > p2.y {
        true => p1.y - p2.y,
        false => p2.y - p1.y,
    };

    x_diff + y_diff
}

#[inline]
fn neighbours(grid: &Vec<Vec<char>>, pos: Position) -> Vec<Position> {
    let mut v = Vec::new();

    if pos.x > 0 && grid[pos.y][pos.x-1] == ' ' {
        v.push(Position{x: pos.x-1, y: pos.y});
    }
    if pos.x + 1 < grid[pos.y].len() && grid[pos.y][pos.x+1] == ' ' {
        v.push(Position{x: pos.x+1, y: pos.y});
    }

    if pos.y > 0 && pos.x < grid[pos.y-1].len() && grid[pos.y-1][pos.x] == ' ' {
        v.push(Position{x: pos.x, y: pos.y-1});
    }
    if pos.y + 1 < grid.len() &&
       pos.x < grid[pos.y+1].len() &&
       grid[pos.y+1][pos.x] == ' ' {
        v.push(Position{x: pos.x, y: pos.y+1});
    }
    v
}

/// Generate the optimal path from start to goal given a mapping from positions
/// to the position that lead to that position.
#[inline]
fn reconstruct(came_from: &HashMap<Position, Position>,
               start: Position,
               goal: Position) -> Vec<Position> {
    let mut current = goal;
    let mut path = vec![current];
    while current != start {
        current = came_from[&current];
        path.push(current);
    }
    path.reverse();
    path
}

/// Find the optimal path from a starting position to a goal position. Return
/// None if no path is possible. The input grid is a 2-dimensional grid of
/// characters. Any character other than a space is considered an impassable
/// obstacle.
///
/// # Examples
///
/// ```
/// use bestpath::{find_path, Position};
///
/// let grid = vec![vec![' ', '*', '*', ' '],
///                 vec![' ', ' ', ' ', '*'],
///                 vec!['*', '*', ' ', ' '],
///                 vec![' ', '*', ' ', ' ']];
/// assert_eq!(Some(vec![Position{x: 0, y: 0},
///                      Position{x: 0, y: 1},
///                      Position{x: 1, y: 1},
///                      Position{x: 2, y: 1},
///                      Position{x: 2, y: 2}]),
///            find_path(&grid, Position{x: 0, y: 0}, Position{x: 2, y: 2}));
/// assert_eq!(None,
///            find_path(&grid, Position{x: 0, y: 0}, Position{x: 3, y: 0}));
/// ```
#[inline]
pub fn find_path(grid: &Vec<Vec<char>>, start: Position, goal: Position)
        -> Option<Vec<Position>>  {
    let mut frontier = BinaryHeap::new();
    frontier.push(PositionPriority { minimum_cost: 0, position: start });

    let mut came_from = HashMap::new();
    let mut cost_so_far = HashMap::new();
    cost_so_far.insert(start, 0);

    while let Some(PositionPriority { minimum_cost, position }) = frontier.pop() {
        if position == goal {
            return Some(reconstruct(&came_from, start, goal));
        }
        let cost = cost_so_far[&position];

        for next_position in neighbours(grid, position) {
            let new_cost = cost + 1;
            if let Some(existing_cost) = cost_so_far.get(&next_position) {
                if new_cost >= *existing_cost {
                    continue;
                }
            }
            cost_so_far.insert(next_position, new_cost);
            frontier.push(PositionPriority {
                minimum_cost: new_cost +
                    min_distance_heuristic(goal, next_position),
                position: next_position });
            came_from.insert(next_position, position);
        }
    }
    None
}

/// Generate a copy of the given grid with the path filled in. The starting
/// position is shown with a '@' , the goal postiion with a 'X' and all other
/// points on the path with a 'o'.
///
/// # Examples
///
/// ```
/// use bestpath::{find_path, format_path_map, Position};
///
/// let grid = vec![vec![' ', ' ', ' ', ' '],
///                 vec![' ', '*', ' ', '*'],
///                 vec!['*', '*', ' ', ' '],
///                 vec![' ', '*', ' ', ' ']];
/// let path = find_path(&grid,
///                      Position{x: 0, y: 1},
///                      Position{x: 3, y: 2}).unwrap();
/// assert_eq!(vec![vec!['o', 'o', 'o', ' '],
///                 vec!['@', '*', 'o', '*'],
///                 vec!['*', '*', 'o', 'X'],
///                 vec![' ', '*', ' ', ' ']],
///            format_path_map(&grid, &path));
/// ```
#[inline]
pub fn format_path_map(grid: &Vec<Vec<char>>, path: &Vec<Position>)
        -> Vec<Vec<char>> {
    let mut m = grid.clone();
    let last_path = path.len() - 1;
    for (i, p) in path.iter().enumerate() {
        m[p.y][p.x] = match i {
            0 => '@',
            _ if i == last_path => 'X',
            _ => 'o',
        }
    }
    m
}

#[allow(dead_code)]
fn print_grid(grid: &Vec<Vec<char>>) {
    println!("");
    for row in grid {
        for ch in row {
            print!(" {} ", ch);
        }
        println!("");
    }
}

#[test]
fn no_path() {
    let map =
        vec![vec![' ', ' ', '█', ' ', ' ', ' ', ' '],
             vec!['█', '█', ' ', ' ', '█', '█', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█'],
             vec![' ', ' ', ' ', '█', ' ', '█', ' '],
             vec!['█', ' ', ' ', ' ', ' ', '█', ' '],
             vec![' ', '█', '█', '█', '█', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map, Position { x: 0, y: 0}, Position { x: 6, y: 6});
    assert_eq!(None, path);
}

#[test]
fn multiple_complex_paths_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['█', '█', ' ', '█', '█', ' ', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█'],
             vec![' ', '█', ' ', '█', ' ', ' ', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█'],
             vec![' ', '█', '█', ' ', ' ', ' ', ' '],
             vec!['█', '█', ' ', ' ', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', '█', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 6, y: 8}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', 'o', 'o', 'o', 'o', 'o', ' '],
             vec!['█', '█', ' ', '█', '█', 'o', ' '],
             vec![' ', '█', ' ', '█', '█', 'o', '█'],
             vec![' ', '█', ' ', '█', ' ', 'o', ' '],
             vec![' ', '█', ' ', '█', '█', 'o', '█'],
             vec![' ', '█', '█', ' ', ' ', 'o', 'o'],
             vec!['█', '█', ' ', ' ', '█', '█', 'o'],
             vec![' ', ' ', ' ', ' ', '█', '█', 'o'],
             vec![' ', ' ', ' ', ' ', '█', ' ', 'X']],
        path_map);
}

#[test]
fn verticle_map() {
    let map =
        vec![vec![' '],
             vec![' '],
             vec![' '],
             vec![' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 0, y: 3}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@'],
             vec!['o'],
             vec!['o'],
             vec!['X']],
        path_map);
}

#[test]
fn horizontal_map() {
    let map =
        vec![vec![' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 3, y: 0}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', 'o', 'o', 'X']],
        path_map);
}

#[test]
fn straight_diagonal_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 6, y: 6}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(13, path.len())  // There are many equivalent paths.
}

#[test]
fn single_walled_path_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['█', '█', '█', '█', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', '█', '█', '█', '█', '█', '█'],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['█', '█', '█', '█', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 0, y: 6}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['█', '█', '█', '█', '█', '█', 'o'],
             vec!['o', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['o', '█', '█', '█', '█', '█', '█'],
             vec!['o', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['█', '█', '█', '█', '█', '█', 'o'],
             vec!['X', 'o', 'o', 'o', 'o', 'o', 'o']],
        path_map);
}

#[test]
fn spiral_wall_path_to_goal() {
    let map =
        vec![vec![' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', '█', '█', '█', '█', '█', '█', '█'],
             vec![' ', '█', ' ', ' ', ' ', ' ', ' ', ' '],
             vec![' ', '█', ' ', '█', '█', '█', '█', ' '],
             vec![' ', '█', ' ', '█', ' ', ' ', '█', ' '],
             vec![' ', '█', ' ', '█', '█', ' ', '█', ' '],
             vec![' ', '█', ' ', ' ', ' ', ' ', '█', ' '],
             vec![' ', '█', '█', '█', '█', '█', '█', ' '],
             vec![' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']];

    let path = find_path(&map,
                         Position { x: 0, y: 0},
                         Position { x: 4, y: 4}).unwrap();
    let path_map = format_path_map(&map, &path);
    print_grid(&path_map);
    assert_eq!(
        vec![vec!['@', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
             vec!['o', '█', '█', '█', '█', '█', '█', '█'],
             vec!['o', '█', 'o', 'o', 'o', 'o', 'o', 'o'],
             vec!['o', '█', 'o', '█', '█', '█', '█', 'o'],
             vec!['o', '█', 'o', '█', 'X', 'o', '█', 'o'],
             vec!['o', '█', 'o', '█', '█', 'o', '█', 'o'],
             vec!['o', '█', 'o', 'o', 'o', 'o', '█', 'o'],
             vec!['o', '█', '█', '█', '█', '█', '█', 'o'],
             vec!['o', 'o', 'o', 'o', 'o', 'o', 'o', 'o']],
        path_map);
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    common::{check_network, handle_request, strip_hex_prefix, with_context},
    error::{ApiError, ApiResult},
    types::{Block, BlockIdentifier, BlockRequest, BlockResponse, Transaction},
    RosettaContext,
};
use aptos_crypto::HashValue;
use aptos_logger::{debug, trace};
use std::str::FromStr;
use warp::Filter;

const Y2K_SECS: u64 = 946713600000;

pub fn block_route(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("block")
        .and(warp::post())
        .and(warp::body::json())
        .and(with_context(server_context))
        .and_then(handle_request(block))
}

/// Retrieves a block (in this case a single transaction) given it's identifier.
///
/// Our implementation allows for by `index`, which is the ledger `version` or by
/// transaction `hash`.
///
/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block)
async fn block(request: BlockRequest, server_context: RosettaContext) -> ApiResult<BlockResponse> {
    debug!("/block");
    trace!(
        request = ?request,
        server_context = ?server_context,
        "/block",
    );

    check_network(request.network_identifier, &server_context)?;

    let rest_client = server_context.rest_client()?;

    // Retrieve by block or by hash, both or neither is not allowed
    let (parent_transaction, transactions): (
        aptos_rest_client::Transaction,
        Vec<aptos_rest_client::Transaction>,
    ) = match (
        &request.block_identifier.index,
        &request.block_identifier.hash,
    ) {
        (Some(block_index), None) => {
            get_block_by_index(rest_client, server_context.block_size, *block_index).await?
        }
        (None, Some(hash)) => {
            // Allow 0x in front of hash
            let hash = HashValue::from_str(strip_hex_prefix(hash))
                .map_err(|err| ApiError::DeserializationFailed(Some(err.to_string())))?;
            let response = rest_client.get_transaction(hash).await?;
            let txn = response.into_inner();
            let version = txn.version().unwrap();
            let block_index = version_to_block_index(server_context.block_size, version);

            get_block_by_index(rest_client, server_context.block_size, block_index).await?
        }
        (None, None) => {
            // Get current version
            let response = rest_client.get_ledger_information().await?;
            let version = response.state().version;
            let block_index = version_to_block_index(server_context.block_size, version) - 1;

            get_block_by_index(rest_client, server_context.block_size, block_index).await?
        }
        (_, _) => return Err(ApiError::BlockParameterConflict),
    };

    // Build up the transaction, which should contain the `operations` as the change set
    let (block_identifier, timestamp) = if let Some(first) = transactions.first() {
        // note: timestamps are in microseconds, so we convert to milliseconds
        let mut timestamp = first.timestamp() / 1000;

        // Rosetta doesn't like timestamps before 2000
        if timestamp < Y2K_SECS {
            timestamp = Y2K_SECS;
        }
        (
            BlockIdentifier::from_transaction(server_context.block_size, first)?,
            timestamp,
        )
    } else {
        return Err(ApiError::BlockIncomplete);
    };

    let mut txns: Vec<Transaction> = Vec::new();
    for txn in transactions {
        txns.push(
            Transaction::from_transaction(server_context.coin_cache.clone(), rest_client, txn)
                .await?,
        )
    }

    let block = Block {
        block_identifier,
        parent_block_identifier: BlockIdentifier::from_transaction(
            server_context.block_size,
            &parent_transaction,
        )?,
        timestamp,
        transactions: txns,
    };

    let response = BlockResponse {
        block: Some(block),
        other_transactions: None,
    };

    Ok(response)
}

async fn get_block_by_index(
    rest_client: &aptos_rest_client::Client,
    block_size: u64,
    block_index: u64,
) -> ApiResult<(
    aptos_rest_client::Transaction,
    Vec<aptos_rest_client::Transaction>,
)> {
    let version = block_index_to_version(block_size, block_index);

    // For the genesis block, we populate parent_block_identifier with the
    // same genesis block. Refer to
    // https://www.rosetta-api.org/docs/common_mistakes.html#malformed-genesis-block
    if version == 0 {
        let response = rest_client.get_transaction_by_version(version).await?;
        let txn = response.into_inner();
        Ok((txn.clone(), vec![txn]))
    } else {
        let previous_version = block_index_to_version(block_size, block_index - 1);
        let parent_txn = rest_client
            .get_transaction_by_version(previous_version)
            .await?
            .into_inner();
        let txns = rest_client
            .get_transactions(Some(version), Some(block_size))
            .await?
            .into_inner();

        // We can't give an incomplete block, it'll have to be retried
        if txns.len() != block_size as usize {
            return Err(ApiError::BlockIncomplete);
        }
        Ok((parent_txn, txns))
    }
}

pub fn block_index_to_version(block_size: u64, block_index: u64) -> u64 {
    if block_index == 0 {
        0
    } else {
        ((block_index - 1) * block_size) + 1
    }
}

pub fn version_to_block_index(block_size: u64, version: u64) -> u64 {
    if version == 0 {
        0
    } else {
        ((version - 1) / block_size) + 1
    }
}
<commit_msg>[aptos-rosetta] Cleanup block API<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    common::{check_network, handle_request, strip_hex_prefix, with_context},
    error::{ApiError, ApiResult},
    types::{Block, BlockIdentifier, BlockRequest, BlockResponse, Transaction},
    RosettaContext,
};
use aptos_crypto::HashValue;
use aptos_logger::{debug, trace};
use std::str::FromStr;
use warp::Filter;

/// The year 2000 in seconds, as this is the lower limit for Rosetta API implementations
const Y2K_SECS: u64 = 946713600000;

pub fn block_route(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("block")
        .and(warp::post())
        .and(warp::body::json())
        .and(with_context(server_context))
        .and_then(handle_request(block))
}

/// Retrieves a block (in this case a single transaction) given it's identifier.
///
/// Our implementation allows for by `index`, which is the ledger `version` or by
/// transaction `hash`.
///
/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block)
async fn block(request: BlockRequest, server_context: RosettaContext) -> ApiResult<BlockResponse> {
    debug!("/block");
    trace!(
        request = ?request,
        server_context = ?server_context,
        "/block",
    );

    check_network(request.network_identifier, &server_context)?;

    let rest_client = server_context.rest_client()?;

    // Retrieve by block or by hash, both or neither is not allowed
    let (parent_transaction, transactions): (
        aptos_rest_client::Transaction,
        Vec<aptos_rest_client::Transaction>,
    ) = match (
        &request.block_identifier.index,
        &request.block_identifier.hash,
    ) {
        // By index
        (Some(block_index), None) => {
            get_block_by_index(rest_client, server_context.block_size, *block_index).await?
        }
        // By hash
        (None, Some(hash)) => {
            // Allow 0x in front of hash
            let hash = HashValue::from_str(strip_hex_prefix(hash))
                .map_err(|err| ApiError::DeserializationFailed(Some(err.to_string())))?;
            let response = rest_client.get_transaction(hash).await?;

            // If there is no version, then it's a pending transaction
            if let Some(version) = response.inner().version() {
                let block_index = version_to_block_index(server_context.block_size, version);
                get_block_by_index(rest_client, server_context.block_size, block_index).await?
            } else {
                return Err(ApiError::BlockIncomplete);
            }
        }
        // Get current version
        (None, None) => {
            let response = rest_client.get_ledger_information().await?;
            let version = response.state().version;

            // The current version won't be a full block, so we have to go one before it
            let block_index = version_to_block_index(server_context.block_size, version) - 1;

            get_block_by_index(rest_client, server_context.block_size, block_index).await?
        }
        (_, _) => return Err(ApiError::BlockParameterConflict),
    };

    let block = build_block(server_context, parent_transaction, transactions).await?;

    Ok(BlockResponse {
        block: Some(block),
        other_transactions: None,
    })
}

/// Build up the transaction, which should contain the `operations` as the change set
async fn build_block(
    server_context: RosettaContext,
    parent_transaction: aptos_rest_client::Transaction,
    transactions: Vec<aptos_rest_client::Transaction>,
) -> ApiResult<Block> {
    let (block_identifier, timestamp) =
        get_block_id_and_timestamp(server_context.block_size, &transactions)?;

    // Convert the transactions and build the block
    let mut txns: Vec<Transaction> = Vec::new();
    for txn in transactions {
        txns.push(
            Transaction::from_transaction(
                server_context.coin_cache.clone(),
                server_context.rest_client()?,
                txn,
            )
            .await?,
        )
    }

    Ok(Block {
        block_identifier,
        parent_block_identifier: BlockIdentifier::from_transaction(
            server_context.block_size,
            &parent_transaction,
        )?,
        timestamp,
        transactions: txns,
    })
}

/// Retrieves the block id and the timestamp from the first transaction in the block
fn get_block_id_and_timestamp(
    block_size: u64,
    transactions: &[aptos_rest_client::Transaction],
) -> ApiResult<(BlockIdentifier, u64)> {
    if let Some(first) = transactions.first() {
        // note: timestamps are in microseconds, so we convert to milliseconds
        let mut timestamp = first.timestamp() / 1000;

        // Rosetta doesn't like timestamps before 2000
        if timestamp < Y2K_SECS {
            timestamp = Y2K_SECS;
        }
        Ok((
            BlockIdentifier::from_transaction(block_size, first)?,
            timestamp,
        ))
    } else {
        Err(ApiError::BlockIncomplete)
    }
}

/// Retrieves a block by its index
async fn get_block_by_index(
    rest_client: &aptos_rest_client::Client,
    block_size: u64,
    block_index: u64,
) -> ApiResult<(
    aptos_rest_client::Transaction,
    Vec<aptos_rest_client::Transaction>,
)> {
    let version = block_index_to_version(block_size, block_index);

    // For the genesis block, we populate parent_block_identifier with the
    // same genesis block. Refer to
    // https://www.rosetta-api.org/docs/common_mistakes.html#malformed-genesis-block
    if version == 0 {
        let response = rest_client.get_transaction_by_version(version).await?;
        let txn = response.into_inner();
        Ok((txn.clone(), vec![txn]))
    } else {
        let previous_version = block_index_to_version(block_size, block_index - 1);
        let parent_txn = rest_client
            .get_transaction_by_version(previous_version)
            .await?
            .into_inner();
        let txns = rest_client
            .get_transactions(Some(version), Some(block_size))
            .await?
            .into_inner();

        // We can't give an incomplete block, it'll have to be retried
        if txns.len() != block_size as usize {
            return Err(ApiError::BlockIncomplete);
        }
        Ok((parent_txn, txns))
    }
}

/// Converts block index to its associated version
pub fn block_index_to_version(block_size: u64, block_index: u64) -> u64 {
    if block_index == 0 {
        0
    } else {
        ((block_index - 1) * block_size) + 1
    }
}

/// Converts ledger version to its associated block index
pub fn version_to_block_index(block_size: u64, version: u64) -> u64 {
    if version == 0 {
        0
    } else {
        ((version - 1) / block_size) + 1
    }
}
<|endoftext|>


<commit_before>use std::time::UNIX_EPOCH;

use matches::assert_matches;
use ruma_identifiers::{event_id, room_alias_id, room_id, user_id};
use serde_json::{from_value as from_json_value, json, Value as JsonValue};

use ruma_events::{
    room::{
        aliases::AliasesEventContent,
        message::{MessageEventContent, MessageType, TextMessageEventContent},
        power_levels::PowerLevelsEventContent,
    },
    AnyEphemeralRoomEvent, AnyEvent, AnyMessageEvent, AnyRoomEvent, AnyStateEvent,
    AnyStateEventContent, AnySyncMessageEvent, AnySyncRoomEvent, AnySyncStateEvent, MessageEvent,
    StateEvent, SyncMessageEvent, SyncStateEvent, Unsigned,
};

fn message_event() -> JsonValue {
    json!({
        "content": {
            "body": "baba",
            "format": "org.matrix.custom.html",
            "formatted_body": "baba",
            "msgtype": "m.text"
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "room_id": "!room:room.com",
        "type": "m.room.message",
        "unsigned": {
            "age": 1
        }
    })
}

fn message_event_sync() -> JsonValue {
    json!({
        "content": {
            "body": "baba",
            "format": "org.matrix.custom.html",
            "formatted_body": "baba",
            "msgtype": "m.text"
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "type": "m.room.message",
        "unsigned": {
            "age": 1
        }
    })
}

fn aliases_event() -> JsonValue {
    json!({
        "content": {
            "aliases": ["#somewhere:localhost"]
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "state_key": "",
        "room_id": "!room:room.com",
        "type": "m.room.aliases",
        "unsigned": {
            "age": 1
        }
    })
}

fn aliases_event_sync() -> JsonValue {
    json!({
        "content": {
            "aliases": ["#somewhere:localhost"]
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "state_key": "",
        "type": "m.room.aliases",
        "unsigned": {
            "age": 1
        }
    })
}

#[test]
fn power_event_sync_deserialization() {
    let json_data = json!({
        "content": {
            "ban": 50,
            "events": {
                "m.room.avatar": 50,
                "m.room.canonical_alias": 50,
                "m.room.history_visibility": 100,
                "m.room.name": 50,
                "m.room.power_levels": 100
            },
            "events_default": 0,
            "invite": 0,
            "kick": 50,
            "redact": 50,
            "state_default": 50,
            "users": {
                "@example:localhost": 100
            },
            "users_default": 0
        },
        "event_id": "$15139375512JaHAW:localhost",
        "origin_server_ts": 45,
        "sender": "@example:localhost",
        "state_key": "",
        "type": "m.room.power_levels",
        "unsigned": {
            "age": 45
        }
    });

    assert_matches!(
        from_json_value::<AnySyncRoomEvent>(json_data),
        Ok(AnySyncRoomEvent::State(
            AnySyncStateEvent::RoomPowerLevels(SyncStateEvent {
                content: PowerLevelsEventContent {
                    ban, ..
                },
                ..
            })
        ))
        if ban == js_int::Int::new(50).unwrap()
    );
}

#[test]
fn message_event_sync_deserialization() {
    let json_data = message_event_sync();

    assert_matches!(
        from_json_value::<AnySyncRoomEvent>(json_data),
        Ok(AnySyncRoomEvent::Message(
            AnySyncMessageEvent::RoomMessage(SyncMessageEvent {
                content: MessageEventContent {
                    msgtype: MessageType::Text(TextMessageEventContent {
                        body,
                        formatted: Some(formatted),
                        ..
                    }),
                    ..
                },
                ..
            })
        ))
        if body == "baba" && formatted.body == "baba"
    );
}

#[test]
fn aliases_event_sync_deserialization() {
    let json_data = aliases_event_sync();

    assert_matches!(
        from_json_value::<AnySyncRoomEvent>(json_data),
        Ok(AnySyncRoomEvent::State(
            AnySyncStateEvent::RoomAliases(SyncStateEvent {
                content: AliasesEventContent {
                    aliases,
                    ..
                },
                ..
            })
        ))
        if aliases == vec![ room_alias_id!("#somewhere:localhost") ]
    );
}

#[test]
fn message_room_event_deserialization() {
    let json_data = message_event();

    assert_matches!(
        from_json_value::<AnyRoomEvent>(json_data),
        Ok(AnyRoomEvent::Message(
            AnyMessageEvent::RoomMessage(MessageEvent {
                content: MessageEventContent {
                    msgtype: MessageType::Text(TextMessageEventContent {
                        body,
                        formatted: Some(formatted),
                        ..
                    }),
                    ..
                },
                ..
            })
        ))
        if body == "baba" && formatted.body == "baba"
    );
}

#[test]
fn message_event_serialization() {
    let event = MessageEvent {
        content: MessageEventContent::text_plain("test"),
        event_id: event_id!("$1234:example.com"),
        origin_server_ts: UNIX_EPOCH,
        room_id: room_id!("!roomid:example.com"),
        sender: user_id!("@test:example.com"),
        unsigned: Unsigned::default(),
    };

    assert_eq!(
        serde_json::to_string(&event).expect("Failed to serialize message event"),
        r#"{"type":"m.room.message","content":{"msgtype":"m.text","body":"test"},"event_id":"$1234:example.com","sender":"@test:example.com","origin_server_ts":0,"room_id":"!roomid:example.com"}"#
    )
}

#[test]
fn alias_room_event_deserialization() {
    let json_data = aliases_event();

    assert_matches!(
        from_json_value::<AnyRoomEvent>(json_data),
        Ok(AnyRoomEvent::State(
            AnyStateEvent::RoomAliases(StateEvent {
                content: AliasesEventContent {
                    aliases,
                    ..
                },
                ..
            })
        ))
        if aliases == vec![ room_alias_id!("#somewhere:localhost") ]
    );
}

#[test]
fn message_event_deserialization() {
    let json_data = message_event();

    assert_matches!(
        from_json_value::<AnyEvent>(json_data),
        Ok(AnyEvent::Message(
            AnyMessageEvent::RoomMessage(MessageEvent {
                content: MessageEventContent {
                    msgtype: MessageType::Text(TextMessageEventContent {
                        body,
                        formatted: Some(formatted),
                        ..
                    }),
                    ..
                },
                ..
            })
        ))
        if body == "baba" && formatted.body == "baba"
    );
}

#[test]
fn alias_event_deserialization() {
    let json_data = aliases_event();

    assert_matches!(
        from_json_value::<AnyEvent>(json_data),
        Ok(AnyEvent::State(
            AnyStateEvent::RoomAliases(StateEvent {
                content: AliasesEventContent {
                    aliases,
                    ..
                },
                ..
            })
        ))
        if aliases == vec![ room_alias_id!("#somewhere:localhost") ]
    );
}

#[test]
fn alias_event_field_access() {
    let json_data = aliases_event();

    assert_matches!(
        from_json_value::<AnyEvent>(json_data.clone()),
        Ok(AnyEvent::State(state_event))
        if state_event.state_key() == ""
            && state_event.room_id() == &room_id!("!room:room.com")
            && state_event.event_id() == &event_id!("$152037280074GZeOm:localhost")
            && state_event.sender() == &user_id!("@example:localhost")
    );

    let deser = from_json_value::<AnyStateEvent>(json_data).unwrap();
    if let AnyStateEventContent::RoomAliases(AliasesEventContent { aliases, .. }) = deser.content()
    {
        assert_eq!(aliases, vec![room_alias_id!("#somewhere:localhost")])
    } else {
        panic!("the `Any*Event` enum's accessor methods may have been altered")
    }
    assert_eq!(deser.event_type(), "m.room.aliases");
}

#[test]
fn ephemeral_event_deserialization() {
    let json_data = json!({
        "content": {
            "user_ids": [
                "@alice:matrix.org",
                "@bob:example.com"
            ]
        },
        "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
        "type": "m.typing"
    });

    assert_matches!(
        from_json_value::<AnyEvent>(json_data),
        Ok(AnyEvent::Ephemeral(ephem @ AnyEphemeralRoomEvent::Typing(_)))
        if ephem.room_id() == &room_id!("!jEsUZKDJdhlrceRyVU:example.org")
    );
}
<commit_msg>events: Remove AnyEvent from enum tests<commit_after>use std::time::UNIX_EPOCH;

use matches::assert_matches;
use ruma_identifiers::{event_id, room_alias_id, room_id, user_id};
use serde_json::{from_value as from_json_value, json, Value as JsonValue};

use ruma_events::{
    room::{
        aliases::AliasesEventContent,
        message::{MessageEventContent, MessageType, TextMessageEventContent},
        power_levels::PowerLevelsEventContent,
    },
    AnyEphemeralRoomEvent, AnyMessageEvent, AnyRoomEvent, AnyStateEvent, AnyStateEventContent,
    AnySyncMessageEvent, AnySyncRoomEvent, AnySyncStateEvent, MessageEvent, StateEvent,
    SyncMessageEvent, SyncStateEvent, Unsigned,
};

fn message_event() -> JsonValue {
    json!({
        "content": {
            "body": "baba",
            "format": "org.matrix.custom.html",
            "formatted_body": "baba",
            "msgtype": "m.text"
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "room_id": "!room:room.com",
        "type": "m.room.message",
        "unsigned": {
            "age": 1
        }
    })
}

fn message_event_sync() -> JsonValue {
    json!({
        "content": {
            "body": "baba",
            "format": "org.matrix.custom.html",
            "formatted_body": "baba",
            "msgtype": "m.text"
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "type": "m.room.message",
        "unsigned": {
            "age": 1
        }
    })
}

fn aliases_event() -> JsonValue {
    json!({
        "content": {
            "aliases": ["#somewhere:localhost"]
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "state_key": "",
        "room_id": "!room:room.com",
        "type": "m.room.aliases",
        "unsigned": {
            "age": 1
        }
    })
}

fn aliases_event_sync() -> JsonValue {
    json!({
        "content": {
            "aliases": ["#somewhere:localhost"]
        },
        "event_id": "$152037280074GZeOm:localhost",
        "origin_server_ts": 1,
        "sender": "@example:localhost",
        "state_key": "",
        "type": "m.room.aliases",
        "unsigned": {
            "age": 1
        }
    })
}

#[test]
fn power_event_sync_deserialization() {
    let json_data = json!({
        "content": {
            "ban": 50,
            "events": {
                "m.room.avatar": 50,
                "m.room.canonical_alias": 50,
                "m.room.history_visibility": 100,
                "m.room.name": 50,
                "m.room.power_levels": 100
            },
            "events_default": 0,
            "invite": 0,
            "kick": 50,
            "redact": 50,
            "state_default": 50,
            "users": {
                "@example:localhost": 100
            },
            "users_default": 0
        },
        "event_id": "$15139375512JaHAW:localhost",
        "origin_server_ts": 45,
        "sender": "@example:localhost",
        "state_key": "",
        "type": "m.room.power_levels",
        "unsigned": {
            "age": 45
        }
    });

    assert_matches!(
        from_json_value::<AnySyncRoomEvent>(json_data),
        Ok(AnySyncRoomEvent::State(
            AnySyncStateEvent::RoomPowerLevels(SyncStateEvent {
                content: PowerLevelsEventContent {
                    ban, ..
                },
                ..
            })
        ))
        if ban == js_int::Int::new(50).unwrap()
    );
}

#[test]
fn message_event_sync_deserialization() {
    let json_data = message_event_sync();

    assert_matches!(
        from_json_value::<AnySyncRoomEvent>(json_data),
        Ok(AnySyncRoomEvent::Message(
            AnySyncMessageEvent::RoomMessage(SyncMessageEvent {
                content: MessageEventContent {
                    msgtype: MessageType::Text(TextMessageEventContent {
                        body,
                        formatted: Some(formatted),
                        ..
                    }),
                    ..
                },
                ..
            })
        ))
        if body == "baba" && formatted.body == "baba"
    );
}

#[test]
fn aliases_event_sync_deserialization() {
    let json_data = aliases_event_sync();

    assert_matches!(
        from_json_value::<AnySyncRoomEvent>(json_data),
        Ok(AnySyncRoomEvent::State(
            AnySyncStateEvent::RoomAliases(SyncStateEvent {
                content: AliasesEventContent {
                    aliases,
                    ..
                },
                ..
            })
        ))
        if aliases == vec![ room_alias_id!("#somewhere:localhost") ]
    );
}

#[test]
fn message_room_event_deserialization() {
    let json_data = message_event();

    assert_matches!(
        from_json_value::<AnyRoomEvent>(json_data),
        Ok(AnyRoomEvent::Message(
            AnyMessageEvent::RoomMessage(MessageEvent {
                content: MessageEventContent {
                    msgtype: MessageType::Text(TextMessageEventContent {
                        body,
                        formatted: Some(formatted),
                        ..
                    }),
                    ..
                },
                ..
            })
        ))
        if body == "baba" && formatted.body == "baba"
    );
}

#[test]
fn message_event_serialization() {
    let event = MessageEvent {
        content: MessageEventContent::text_plain("test"),
        event_id: event_id!("$1234:example.com"),
        origin_server_ts: UNIX_EPOCH,
        room_id: room_id!("!roomid:example.com"),
        sender: user_id!("@test:example.com"),
        unsigned: Unsigned::default(),
    };

    assert_eq!(
        serde_json::to_string(&event).expect("Failed to serialize message event"),
        r#"{"type":"m.room.message","content":{"msgtype":"m.text","body":"test"},"event_id":"$1234:example.com","sender":"@test:example.com","origin_server_ts":0,"room_id":"!roomid:example.com"}"#
    )
}

#[test]
fn alias_room_event_deserialization() {
    let json_data = aliases_event();

    assert_matches!(
        from_json_value::<AnyRoomEvent>(json_data),
        Ok(AnyRoomEvent::State(
            AnyStateEvent::RoomAliases(StateEvent {
                content: AliasesEventContent {
                    aliases,
                    ..
                },
                ..
            })
        ))
        if aliases == vec![ room_alias_id!("#somewhere:localhost") ]
    );
}

#[test]
fn message_event_deserialization() {
    let json_data = message_event();

    assert_matches!(
        from_json_value::<AnyRoomEvent>(json_data),
        Ok(AnyRoomEvent::Message(
            AnyMessageEvent::RoomMessage(MessageEvent {
                content: MessageEventContent {
                    msgtype: MessageType::Text(TextMessageEventContent {
                        body,
                        formatted: Some(formatted),
                        ..
                    }),
                    ..
                },
                ..
            })
        ))
        if body == "baba" && formatted.body == "baba"
    );
}

#[test]
fn alias_event_deserialization() {
    let json_data = aliases_event();

    assert_matches!(
        from_json_value::<AnyRoomEvent>(json_data),
        Ok(AnyRoomEvent::State(
            AnyStateEvent::RoomAliases(StateEvent {
                content: AliasesEventContent {
                    aliases,
                    ..
                },
                ..
            })
        ))
        if aliases == vec![ room_alias_id!("#somewhere:localhost") ]
    );
}

#[test]
fn alias_event_field_access() {
    let json_data = aliases_event();

    assert_matches!(
        from_json_value::<AnyRoomEvent>(json_data.clone()),
        Ok(AnyRoomEvent::State(state_event))
        if state_event.state_key() == ""
            && state_event.room_id() == &room_id!("!room:room.com")
            && state_event.event_id() == &event_id!("$152037280074GZeOm:localhost")
            && state_event.sender() == &user_id!("@example:localhost")
    );

    let deser = from_json_value::<AnyStateEvent>(json_data).unwrap();
    if let AnyStateEventContent::RoomAliases(AliasesEventContent { aliases, .. }) = deser.content()
    {
        assert_eq!(aliases, vec![room_alias_id!("#somewhere:localhost")])
    } else {
        panic!("the `Any*Event` enum's accessor methods may have been altered")
    }
    assert_eq!(deser.event_type(), "m.room.aliases");
}

#[test]
fn ephemeral_event_deserialization() {
    let json_data = json!({
        "content": {
            "user_ids": [
                "@alice:matrix.org",
                "@bob:example.com"
            ]
        },
        "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
        "type": "m.typing"
    });

    assert_matches!(
        from_json_value::<AnyEphemeralRoomEvent>(json_data),
        Ok(ephem @ AnyEphemeralRoomEvent::Typing(_))
        if ephem.room_id() == &room_id!("!jEsUZKDJdhlrceRyVU:example.org")
    );
}
<|endoftext|>


<commit_before>//! In this example, you will be shown various ways of sharing data between events and commands.
//! And how to use locks correctly to avoid deadlocking the bot.

use std::collections::HashMap;
use std::env;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

use serenity::async_trait;
use serenity::framework::standard::macros::{command, group, hook};
use serenity::framework::standard::{Args, CommandResult, StandardFramework};
use serenity::model::channel::Message;
use serenity::model::gateway::Ready;
use serenity::prelude::*;
use tokio::sync::RwLock;

// A container type is created for inserting into the Client's `data`, which
// allows for data to be accessible across all events and framework commands, or
// anywhere else that has a copy of the `data` Arc.
// These places are usually where either Context or Client is present.
//
// Documentation about TypeMap can be found here:
// https://docs.rs/typemap_rev/0.1/typemap_rev/struct.TypeMap.html
struct CommandCounter;

impl TypeMapKey for CommandCounter {
    type Value = Arc<RwLock<HashMap<String, u64>>>;
}

struct MessageCount;

impl TypeMapKey for MessageCount {
    // While you will be using RwLock or Mutex most of the time you want to modify data,
    // sometimes it's not required; like for example, with static data, or if you are using other
    // kinds of atomic operators.
    //
    // Arc should stay, to allow for the data lock to be closed early.
    type Value = Arc<AtomicUsize>;
}

#[group]
#[commands(ping, command_usage, owo_count)]
struct General;

#[hook]
async fn before(ctx: &Context, msg: &Message, command_name: &str) -> bool {
    println!("Running command '{}' invoked by '{}'", command_name, msg.author.tag());

    let counter_lock = {
        // While data is a RwLock, it's recommended that you always open the lock as read.
        // This is mainly done to avoid Deadlocks for having a possible writer waiting for multiple
        // readers to close.
        let data_read = ctx.data.read().await;

        // Since the CommandCounter Value is wrapped in an Arc, cloning will not duplicate the
        // data, instead the reference is cloned.
        // We wrap every value on in an Arc, as to keep the data lock open for the least time possible,
        // to again, avoid deadlocking it.
        data_read.get::<CommandCounter>().expect("Expected CommandCounter in TypeMap.").clone()
    };

    // Just like with client.data in main, we want to keep write locks open the least time
    // possible, so we wrap them on a block so they get automatically closed at the end.
    {
        // The HashMap of CommandCounter is wrapped in an RwLock; since we want to write to it, we will
        // open the lock in write mode.
        let mut counter = counter_lock.write().await;

        // And we write the amount of times the command has been called to it.
        let entry = counter.entry(command_name.to_string()).or_insert(0);
        *entry += 1;
    }

    true
}

struct Handler;

#[async_trait]
impl EventHandler for Handler {
    async fn message(&self, ctx: Context, msg: Message) {
        // We are verifying if the bot id is the same as the message author id
        if msg.author.id != ctx.cache.current_user_id()
            && msg.content.to_lowercase().contains("owo")
        {
            // Since data is located in Context, this means you are also able to use it within events!
            let count = {
                let data_read = ctx.data.read().await;
                data_read.get::<MessageCount>().expect("Expected MessageCount in TypeMap.").clone()
            };

            // Atomic operations with ordering do not require mut to be modified.
            // In this case, we want to increase the message count by 1.
            // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicUsize.html#method.fetch_add
            count.fetch_add(1, Ordering::SeqCst);
        }
    }

    async fn ready(&self, _: Context, ready: Ready) {
        println!("{} is connected!", ready.user.name);
    }
}

#[tokio::main]
async fn main() {
    let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");

    let framework = StandardFramework::new()
        .configure(|c| c.with_whitespace(true).prefix("~"))
        .before(before)
        .group(&GENERAL_GROUP);

    let intents = GatewayIntents::GUILD_MESSAGES
        | GatewayIntents::DIRECT_MESSAGES
        | GatewayIntents::MESSAGE_CONTENT;
    let mut client = Client::builder(&token, intents)
        .event_handler(Handler)
        .framework(framework)
        .await
        .expect("Err creating client");

    // This is where we can initially insert the data we desire into the "global" data TypeMap.
    // client.data is wrapped on a RwLock, and since we want to insert to it, we have to open it in
    // write mode, but there's a small thing catch:
    // There can only be a single writer to a given lock open in the entire application, this means
    // you can't open a new write lock until the previous write lock has closed.
    // This is not the case with read locks, read locks can be open indefinitely, BUT as soon as
    // you need to open the lock in write mode, all the read locks must be closed.
    //
    // You can find more information about deadlocks in the Rust Book, ch16-03:
    // https://doc.rust-lang.org/book/ch16-03-shared-state.html
    //
    // All of this means that we have to keep locks open for the least time possible, so we put
    // them inside a block, so they get closed automatically when dropped.
    // If we don't do this, we would never be able to open the data lock anywhere else.
    //
    // Alternatively, you can also use `ClientBuilder::type_map_insert` or
    // `ClientBuilder::type_map` to populate the global TypeMap without dealing with the RwLock.
    {
        // Open the data lock in write mode, so keys can be inserted to it.
        let mut data = client.data.write().await;

        // The CommandCounter Value has the following type:
        // Arc<RwLock<HashMap<String, u64>>>
        // So, we have to insert the same type to it.
        data.insert::<CommandCounter>(Arc::new(RwLock::new(HashMap::default())));

        data.insert::<MessageCount>(Arc::new(AtomicUsize::new(0)));
    }

    if let Err(why) = client.start().await {
        eprintln!("Client error: {:?}", why);
    }
}

#[command]
async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
    msg.reply(ctx, "Pong!").await?;

    Ok(())
}

/// Usage: `~command_usage <command_name>`
/// Example: `~command_usage ping`
#[command]
async fn command_usage(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
    let command_name = match args.single_quoted::<String>() {
        Ok(x) => x,
        Err(_) => {
            msg.reply(ctx, "I require an argument to run this command.").await?;
            return Ok(());
        },
    };

    // Yet again, we want to keep the locks open for the least time possible.
    let amount = {
        // Since we only want to read the data and not write to it, we open it in read mode,
        // and since this is open in read mode, it means that there can be multiple locks open at
        // the same time, and as mentioned earlier, it's heavily recommended that you only open
        // the data lock in read mode, as it will avoid a lot of possible deadlocks.
        let data_read = ctx.data.read().await;

        // Then we obtain the value we need from data, in this case, we want the command counter.
        // The returned value from get() is an Arc, so the reference will be cloned, rather than
        // the data.
        let command_counter_lock =
            data_read.get::<CommandCounter>().expect("Expected CommandCounter in TypeMap.").clone();

        let command_counter = command_counter_lock.read().await;
        // And we return a usable value from it.
        // This time, the value is not Arc, so the data will be cloned.
        command_counter.get(&command_name).map_or(0, |x| *x)
    };

    if amount == 0 {
        msg.reply(ctx, format!("The command `{}` has not yet been used.", command_name)).await?;
    } else {
        msg.reply(
            ctx,
            format!("The command `{}` has been used {} time/s this session!", command_name, amount),
        )
        .await?;
    }

    Ok(())
}

#[command]
async fn owo_count(ctx: &Context, msg: &Message) -> CommandResult {
    let raw_count = {
        let data_read = ctx.data.read().await;
        data_read.get::<MessageCount>().expect("Expected MessageCount in TypeMap.").clone()
    };

    let count = raw_count.load(Ordering::Relaxed);

    if count == 1 {
        msg.reply(
            ctx,
            "You are the first one to say owo this session! *because it's on the command name* :P",
        )
        .await?;
    } else {
        msg.reply(ctx, format!("OWO Has been said {} times!", count)).await?;
    }

    Ok(())
}
<commit_msg>Count every "owo" word in messages in example 12 (#1998)<commit_after>//! In this example, you will be shown various ways of sharing data between events and commands.
//! And how to use locks correctly to avoid deadlocking the bot.

use std::collections::HashMap;
use std::env;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

use serenity::async_trait;
use serenity::framework::standard::macros::{command, group, hook};
use serenity::framework::standard::{Args, CommandResult, StandardFramework};
use serenity::model::channel::Message;
use serenity::model::gateway::Ready;
use serenity::prelude::*;
use tokio::sync::RwLock;

// A container type is created for inserting into the Client's `data`, which
// allows for data to be accessible across all events and framework commands, or
// anywhere else that has a copy of the `data` Arc.
// These places are usually where either Context or Client is present.
//
// Documentation about TypeMap can be found here:
// https://docs.rs/typemap_rev/0.1/typemap_rev/struct.TypeMap.html
struct CommandCounter;

impl TypeMapKey for CommandCounter {
    type Value = Arc<RwLock<HashMap<String, u64>>>;
}

struct MessageCount;

impl TypeMapKey for MessageCount {
    // While you will be using RwLock or Mutex most of the time you want to modify data,
    // sometimes it's not required; like for example, with static data, or if you are using other
    // kinds of atomic operators.
    //
    // Arc should stay, to allow for the data lock to be closed early.
    type Value = Arc<AtomicUsize>;
}

#[group]
#[commands(ping, command_usage, owo_count)]
struct General;

#[hook]
async fn before(ctx: &Context, msg: &Message, command_name: &str) -> bool {
    println!("Running command '{}' invoked by '{}'", command_name, msg.author.tag());

    let counter_lock = {
        // While data is a RwLock, it's recommended that you always open the lock as read.
        // This is mainly done to avoid Deadlocks for having a possible writer waiting for multiple
        // readers to close.
        let data_read = ctx.data.read().await;

        // Since the CommandCounter Value is wrapped in an Arc, cloning will not duplicate the
        // data, instead the reference is cloned.
        // We wrap every value on in an Arc, as to keep the data lock open for the least time possible,
        // to again, avoid deadlocking it.
        data_read.get::<CommandCounter>().expect("Expected CommandCounter in TypeMap.").clone()
    };

    // Just like with client.data in main, we want to keep write locks open the least time
    // possible, so we wrap them on a block so they get automatically closed at the end.
    {
        // The HashMap of CommandCounter is wrapped in an RwLock; since we want to write to it, we will
        // open the lock in write mode.
        let mut counter = counter_lock.write().await;

        // And we write the amount of times the command has been called to it.
        let entry = counter.entry(command_name.to_string()).or_insert(0);
        *entry += 1;
    }

    true
}

struct Handler;

#[async_trait]
impl EventHandler for Handler {
    async fn message(&self, ctx: Context, msg: Message) {
        // We are verifying if the bot id is the same as the message author id.
        if msg.author.id != ctx.cache.current_user_id()
            && msg.content.to_lowercase().contains("owo")
        {
            // Since data is located in Context, this means you are also able to use it within events!
            let count = {
                let data_read = ctx.data.read().await;
                data_read.get::<MessageCount>().expect("Expected MessageCount in TypeMap.").clone()
            };

            // Here, we are checking how many "owo" there are in the message content.
            let owo_in_msg = msg.content.to_ascii_lowercase().matches("owo").count();

            // Atomic operations with ordering do not require mut to be modified.
            // In this case, we want to increase the message count by 1.
            // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicUsize.html#method.fetch_add
            count.fetch_add(owo_in_msg, Ordering::SeqCst);
        }
    }

    async fn ready(&self, _: Context, ready: Ready) {
        println!("{} is connected!", ready.user.name);
    }
}

#[tokio::main]
async fn main() {
    let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");

    let framework = StandardFramework::new()
        .configure(|c| c.with_whitespace(true).prefix("~"))
        .before(before)
        .group(&GENERAL_GROUP);

    let intents = GatewayIntents::GUILD_MESSAGES
        | GatewayIntents::DIRECT_MESSAGES
        | GatewayIntents::MESSAGE_CONTENT;
    let mut client = Client::builder(&token, intents)
        .event_handler(Handler)
        .framework(framework)
        .await
        .expect("Err creating client");

    // This is where we can initially insert the data we desire into the "global" data TypeMap.
    // client.data is wrapped on a RwLock, and since we want to insert to it, we have to open it in
    // write mode, but there's a small thing catch:
    // There can only be a single writer to a given lock open in the entire application, this means
    // you can't open a new write lock until the previous write lock has closed.
    // This is not the case with read locks, read locks can be open indefinitely, BUT as soon as
    // you need to open the lock in write mode, all the read locks must be closed.
    //
    // You can find more information about deadlocks in the Rust Book, ch16-03:
    // https://doc.rust-lang.org/book/ch16-03-shared-state.html
    //
    // All of this means that we have to keep locks open for the least time possible, so we put
    // them inside a block, so they get closed automatically when dropped.
    // If we don't do this, we would never be able to open the data lock anywhere else.
    //
    // Alternatively, you can also use `ClientBuilder::type_map_insert` or
    // `ClientBuilder::type_map` to populate the global TypeMap without dealing with the RwLock.
    {
        // Open the data lock in write mode, so keys can be inserted to it.
        let mut data = client.data.write().await;

        // The CommandCounter Value has the following type:
        // Arc<RwLock<HashMap<String, u64>>>
        // So, we have to insert the same type to it.
        data.insert::<CommandCounter>(Arc::new(RwLock::new(HashMap::default())));

        data.insert::<MessageCount>(Arc::new(AtomicUsize::new(0)));
    }

    if let Err(why) = client.start().await {
        eprintln!("Client error: {:?}", why);
    }
}

#[command]
async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
    msg.reply(ctx, "Pong!").await?;

    Ok(())
}

/// Usage: `~command_usage <command_name>`
/// Example: `~command_usage ping`
#[command]
async fn command_usage(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
    let command_name = match args.single_quoted::<String>() {
        Ok(x) => x,
        Err(_) => {
            msg.reply(ctx, "I require an argument to run this command.").await?;
            return Ok(());
        },
    };

    // Yet again, we want to keep the locks open for the least time possible.
    let amount = {
        // Since we only want to read the data and not write to it, we open it in read mode,
        // and since this is open in read mode, it means that there can be multiple locks open at
        // the same time, and as mentioned earlier, it's heavily recommended that you only open
        // the data lock in read mode, as it will avoid a lot of possible deadlocks.
        let data_read = ctx.data.read().await;

        // Then we obtain the value we need from data, in this case, we want the command counter.
        // The returned value from get() is an Arc, so the reference will be cloned, rather than
        // the data.
        let command_counter_lock =
            data_read.get::<CommandCounter>().expect("Expected CommandCounter in TypeMap.").clone();

        let command_counter = command_counter_lock.read().await;
        // And we return a usable value from it.
        // This time, the value is not Arc, so the data will be cloned.
        command_counter.get(&command_name).map_or(0, |x| *x)
    };

    if amount == 0 {
        msg.reply(ctx, format!("The command `{}` has not yet been used.", command_name)).await?;
    } else {
        msg.reply(
            ctx,
            format!("The command `{}` has been used {} time/s this session!", command_name, amount),
        )
        .await?;
    }

    Ok(())
}

#[command]
async fn owo_count(ctx: &Context, msg: &Message) -> CommandResult {
    let raw_count = {
        let data_read = ctx.data.read().await;
        data_read.get::<MessageCount>().expect("Expected MessageCount in TypeMap.").clone()
    };

    let count = raw_count.load(Ordering::Relaxed);

    if count == 1 {
        msg.reply(
            ctx,
            "You are the first one to say owo this session! *because it's on the command name* :P",
        )
        .await?;
    } else {
        msg.reply(ctx, format!("OWO Has been said {} times!", count)).await?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.

use std::os::raw::{c_char, c_void, c_int};
use ffi::types::*;

#[link(name = "portmidi")]
extern "C" {
    pub fn Pm_Initialize() -> PmError;
    pub fn Pm_Terminate() -> PmError;
    pub fn Pm_HasHostError(stream: *const PortMidiStream) -> c_int;
    pub fn Pm_GetErrorText(errorCode: PmError) -> *const c_char;
    pub fn Pm_GetHostErrorText(msg: *const c_char, len: c_int);
    pub fn Pm_CountDevices() -> c_int;
    pub fn Pm_GetDefaultInputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDefaultOutputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDeviceInfo(id: PmDeviceId) -> *const PmDeviceInfo;
    pub fn Pm_OpenInput(stream: *const *const PortMidiStream,
                        inputDevice: PmDeviceId,
                        inputDriverInfo: *const c_void,
                        bufferSize: i32,
                        time_proc: *const c_void,
                        time_info: *const c_void)
                        -> PmError;
    pub fn Pm_OpenOutput(stream: *const *const PortMidiStream,
                         outputDevice: PmDeviceId,
                         inputDriverInfo: *const c_void,
                         bufferSize: i32,
                         time_proc: *const c_void,
                         time_info: *const c_void,
                         latency: i32)
                         -> PmError;
    pub fn Pm_Read(stream: *const PortMidiStream, buffer: *mut PmEvent, length: c_int) -> c_int;
    pub fn Pm_Abort(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Close(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Poll(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Write(stream: *const PortMidiStream,
                    buffer: *const PmEvent,
                    length: c_int)
                    -> PmError;
    pub fn Pm_WriteShort(stream: *const PortMidiStream,
                         timestamp: PmTimestamp,
                         message: PmMessage)
                         -> PmError;
}
<commit_msg>Make unused functions private<commit_after>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.

use std::os::raw::{c_char, c_void, c_int};
use ffi::types::*;

#[link(name = "portmidi")]
extern "C" {
    pub fn Pm_Initialize() -> PmError;
    pub fn Pm_Terminate() -> PmError;
    fn Pm_HasHostError(stream: *const PortMidiStream) -> c_int;
    pub fn Pm_GetErrorText(errorCode: PmError) -> *const c_char;
    fn Pm_GetHostErrorText(msg: *const c_char, len: c_int);
    pub fn Pm_CountDevices() -> c_int;
    pub fn Pm_GetDefaultInputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDefaultOutputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDeviceInfo(id: PmDeviceId) -> *const PmDeviceInfo;
    pub fn Pm_OpenInput(stream: *const *const PortMidiStream,
                        inputDevice: PmDeviceId,
                        inputDriverInfo: *const c_void,
                        bufferSize: i32,
                        time_proc: *const c_void,
                        time_info: *const c_void)
                        -> PmError;
    pub fn Pm_OpenOutput(stream: *const *const PortMidiStream,
                         outputDevice: PmDeviceId,
                         inputDriverInfo: *const c_void,
                         bufferSize: i32,
                         time_proc: *const c_void,
                         time_info: *const c_void,
                         latency: i32)
                         -> PmError;
    pub fn Pm_Read(stream: *const PortMidiStream, buffer: *mut PmEvent, length: c_int) -> c_int;
    fn Pm_Abort(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Close(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Poll(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Write(stream: *const PortMidiStream,
                    buffer: *const PmEvent,
                    length: c_int)
                    -> PmError;
    pub fn Pm_WriteShort(stream: *const PortMidiStream,
                         timestamp: PmTimestamp,
                         message: PmMessage)
                         -> PmError;
}
<|endoftext|>


<commit_before>pub trait Property {
    fn inherits_automatically() -> bool;
}

/// Generates a property definition that simply parses strings to enum variants
///
/// This can be used for properties with simple symbol-based values.
/// For example, the SVG spec defines the `stroke-linejoin` property
/// to have possible values `miter | round | bevel | inherit`, with a default
/// of `miter`.  We can define the property like this:
///
/// ```
/// make_ident_property!(
/// StrokeLinejoin,
/// default: Miter,
///
/// "miter" => Miter,
/// "round" => Round,
/// "bevel" => Bevel,
/// );
/// ```
///
/// The macro will generate a `StrokeLinejoin` enum with the provided
/// variants.  It will generate an `impl Default for StrokeLinejoin`
/// with the provided `default:` value.  Finally, it will generate an
/// `impl Parse for StrokeLinejoin`, from `parsers::Parse`, where
/// `type Data = ()` and `type Err = AttributeError`.
#[macro_export]
macro_rules! make_ident_property {
    ($name: ident,
     default: $default: ident,
     inherits_automatically: $inherits_automatically: expr,
     $($str_prop: expr => $variant: ident,)+
    ) => {
        #[repr(C)]
        #[derive(Debug, Copy, Clone)]
        pub enum $name {
            $($variant),+
        }

        impl Default for $name {
            fn default() -> $name {
                $name::$default
            }
        }

        impl ::property_macros::Property for $name {
            fn inherits_automatically() -> bool {
                $inherits_automatically
            }
        }

        impl Parse for $name {
            type Data = ();
            type Err = AttributeError;

            fn parse(s: &str, _: Self::Data) -> Result<$name, AttributeError> {
                match s.trim() {
                    $($str_prop => Ok($name::$variant),)+

                    _ => Err(AttributeError::from(ParseError::new("invalid value"))),
                }
            }
        }
    };
}
<commit_msg>make_ident_property(): Qualify the type names we use so the caller doesn't have to import them<commit_after>pub trait Property {
    fn inherits_automatically() -> bool;
}

/// Generates a property definition that simply parses strings to enum variants
///
/// This can be used for properties with simple symbol-based values.
/// For example, the SVG spec defines the `stroke-linejoin` property
/// to have possible values `miter | round | bevel | inherit`, with a default
/// of `miter`.  We can define the property like this:
///
/// ```
/// make_ident_property!(
/// StrokeLinejoin,
/// default: Miter,
///
/// "miter" => Miter,
/// "round" => Round,
/// "bevel" => Bevel,
/// );
/// ```
///
/// The macro will generate a `StrokeLinejoin` enum with the provided
/// variants.  It will generate an `impl Default for StrokeLinejoin`
/// with the provided `default:` value.  Finally, it will generate an
/// `impl Parse for StrokeLinejoin`, from `parsers::Parse`, where
/// `type Data = ()` and `type Err = AttributeError`.
#[macro_export]
macro_rules! make_ident_property {
    ($name: ident,
     default: $default: ident,
     inherits_automatically: $inherits_automatically: expr,
     $($str_prop: expr => $variant: ident,)+
    ) => {
        #[repr(C)]
        #[derive(Debug, Copy, Clone)]
        pub enum $name {
            $($variant),+
        }

        impl Default for $name {
            fn default() -> $name {
                $name::$default
            }
        }

        impl ::property_macros::Property for $name {
            fn inherits_automatically() -> bool {
                $inherits_automatically
            }
        }

        impl ::parsers::Parse for $name {
            type Data = ();
            type Err = ::error::AttributeError;

            fn parse(s: &str, _: Self::Data) -> Result<$name, ::error::AttributeError> {
                match s.trim() {
                    $($str_prop => Ok($name::$variant),)+

                    _ => Err(::error::AttributeError::from(::parsers::ParseError::new("invalid value"))),
                }
            }
        }
    };
}
<|endoftext|>


<commit_before>/// Represents a language following [ISO 639-3](https://en.wikipedia.org/wiki/ISO_639-3) standard.
#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
pub enum Lang {
    {% for lang in lang_infos %}
    /// {{ lang.name }} ({{ lang.eng_name }})
    {{ lang.code | capitalize }} = {{ loop.index }},
    {% endfor %}
}

#[inline] fn lang_from_code<S: Into<String>>(code: S) -> Option<Lang> {
    match code.into().to_lowercase().as_ref() {
        {% for lang in lang_infos %}
        "{{ lang.code }}" => Some(Lang::{{ lang.code | capitalize }}),
        {% endfor %}
        _ => None,
    }
}

#[inline] fn lang_to_code(lang: Lang) -> &'static str {
    match lang {
        {% for lang in lang_infos %}
        Lang::{{ lang.code | capitalize }} => "{{ lang.code }}",
        {% endfor %}
    }
}

#[inline] fn lang_to_name(lang: Lang) -> &'static str {
    match lang {
        {% for lang in lang_infos %}
        Lang::{{ lang.code | capitalize }} => "{{ lang.name }}",
        {% endfor %}
    }
}

#[inline] fn lang_to_eng_name(lang: Lang) -> &'static str {
    match lang {
        {% for lang in lang_infos %}
        Lang::{{ lang.code | capitalize }} => "{{ lang.eng_name }}",
        {% endfor %}
    }
}

{% for script, langs in scripts %}
/// Languages for script {{ script }}
pub static {{ script | upper }}_LANGS: LangProfileList = &[
    {% for lang in langs %}
    (Lang::{{ lang.info.code | capitalize }}, &[ {% for trigram in lang.trigrams %} "{{ trigram }}", {% endfor %} ]),
    {% endfor %}
];
{% endfor %}
<commit_msg>Remove #[inline]<commit_after>/// Represents a language following [ISO 639-3](https://en.wikipedia.org/wiki/ISO_639-3) standard.
#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
pub enum Lang {
    {% for lang in lang_infos %}
    /// {{ lang.name }} ({{ lang.eng_name }})
    {{ lang.code | capitalize }} = {{ loop.index }},
    {% endfor %}
}

fn lang_from_code<S: Into<String>>(code: S) -> Option<Lang> {
    match code.into().to_lowercase().as_ref() {
        {% for lang in lang_infos %}
        "{{ lang.code }}" => Some(Lang::{{ lang.code | capitalize }}),
        {% endfor %}
        _ => None,
    }
}

fn lang_to_code(lang: Lang) -> &'static str {
    match lang {
        {% for lang in lang_infos %}
        Lang::{{ lang.code | capitalize }} => "{{ lang.code }}",
        {% endfor %}
    }
}

fn lang_to_name(lang: Lang) -> &'static str {
    match lang {
        {% for lang in lang_infos %}
        Lang::{{ lang.code | capitalize }} => "{{ lang.name }}",
        {% endfor %}
    }
}

fn lang_to_eng_name(lang: Lang) -> &'static str {
    match lang {
        {% for lang in lang_infos %}
        Lang::{{ lang.code | capitalize }} => "{{ lang.eng_name }}",
        {% endfor %}
    }
}

{% for script, langs in scripts %}
/// Languages for script {{ script }}
pub static {{ script | upper }}_LANGS: LangProfileList = &[
    {% for lang in langs %}
    (Lang::{{ lang.info.code | capitalize }}, &[ {% for trigram in lang.trigrams %} "{{ trigram }}", {% endfor %} ]),
    {% endfor %}
];
{% endfor %}
<|endoftext|>