<commit_before>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use std::str;
use std::io::{Read, Write, ErrorKind};
use std::result::Result;
use std::net::TcpStream;
use std::time::Duration;
use rand::{thread_rng, Rng};

use super::command::ControlCommandResponse;
use super::command::ControlCommand;
use super::command::COMMAND_SIZE;
use APP_CONF;
use LINE_FEED;
use cache::route::CacheRoute;
use cache::route::ROUTE_HASH_SIZE;

pub struct ControlHandle;

enum ControlHandleError {
    Closed,
    IncompatibleHasher,
    NotRecognized,
    TimedOut,
    ConnectionAborted,
    Interrupted,
    Unknown,
}

#[derive(PartialEq)]
enum ControlHandleMessageResult {
    Continue,
    Close,
}

const LINE_END_GAP: usize = 1;
const MAX_LINE_SIZE: usize = COMMAND_SIZE + ROUTE_HASH_SIZE + LINE_END_GAP + 1;
const HASH_VALUE_SIZE: usize = 10;
const HASH_RESULT_SIZE: usize = 7 + ROUTE_HASH_SIZE + LINE_END_GAP + 1;
const SHARD_DEFAULT: ControlShard = 0;
const TCP_TIMEOUT_NON_ESTABLISHED: u64 = 20;

static BUFFER_LINE_SEPARATOR: u8 = '\n' as u8;

pub type ControlShard = u8;

lazy_static! {
    static ref CONNECTED_BANNER: String = format!("CONNECTED <{} v{}>",
        env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
}

impl ControlHandleError {
    pub fn to_str(&self) -> &'static str {
        match *self {
            ControlHandleError::Closed => "closed",
            ControlHandleError::IncompatibleHasher => "incompatible_hasher",
            ControlHandleError::NotRecognized => "not_recognized",
            ControlHandleError::TimedOut => "timed_out",
            ControlHandleError::ConnectionAborted => "connection_aborted",
            ControlHandleError::Interrupted => "interrupted",
            ControlHandleError::Unknown => "unknown",
        }
    }
}

impl ControlHandle {
    pub fn client(mut stream: TcpStream) {
        // Configure stream (non-established)
        ControlHandle::configure_stream(&stream, false);

        // Send connected banner
        write!(stream, "{}{}", *CONNECTED_BANNER, LINE_FEED).expect("write failed");

        // Ensure client hasher is compatible
        match Self::ensure_hasher(&stream) {
            Ok(_) => {
                // Configure stream (established)
                ControlHandle::configure_stream(&stream, true);

                // Send started acknowledgement
                write!(stream, "STARTED{}", LINE_FEED).expect("write failed");

                // Select default shard
                let mut shard = SHARD_DEFAULT;

                // Initialize packet buffer
                let mut buffer = Vec::new();

                // Wait for incoming messages
                'handler: loop {
                    let mut read = [0; MAX_LINE_SIZE];

                    match stream.read(&mut read) {
                        Ok(n) => {
                            // Should close?
                            if n == 0 {
                                break;
                            }

                            // Buffer chunk
                            buffer.extend_from_slice(&read[0..n]);

                            // Should handle this chunk? (terminated)
                            if buffer[buffer.len() - 1] == BUFFER_LINE_SEPARATOR {
                                {
                                    // Handle all buffered chunks as lines
                                    let buffer_split =
                                        buffer.split(|value| value == &BUFFER_LINE_SEPARATOR);

                                    for line in buffer_split {
                                        if line.len() > 0 {
                                            if Self::on_message(&mut shard, &stream, line) ==
                                                ControlHandleMessageResult::Close
                                            {
                                                // Should close?
                                                break 'handler;
                                            }
                                        }
                                    }
                                }

                                // Reset buffer
                                buffer.clear();
                            }
                        }
                        Err(err) => {
                            info!("closing control thread with traceback: {}", err);

                            panic!("closing control channel");
                        }
                    }
                }
            }
            Err(err) => {
                write!(stream, "ENDED {}{}", err.to_str(), LINE_FEED).expect("write failed");
            }
        }
    }

    fn configure_stream(stream: &TcpStream, is_established: bool) {
        let tcp_timeout = if is_established == true {
            APP_CONF.control.tcp_timeout
        } else {
            TCP_TIMEOUT_NON_ESTABLISHED
        };

        assert!(stream.set_nodelay(true).is_ok());

        assert!(
            stream
                .set_read_timeout(Some(Duration::new(tcp_timeout, 0)))
                .is_ok()
        );
        assert!(
            stream
                .set_write_timeout(Some(Duration::new(tcp_timeout, 0)))
                .is_ok()
        );
    }

    fn ensure_hasher(mut stream: &TcpStream) -> Result<Option<()>, ControlHandleError> {
        let test_value: String = thread_rng()
            .gen_ascii_chars()
            .take(HASH_VALUE_SIZE)
            .collect();
        let test_hash = CacheRoute::hash(test_value.as_str());

        write!(stream, "HASHREQ {}{}", test_value, LINE_FEED).expect("write failed");

        debug!(
            "sent hasher request: {} and expecting hash: {}",
            test_value,
            test_hash
        );

        loop {
            let mut read = [0; HASH_RESULT_SIZE];

            match stream.read(&mut read) {
                Ok(n) => {
                    if n == 0 {
                        return Err(ControlHandleError::Closed);
                    }

                    let mut parts = str::from_utf8(&read[0..n]).unwrap_or("").split_whitespace();

                    if parts.next().unwrap_or("") == "HASHRES" {
                        let res_hash = parts.next().unwrap_or("");

                        debug!(
                            "got hasher response: {} and expecting: {}",
                            res_hash,
                            test_hash
                        );

                        // Validate hash
                        if res_hash.is_empty() == false && res_hash == test_hash {
                            return Ok(None);
                        }

                        return Err(ControlHandleError::IncompatibleHasher);
                    }

                    return Err(ControlHandleError::NotRecognized);
                }
                Err(err) => {
                    let err_reason = match err.kind() {
                        ErrorKind::TimedOut => ControlHandleError::TimedOut,
                        ErrorKind::ConnectionAborted => ControlHandleError::ConnectionAborted,
                        ErrorKind::Interrupted => ControlHandleError::Interrupted,
                        _ => ControlHandleError::Unknown,
                    };

                    return Err(err_reason);
                }
            }
        }
    }

    fn on_message(
        shard: &mut ControlShard,
        mut stream: &TcpStream,
        message_slice: &[u8],
    ) -> ControlHandleMessageResult {
        let message = str::from_utf8(message_slice).unwrap_or("");

        debug!("got control message on shard {}: {}", shard, message);

        let mut result = ControlHandleMessageResult::Continue;

        let response = match Self::handle_message(shard, &message) {
            Ok(resp) => {
                match resp {
                    ControlCommandResponse::Ok |
                    ControlCommandResponse::Pong |
                    ControlCommandResponse::Ended |
                    ControlCommandResponse::Nil |
                    ControlCommandResponse::Void => {
                        if resp == ControlCommandResponse::Ended {
                            result = ControlHandleMessageResult::Close;
                        }
                        resp.to_str()
                    }
                    _ => ControlCommandResponse::Err.to_str(),
                }
            }
            _ => ControlCommandResponse::Err.to_str(),
        };

        if response.is_empty() == false {
            write!(stream, "{}{}", response, LINE_FEED).expect("write failed");

            debug!("wrote response: {}", response);
        }

        return result;
    }

    fn handle_message(
        shard: &mut ControlShard,
        message: &str,
    ) -> Result<ControlCommandResponse, Option<()>> {
        let mut parts = message.split_whitespace();
        let command = parts.next().unwrap_or("");

        debug!("will dispatch command: {}", command);

        match command {
            "" => Ok(ControlCommandResponse::Void),
            "FLUSHB" => ControlCommand::dispatch_flush_bucket(shard, parts),
            "FLUSHA" => ControlCommand::dispatch_flush_auth(shard, parts),
            "PING" => ControlCommand::dispatch_ping(),
            "SHARD" => ControlCommand::dispatch_shard(shard, parts),
            "QUIT" => ControlCommand::dispatch_quit(),
            _ => Ok(ControlCommandResponse::Nil),
        }
    }
}
<commit_msg>Improve<commit_after>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use std::str;
use std::io::{Read, Write, ErrorKind};
use std::result::Result;
use std::net::TcpStream;
use std::time::Duration;
use rand::{thread_rng, Rng};

use super::command::ControlCommandResponse;
use super::command::ControlCommand;
use super::command::COMMAND_SIZE;
use APP_CONF;
use LINE_FEED;
use cache::route::CacheRoute;
use cache::route::ROUTE_HASH_SIZE;

pub struct ControlHandle;

enum ControlHandleError {
    Closed,
    IncompatibleHasher,
    NotRecognized,
    TimedOut,
    ConnectionAborted,
    Interrupted,
    Unknown,
}

#[derive(PartialEq)]
enum ControlHandleMessageResult {
    Continue,
    Close,
}

const LINE_END_GAP: usize = 1;
const MAX_LINE_SIZE: usize = COMMAND_SIZE + ROUTE_HASH_SIZE + LINE_END_GAP + 1;
const HASH_VALUE_SIZE: usize = 10;
const HASH_RESULT_SIZE: usize = 7 + ROUTE_HASH_SIZE + LINE_END_GAP + 1;
const SHARD_DEFAULT: ControlShard = 0;
const TCP_TIMEOUT_NON_ESTABLISHED: u64 = 20;

static BUFFER_LINE_SEPARATOR: u8 = '\n' as u8;

pub type ControlShard = u8;

lazy_static! {
    static ref CONNECTED_BANNER: String = format!("CONNECTED <{} v{}>",
        env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
}

impl ControlHandleError {
    pub fn to_str(&self) -> &'static str {
        match *self {
            ControlHandleError::Closed => "closed",
            ControlHandleError::IncompatibleHasher => "incompatible_hasher",
            ControlHandleError::NotRecognized => "not_recognized",
            ControlHandleError::TimedOut => "timed_out",
            ControlHandleError::ConnectionAborted => "connection_aborted",
            ControlHandleError::Interrupted => "interrupted",
            ControlHandleError::Unknown => "unknown",
        }
    }
}

impl ControlHandle {
    pub fn client(mut stream: TcpStream) {
        // Configure stream (non-established)
        ControlHandle::configure_stream(&stream, false);

        // Send connected banner
        write!(stream, "{}{}", *CONNECTED_BANNER, LINE_FEED).expect("write failed");

        // Ensure client hasher is compatible
        match Self::ensure_hasher(&stream) {
            Ok(_) => {
                // Configure stream (established)
                ControlHandle::configure_stream(&stream, true);

                // Send started acknowledgement
                write!(stream, "STARTED{}", LINE_FEED).expect("write failed");

                // Select default shard
                let mut shard = SHARD_DEFAULT;

                // Initialize packet buffer
                let mut buffer = Vec::new();

                // Wait for incoming messages
                'handler: loop {
                    let mut read = [0; MAX_LINE_SIZE];

                    match stream.read(&mut read) {
                        Ok(n) => {
                            // Should close?
                            if n == 0 {
                                break;
                            }

                            // Buffer chunk
                            buffer.extend_from_slice(&read[0..n]);

                            // Should handle this chunk? (terminated)
                            if buffer[buffer.len() - 1] == BUFFER_LINE_SEPARATOR {
                                {
                                    // Handle all buffered chunks as lines
                                    let buffer_split =
                                        buffer.split(|value| value == &BUFFER_LINE_SEPARATOR);

                                    for line in buffer_split {
                                        if line.is_empty() == false {
                                            if Self::on_message(&mut shard, &stream, line) ==
                                                ControlHandleMessageResult::Close
                                            {
                                                // Should close?
                                                break 'handler;
                                            }
                                        }
                                    }
                                }

                                // Reset buffer
                                buffer.clear();
                            }
                        }
                        Err(err) => {
                            info!("closing control thread with traceback: {}", err);

                            panic!("closing control channel");
                        }
                    }
                }
            }
            Err(err) => {
                write!(stream, "ENDED {}{}", err.to_str(), LINE_FEED).expect("write failed");
            }
        }
    }

    fn configure_stream(stream: &TcpStream, is_established: bool) {
        let tcp_timeout = if is_established == true {
            APP_CONF.control.tcp_timeout
        } else {
            TCP_TIMEOUT_NON_ESTABLISHED
        };

        assert!(stream.set_nodelay(true).is_ok());

        assert!(
            stream
                .set_read_timeout(Some(Duration::new(tcp_timeout, 0)))
                .is_ok()
        );
        assert!(
            stream
                .set_write_timeout(Some(Duration::new(tcp_timeout, 0)))
                .is_ok()
        );
    }

    fn ensure_hasher(mut stream: &TcpStream) -> Result<Option<()>, ControlHandleError> {
        let test_value: String = thread_rng()
            .gen_ascii_chars()
            .take(HASH_VALUE_SIZE)
            .collect();
        let test_hash = CacheRoute::hash(test_value.as_str());

        write!(stream, "HASHREQ {}{}", test_value, LINE_FEED).expect("write failed");

        debug!(
            "sent hasher request: {} and expecting hash: {}",
            test_value,
            test_hash
        );

        loop {
            let mut read = [0; HASH_RESULT_SIZE];

            match stream.read(&mut read) {
                Ok(n) => {
                    if n == 0 {
                        return Err(ControlHandleError::Closed);
                    }

                    let mut parts = str::from_utf8(&read[0..n]).unwrap_or("").split_whitespace();

                    if parts.next().unwrap_or("") == "HASHRES" {
                        let res_hash = parts.next().unwrap_or("");

                        debug!(
                            "got hasher response: {} and expecting: {}",
                            res_hash,
                            test_hash
                        );

                        // Validate hash
                        if res_hash.is_empty() == false && res_hash == test_hash {
                            return Ok(None);
                        }

                        return Err(ControlHandleError::IncompatibleHasher);
                    }

                    return Err(ControlHandleError::NotRecognized);
                }
                Err(err) => {
                    let err_reason = match err.kind() {
                        ErrorKind::TimedOut => ControlHandleError::TimedOut,
                        ErrorKind::ConnectionAborted => ControlHandleError::ConnectionAborted,
                        ErrorKind::Interrupted => ControlHandleError::Interrupted,
                        _ => ControlHandleError::Unknown,
                    };

                    return Err(err_reason);
                }
            }
        }
    }

    fn on_message(
        shard: &mut ControlShard,
        mut stream: &TcpStream,
        message_slice: &[u8],
    ) -> ControlHandleMessageResult {
        let message = str::from_utf8(message_slice).unwrap_or("");

        debug!("got control message on shard {}: {}", shard, message);

        let mut result = ControlHandleMessageResult::Continue;

        let response = match Self::handle_message(shard, &message) {
            Ok(resp) => {
                match resp {
                    ControlCommandResponse::Ok |
                    ControlCommandResponse::Pong |
                    ControlCommandResponse::Ended |
                    ControlCommandResponse::Nil |
                    ControlCommandResponse::Void => {
                        if resp == ControlCommandResponse::Ended {
                            result = ControlHandleMessageResult::Close;
                        }
                        resp.to_str()
                    }
                    _ => ControlCommandResponse::Err.to_str(),
                }
            }
            _ => ControlCommandResponse::Err.to_str(),
        };

        if response.is_empty() == false {
            write!(stream, "{}{}", response, LINE_FEED).expect("write failed");

            debug!("wrote response: {}", response);
        }

        return result;
    }

    fn handle_message(
        shard: &mut ControlShard,
        message: &str,
    ) -> Result<ControlCommandResponse, Option<()>> {
        let mut parts = message.split_whitespace();
        let command = parts.next().unwrap_or("");

        debug!("will dispatch command: {}", command);

        match command {
            "" => Ok(ControlCommandResponse::Void),
            "FLUSHB" => ControlCommand::dispatch_flush_bucket(shard, parts),
            "FLUSHA" => ControlCommand::dispatch_flush_auth(shard, parts),
            "PING" => ControlCommand::dispatch_ping(),
            "SHARD" => ControlCommand::dispatch_shard(shard, parts),
            "QUIT" => ControlCommand::dispatch_quit(),
            _ => Ok(ControlCommandResponse::Nil),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::Hash;

use crate::colour::Colour;
use crate::grid::Grid;

// Represent the state of the game as a graph.  One node for each cell.  There is an edge between
// two cells if and only if they are the same colour and have a distance of 1 according to the
// Manhattan metric.
//
// The game has only one real operation (i.e. not counting level generation): Switching the colour
// of the connected component containing the top left cell to whichever colour the clicked cell
// has.  This results in said component growing by every component it neighbours which has its new
// colour.  In essence, the aim of the gaim is to join connected components until only one is
// left.  Fewer clicks is better.
//
// So, we represent the game state as a collection of connected components, each of which has a
// colour, a list of coordinates of the constituent cells, and a list of it s neighbours.  As we
// do all our operations on connected components, we actually work not with the graph of
// neighbouring cells but with a graph of connected components.  The graph of connected components
// is made up of one node representing each connected component and an edge between any two
// connected components that have cells which share an edge, i.e. two cells which would have been
// connected by an edge in the original graph if they had had the same colour (which they did
// not).

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Position {
    pub row: usize,
    pub column: usize,
}

#[derive(Debug, Clone)]
pub struct ConnectedComponent {
    id: usize,
    pub colour: Colour,
    pub cells: HashSet<Position>,
}

impl PartialEq for ConnectedComponent {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for ConnectedComponent {}

impl Hash for ConnectedComponent {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

#[derive(Debug)]
pub struct Graph {
    pub components: HashMap<usize, ConnectedComponent>,
    pub neighbours: HashMap<usize, HashSet<usize>>,
}

fn find_connected_components(grid: &Grid) -> Vec<(ConnectedComponent, HashSet<usize>)> {
    let rows = grid.number_of_rows;
    let columns = grid.number_of_columns;

    let mut remaining_cells: HashSet<usize> = (0..grid.cells.len()).into_iter().collect();

    let mut components = vec![];
    let mut counter = 0;

    // Take any element to land in any component.
    while let Some(&start_cell_index) = remaining_cells.iter().next() {
        let mut visited = HashSet::new();
        let mut neighbours = HashSet::new();
        let mut queue = VecDeque::new();

        queue.push_back(start_cell_index);

        // Gather all cells in the connected component of start_cell_index.
        while let Some(i) = queue.pop_front() {
            visited.insert(i);
            remaining_cells.remove(&i);

            let mut neighbouring_cells = vec![];
            if i % columns > 0 {
                neighbouring_cells.push(i - 1);
            }
            if i % columns < columns - 1 {
                neighbouring_cells.push(i + 1);
            }
            if i >= columns {
                neighbouring_cells.push(i - columns)
            }
            if i < (rows - 1) * columns {
                neighbouring_cells.push(i + columns)
            }

            for neighbour in neighbouring_cells {
                if visited.contains(&neighbour) {
                    continue;
                }

                if grid.cells[i] == grid.cells[neighbour] {
                    queue.push_back(neighbour);
                } else {
                    neighbours.insert(neighbour);
                }
            }
        }

        let cells = visited
            .iter()
            .map(|&i| Position {
                column: i % columns,
                row: i / columns,
            })
            .collect();

        let component = ConnectedComponent {
            id: counter,
            colour: grid.cells[*visited.iter().next().unwrap()],
            cells,
        };
        counter += 1;

        components.push((component, neighbours));
    }

    return components;
}

impl Graph {
    pub fn create(grid: &Grid) -> Self {
        let mut components_and_neighbours = find_connected_components(&grid);
        components_and_neighbours.sort_by_key(|x| x.0.id);

        let mut components = HashMap::with_capacity(components_and_neighbours.len());
        let mut neighbours: HashMap<usize, HashSet<usize>> = HashMap::new();

        for (c, n) in components_and_neighbours.into_iter() {
            let id = c.id;
            components.insert(id, c);
            neighbours.insert(id, n);
        }

        Self {
            components,
            neighbours
        }
    }

    fn to_grid(&self, rows: usize, columns: usize) -> Grid {
        let mut cells = vec![Colour::Red; rows * columns];

        for component in self.neighbours.keys() {
            for position in &self.components[component].cells {
                cells[position.column + position.row * columns] = self.components[component].colour;
            }
        }

        Grid {
            number_of_rows: rows,
            number_of_columns: columns,
            cells,
        }
    }

    pub fn find_component(&self, position: &Position) -> &ConnectedComponent {
        for component in self.neighbours.keys() {
            if self.components[component].cells.contains(&position) {
                return &self.components[component];
            }
        }

        unreachable!()
    }
}

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

    #[test]
    fn should_return_same_grid_as_input() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);
        let reconstituted_grid = graph.to_grid(size, size);

        assert_eq!(reconstituted_grid, grid);
    }

    #[test]
    fn should_have_one_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red; 4],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 1);
    }

    #[test]
    fn should_have_four_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Yellow, Colour::Yellow, Colour::Red],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 4);
    }

    #[test]
    fn should_have_two_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Red, Colour::Yellow, Colour::Yellow],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 2);
    }
}
<commit_msg>Remove unnecessary sorting of temporary vector<commit_after>use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::Hash;

use crate::colour::Colour;
use crate::grid::Grid;

// Represent the state of the game as a graph.  One node for each cell.  There is an edge between
// two cells if and only if they are the same colour and have a distance of 1 according to the
// Manhattan metric.
//
// The game has only one real operation (i.e. not counting level generation): Switching the colour
// of the connected component containing the top left cell to whichever colour the clicked cell
// has.  This results in said component growing by every component it neighbours which has its new
// colour.  In essence, the aim of the gaim is to join connected components until only one is
// left.  Fewer clicks is better.
//
// So, we represent the game state as a collection of connected components, each of which has a
// colour, a list of coordinates of the constituent cells, and a list of it s neighbours.  As we
// do all our operations on connected components, we actually work not with the graph of
// neighbouring cells but with a graph of connected components.  The graph of connected components
// is made up of one node representing each connected component and an edge between any two
// connected components that have cells which share an edge, i.e. two cells which would have been
// connected by an edge in the original graph if they had had the same colour (which they did
// not).

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Position {
    pub row: usize,
    pub column: usize,
}

#[derive(Debug, Clone)]
pub struct ConnectedComponent {
    id: usize,
    pub colour: Colour,
    pub cells: HashSet<Position>,
}

impl PartialEq for ConnectedComponent {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for ConnectedComponent {}

impl Hash for ConnectedComponent {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

#[derive(Debug)]
pub struct Graph {
    pub components: HashMap<usize, ConnectedComponent>,
    pub neighbours: HashMap<usize, HashSet<usize>>,
}

fn find_connected_components(grid: &Grid) -> Vec<(ConnectedComponent, HashSet<usize>)> {
    let rows = grid.number_of_rows;
    let columns = grid.number_of_columns;

    let mut remaining_cells: HashSet<usize> = (0..grid.cells.len()).into_iter().collect();

    let mut components = vec![];
    let mut counter = 0;

    // Take any element to land in any component.
    while let Some(&start_cell_index) = remaining_cells.iter().next() {
        let mut visited = HashSet::new();
        let mut neighbours = HashSet::new();
        let mut queue = VecDeque::new();

        queue.push_back(start_cell_index);

        // Gather all cells in the connected component of start_cell_index.
        while let Some(i) = queue.pop_front() {
            visited.insert(i);
            remaining_cells.remove(&i);

            let mut neighbouring_cells = vec![];
            if i % columns > 0 {
                neighbouring_cells.push(i - 1);
            }
            if i % columns < columns - 1 {
                neighbouring_cells.push(i + 1);
            }
            if i >= columns {
                neighbouring_cells.push(i - columns)
            }
            if i < (rows - 1) * columns {
                neighbouring_cells.push(i + columns)
            }

            for neighbour in neighbouring_cells {
                if visited.contains(&neighbour) {
                    continue;
                }

                if grid.cells[i] == grid.cells[neighbour] {
                    queue.push_back(neighbour);
                } else {
                    neighbours.insert(neighbour);
                }
            }
        }

        let cells = visited
            .iter()
            .map(|&i| Position {
                column: i % columns,
                row: i / columns,
            })
            .collect();

        let component = ConnectedComponent {
            id: counter,
            colour: grid.cells[*visited.iter().next().unwrap()],
            cells,
        };
        counter += 1;

        components.push((component, neighbours));
    }

    return components;
}

impl Graph {
    pub fn create(grid: &Grid) -> Self {
        let components_and_neighbours = find_connected_components(&grid);

        let mut components = HashMap::with_capacity(components_and_neighbours.len());
        let mut neighbours: HashMap<usize, HashSet<usize>> = HashMap::new();

        for (c, n) in components_and_neighbours.into_iter() {
            let id = c.id;
            components.insert(id, c);
            neighbours.insert(id, n);
        }

        Self {
            components,
            neighbours
        }
    }

    fn to_grid(&self, rows: usize, columns: usize) -> Grid {
        let mut cells = vec![Colour::Red; rows * columns];

        for component in self.neighbours.keys() {
            for position in &self.components[component].cells {
                cells[position.column + position.row * columns] = self.components[component].colour;
            }
        }

        Grid {
            number_of_rows: rows,
            number_of_columns: columns,
            cells,
        }
    }

    pub fn find_component(&self, position: &Position) -> &ConnectedComponent {
        for component in self.neighbours.keys() {
            if self.components[component].cells.contains(&position) {
                return &self.components[component];
            }
        }

        unreachable!()
    }
}

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

    #[test]
    fn should_return_same_grid_as_input() {
        let size = 4;
        let grid = Grid::generate(size);

        let graph = Graph::create(&grid);
        let reconstituted_grid = graph.to_grid(size, size);

        assert_eq!(reconstituted_grid, grid);
    }

    #[test]
    fn should_have_one_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red; 4],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 1);
    }

    #[test]
    fn should_have_four_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Yellow, Colour::Yellow, Colour::Red],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 4);
    }

    #[test]
    fn should_have_two_component() {
        let grid = Grid {
            number_of_columns: 2,
            number_of_rows: 2,
            cells: vec![Colour::Red, Colour::Red, Colour::Yellow, Colour::Yellow],
        };

        let graph = Graph::create(&grid);

        assert_eq!(graph.neighbours.len(), 2);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::error;
use std::hash::Hash;
use record::Commands;
use {Command, Error, Stack, Record};

/// A group of either stacks or records.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Group<K: Hash + Eq, V> {
    map: HashMap<K, V>,
    active: Option<K>,
}

impl<K: Hash + Eq, V> Group<K, V> {
    /// Returns a new `Group`.
    #[inline]
    pub fn new() -> Group<K, V> {
        Group {
            map: HashMap::new(),
            active: None,
        }
    }

    /// Returns the number of items in the group.
    #[inline]
    pub fn len(&self) -> usize {
        self.map.len()
    }

    /// Returns `true` if the group is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.map.is_empty()
    }

    /// Inserts an item into the group.
    #[inline]
    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        self.map.insert(k, v)
    }

    /// Removes an item from the group.
    #[inline]
    pub fn remove(&mut self, k: &K) -> Option<V> {
        if self.active.as_ref().map_or(false, |active| active == k) {
            self.set(None);
        }
        self.map.remove(k)
    }

    /// Gets the current active item in the group.
    #[inline]
    pub fn get(&self) -> Option<&V> {
        self.active
            .as_ref()
            .and_then(|active| self.map.get(&active))
    }

    /// Sets the current active item in the group.
    #[inline]
    pub fn set<T: Into<Option<K>>>(&mut self, k: T) -> bool {
        match k.into() {
            Some(ref k) if !self.map.contains_key(k) => false,
            k => {
                self.active = k;
                true
            }
        }
    }
}

impl<K: Hash + Eq, R> Group<K, Stack<R>> {
    /// Calls the [`push`] method on the active `Stack`.
    ///
    /// [`push`]: stack/struct.Stack.html#method.push
    #[inline]
    pub fn push<C>(&mut self, cmd: C) -> Option<Result<(), Error<R>>>
        where C: Command<R> + 'static,
              R: 'static
    {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .map(move |stack| stack.push(cmd))
    }

    /// Calls the [`pop`] method on the active `Stack`.
    ///
    /// [`pop`]: stack/struct.Stack.html#method.pop
    #[inline]
    pub fn pop(&mut self) -> Option<Result<Box<Command<R>>, Error<R>>> {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .and_then(|stack| stack.pop())
    }
}

impl<'a, K: Hash + Eq, R> Group<K, Record<'a, R>> {
    /// Calls the [`push`] method on the active `Record`.
    ///
    /// [`push`]: record/struct.Record.html#method.push
    #[inline]
    pub fn push<C>(&mut self, cmd: C) -> Option<Result<Commands<R>, Error<R>>>
        where C: Command<R> + 'static,
              R: 'static
    {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .map(move |record| record.push(cmd))
    }

    /// Calls the [`redo`] method on the active `Record`.
    ///
    /// [`redo`]: record/struct.Record.html#method.redo
    #[inline]
    pub fn redo(&mut self) -> Option<Result<(), Box<error::Error>>> {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .and_then(|record| record.redo())
    }

    /// Calls the [`undo`] method on the active `Record`.
    ///
    /// [`undo`]: record/struct.Record.html#method.undo
    #[inline]
    pub fn undo(&mut self) -> Option<Result<(), Box<error::Error>>> {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .and_then(|record| record.undo())
    }
}

impl<K: Hash + Eq, V> Default for Group<K, V> {
    #[inline]
    fn default() -> Group<K, V> {
        Group::new()
    }
}
<commit_msg>Remove does not check if k is currently active<commit_after>use std::collections::HashMap;
use std::error;
use std::hash::Hash;
use record::Commands;
use {Command, Error, Stack, Record};

/// A group of either stacks or records.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Group<K: Hash + Eq, V> {
    map: HashMap<K, V>,
    active: Option<K>,
}

impl<K: Hash + Eq, V> Group<K, V> {
    /// Returns a new `Group`.
    #[inline]
    pub fn new() -> Group<K, V> {
        Group {
            map: HashMap::new(),
            active: None,
        }
    }

    /// Returns the number of items in the group.
    #[inline]
    pub fn len(&self) -> usize {
        self.map.len()
    }

    /// Returns `true` if the group is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.map.is_empty()
    }

    /// Inserts an item into the group.
    #[inline]
    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        self.map.insert(k, v)
    }

    /// Removes an item from the group.
    #[inline]
    pub fn remove(&mut self, k: &K) -> Option<V> {
        self.map.remove(k)
    }

    /// Gets the current active item in the group.
    #[inline]
    pub fn get(&self) -> Option<&V> {
        self.active
            .as_ref()
            .and_then(|active| self.map.get(&active))
    }

    /// Sets the current active item in the group.
    #[inline]
    pub fn set<T: Into<Option<K>>>(&mut self, k: T) -> bool {
        match k.into() {
            Some(ref k) if !self.map.contains_key(k) => false,
            k => {
                self.active = k;
                true
            }
        }
    }
}

impl<K: Hash + Eq, R> Group<K, Stack<R>> {
    /// Calls the [`push`] method on the active `Stack`.
    ///
    /// [`push`]: stack/struct.Stack.html#method.push
    #[inline]
    pub fn push<C>(&mut self, cmd: C) -> Option<Result<(), Error<R>>>
        where C: Command<R> + 'static,
              R: 'static
    {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .map(move |stack| stack.push(cmd))
    }

    /// Calls the [`pop`] method on the active `Stack`.
    ///
    /// [`pop`]: stack/struct.Stack.html#method.pop
    #[inline]
    pub fn pop(&mut self) -> Option<Result<Box<Command<R>>, Error<R>>> {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .and_then(|stack| stack.pop())
    }
}

impl<'a, K: Hash + Eq, R> Group<K, Record<'a, R>> {
    /// Calls the [`push`] method on the active `Record`.
    ///
    /// [`push`]: record/struct.Record.html#method.push
    #[inline]
    pub fn push<C>(&mut self, cmd: C) -> Option<Result<Commands<R>, Error<R>>>
        where C: Command<R> + 'static,
              R: 'static
    {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .map(move |record| record.push(cmd))
    }

    /// Calls the [`redo`] method on the active `Record`.
    ///
    /// [`redo`]: record/struct.Record.html#method.redo
    #[inline]
    pub fn redo(&mut self) -> Option<Result<(), Box<error::Error>>> {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .and_then(|record| record.redo())
    }

    /// Calls the [`undo`] method on the active `Record`.
    ///
    /// [`undo`]: record/struct.Record.html#method.undo
    #[inline]
    pub fn undo(&mut self) -> Option<Result<(), Box<error::Error>>> {
        let map = &mut self.map;
        self.active
            .as_ref()
            .and_then(|active| map.get_mut(active))
            .and_then(|record| record.undo())
    }
}

impl<K: Hash + Eq, V> Default for Group<K, V> {
    #[inline]
    fn default() -> Group<K, V> {
        Group::new()
    }
}
<|endoftext|>


<commit_before>//! Module that holds HList data structures and implementations
//!
//! Typically, you would want to use the `hlist!` macro to make it easier
//! for you to use HList.
//!
//! ```
//! # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
//! let h = hlist![1, "hi"];
//! assert_eq!(h.length(), 2);
//! let (a, b) = h.into_tuple2();
//! assert_eq!(a, 1);
//! assert_eq!(b, "hi");
//! # }
//! ```

use std::ops::Add;

/// Typeclass for HList-y behaviour
///
/// An HList is a heterogeneous list, one that is statically typed at compile time. In simple terms,
/// it is just an arbitrarily-nested Tuple2.
pub trait HList: Sized {
    /// Returns the length of a given HList
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    /// let h = hlist![1, "hi"];
    /// assert_eq!(h.length(), 2);
    /// # }
    /// ```
    fn length(&self) -> u32;

    /// Prepends an item to the current HList
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    /// let h1 = hlist![1, "hi"];
    /// let h2 = h1.prepend(true);
    /// let (a, (b, c)) = h2.into_tuple2();
    /// assert_eq!(a, true);
    /// assert_eq!(b, 1);
    /// assert_eq!(c, "hi");
    /// # }
    fn prepend<H>(self, h: H) -> HCons<H, Self> {
        HCons {
            head: h,
            tail: self,
        }
    }
}

/// Represents the right-most end of a heterogeneous list
///
/// Used to begin one:
///
/// ```
/// # use frunk::hlist::*;
///
/// let h = h_cons(1, HNil);
/// let h = h.head;
/// assert_eq!(h, 1);
/// ```
#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct HNil;

impl HList for HNil {
    fn length(&self) -> u32 {
        0
    }
}

/// Represents the most basic non-empty HList. Its value is held in `head`
/// while its tail is another HList.
#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct HCons<H, T> {
    pub head: H,
    pub tail: T,
}

impl<H, T: HList> HList for HCons<H, T> {
    fn length(&self) -> u32 {
        1 + self.tail.length()
    }
}

impl<H, T> HCons<H, T> {
    /// Returns the head of the list and the tail of the list as a tuple2.
    /// The original list is consumed
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    ///
    /// let h = hlist!("hi");
    /// let h = h.head;
    /// assert_eq!(h, "hi");
    /// # }
    /// ```
    pub fn pop(self) -> (H, T) {
        (self.head, self.tail)
    }
}

/// Takes an element and an Hlist and returns another one with
/// the element prepended to the original list. The original list
/// is consumed
///
/// ```
/// # use frunk::hlist::*;
///
/// let h_list = h_cons("what", h_cons(1.23f32, HNil));
/// let (h1, h2) = h_list.into_tuple2();
/// assert_eq!(h1, "what");
/// assert_eq!(h2, 1.23f32);
/// ```
pub fn h_cons<H, T: HList>(h: H, tail: T) -> HCons<H, T> {
    tail.prepend(h)
}

/// Returns an `HList` based on the values passed in.
///
/// Helps to avoid having to write nested `HCons`.
///
/// ```
/// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
///
/// let h = hlist![13.5f32, "hello", Some(41)];
/// let (h1, (h2, h3)) = h.into_tuple2();
/// assert_eq!(h1, 13.5f32);
/// assert_eq!(h2, "hello");
/// assert_eq!(h3, Some(41))
/// # }
/// ```
#[macro_export]
macro_rules! hlist {

    // Nothing
    () => { $crate::hlist::HNil };

    // Just a single item
    ($single: expr) => {
        $crate::hlist::HCons { head: $single, tail: HNil }
    };

    ($first: expr, $( $repeated: expr ), +) => {
        $crate::hlist::HCons { head: $first, tail: hlist!($($repeated), *)}
    };

}

impl<RHS> Add<RHS> for HNil
    where RHS: HList
{
    type Output = RHS;

    fn add(self, rhs: RHS) -> RHS {
        rhs
    }
}

impl<H, T, RHS> Add<RHS> for HCons<H, T>
    where T: Add<RHS>,
          RHS: HList
{
    type Output = HCons<H, <T as Add<RHS>>::Output>;

    fn add(self, rhs: RHS) -> Self::Output {
        HCons {
            head: self.head,
            tail: self.tail + rhs,
        }
    }
}

/// Trait for things that can be turned into a Tuple 2 (pair)
pub trait IntoTuple2 {
    /// The 0 element in the output tuple
    type HeadType;

    /// The 1 element in the output tuple
    type TailOutput;

    /// Turns an HList into nested Tuple2s, which are less troublesome to pattern match
    /// and have a nicer type signature.
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    /// let h = hlist![1, "hello", true, 42f32];
    ///
    /// // We now have a much nicer pattern matching experience
    /// let (first,(second,(third, fourth))) = h.into_tuple2();
    ///
    /// assert_eq!(first ,       1);
    /// assert_eq!(second, "hello");
    /// assert_eq!(third ,    true);
    /// assert_eq!(fourth,   42f32);
    /// # }
    /// ```
    fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput);
}

impl<T1, T2> IntoTuple2 for HCons<T1, HCons<T2, HNil>> {
    type HeadType = T1;
    type TailOutput = T2;

    fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
        (self.head, self.tail.head)
    }
}

impl<T, Tail> IntoTuple2 for HCons<T, Tail>
    where Tail: IntoTuple2
{
    type HeadType = T;
    type TailOutput = (<Tail as IntoTuple2>::HeadType, <Tail as IntoTuple2>::TailOutput);

    fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
        (self.head, self.tail.into_tuple2())
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_hcons() {
        let hlist1 = h_cons(1, HNil);
        let (h, _) = hlist1.pop();
        assert_eq!(h, 1);

        let hlist2 = h_cons("hello", h_cons(1, HNil));
        let (h2, tail2) = hlist2.pop();
        let (h1, _) = tail2.pop();
        assert_eq!(h2, "hello");
        assert_eq!(h1, 1);
    }

    struct HasHList<T: HList>(T);

    #[test]
    fn test_contained_list() {
        let c = HasHList(h_cons(1, HNil));
        let retrieved = c.0;
        assert_eq!(retrieved.length(), 1);
        let new_list = h_cons(2, retrieved);
        assert_eq!(new_list.length(), 2);
    }

    #[test]
    fn test_macro() {
        assert_eq!(hlist![], HNil);
        let h = hlist![1, "2", 3];
        let (h1, tail1) = h.pop();
        assert_eq!(h1, 1);
        assert_eq!(tail1, hlist!["2", 3]);
        let (h2, tail2) = tail1.pop();
        assert_eq!(h2, "2");
        assert_eq!(tail2, hlist![3]);
        let (h3, tail3) = tail2.pop();
        assert_eq!(h3, 3);
        assert_eq!(tail3, HNil);
    }

    #[test]
    fn test_add() {
        let h1 = hlist![true, "hi"];
        let h2 = hlist![1, 32f32];
        let combined = h1 + h2;
        assert_eq!(combined, hlist![true, "hi", 1, 32f32])
    }

}
<commit_msg>Fix example for HList#pop<commit_after>//! Module that holds HList data structures and implementations
//!
//! Typically, you would want to use the `hlist!` macro to make it easier
//! for you to use HList.
//!
//! ```
//! # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
//! let h = hlist![1, "hi"];
//! assert_eq!(h.length(), 2);
//! let (a, b) = h.into_tuple2();
//! assert_eq!(a, 1);
//! assert_eq!(b, "hi");
//! # }
//! ```

use std::ops::Add;

/// Typeclass for HList-y behaviour
///
/// An HList is a heterogeneous list, one that is statically typed at compile time. In simple terms,
/// it is just an arbitrarily-nested Tuple2.
pub trait HList: Sized {
    /// Returns the length of a given HList
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    /// let h = hlist![1, "hi"];
    /// assert_eq!(h.length(), 2);
    /// # }
    /// ```
    fn length(&self) -> u32;

    /// Prepends an item to the current HList
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    /// let h1 = hlist![1, "hi"];
    /// let h2 = h1.prepend(true);
    /// let (a, (b, c)) = h2.into_tuple2();
    /// assert_eq!(a, true);
    /// assert_eq!(b, 1);
    /// assert_eq!(c, "hi");
    /// # }
    fn prepend<H>(self, h: H) -> HCons<H, Self> {
        HCons {
            head: h,
            tail: self,
        }
    }
}

/// Represents the right-most end of a heterogeneous list
///
/// Used to begin one:
///
/// ```
/// # use frunk::hlist::*;
///
/// let h = h_cons(1, HNil);
/// let h = h.head;
/// assert_eq!(h, 1);
/// ```
#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct HNil;

impl HList for HNil {
    fn length(&self) -> u32 {
        0
    }
}

/// Represents the most basic non-empty HList. Its value is held in `head`
/// while its tail is another HList.
#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct HCons<H, T> {
    pub head: H,
    pub tail: T,
}

impl<H, T: HList> HList for HCons<H, T> {
    fn length(&self) -> u32 {
        1 + self.tail.length()
    }
}

impl<H, T> HCons<H, T> {
    /// Returns the head of the list and the tail of the list as a tuple2.
    /// The original list is consumed
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    ///
    /// let h = hlist!("hi");
    /// let (h, tail) = h.pop();
    /// assert_eq!(h, "hi");
    /// assert_eq!(tail, HNil);
    /// # }
    /// ```
    pub fn pop(self) -> (H, T) {
        (self.head, self.tail)
    }
}

/// Takes an element and an Hlist and returns another one with
/// the element prepended to the original list. The original list
/// is consumed
///
/// ```
/// # use frunk::hlist::*;
///
/// let h_list = h_cons("what", h_cons(1.23f32, HNil));
/// let (h1, h2) = h_list.into_tuple2();
/// assert_eq!(h1, "what");
/// assert_eq!(h2, 1.23f32);
/// ```
pub fn h_cons<H, T: HList>(h: H, tail: T) -> HCons<H, T> {
    tail.prepend(h)
}

/// Returns an `HList` based on the values passed in.
///
/// Helps to avoid having to write nested `HCons`.
///
/// ```
/// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
///
/// let h = hlist![13.5f32, "hello", Some(41)];
/// let (h1, (h2, h3)) = h.into_tuple2();
/// assert_eq!(h1, 13.5f32);
/// assert_eq!(h2, "hello");
/// assert_eq!(h3, Some(41))
/// # }
/// ```
#[macro_export]
macro_rules! hlist {

    // Nothing
    () => { $crate::hlist::HNil };

    // Just a single item
    ($single: expr) => {
        $crate::hlist::HCons { head: $single, tail: HNil }
    };

    ($first: expr, $( $repeated: expr ), +) => {
        $crate::hlist::HCons { head: $first, tail: hlist!($($repeated), *)}
    };

}

impl<RHS> Add<RHS> for HNil
    where RHS: HList
{
    type Output = RHS;

    fn add(self, rhs: RHS) -> RHS {
        rhs
    }
}

impl<H, T, RHS> Add<RHS> for HCons<H, T>
    where T: Add<RHS>,
          RHS: HList
{
    type Output = HCons<H, <T as Add<RHS>>::Output>;

    fn add(self, rhs: RHS) -> Self::Output {
        HCons {
            head: self.head,
            tail: self.tail + rhs,
        }
    }
}

/// Trait for things that can be turned into a Tuple 2 (pair)
pub trait IntoTuple2 {
    /// The 0 element in the output tuple
    type HeadType;

    /// The 1 element in the output tuple
    type TailOutput;

    /// Turns an HList into nested Tuple2s, which are less troublesome to pattern match
    /// and have a nicer type signature.
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; fn main() {
    /// let h = hlist![1, "hello", true, 42f32];
    ///
    /// // We now have a much nicer pattern matching experience
    /// let (first,(second,(third, fourth))) = h.into_tuple2();
    ///
    /// assert_eq!(first ,       1);
    /// assert_eq!(second, "hello");
    /// assert_eq!(third ,    true);
    /// assert_eq!(fourth,   42f32);
    /// # }
    /// ```
    fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput);
}

impl<T1, T2> IntoTuple2 for HCons<T1, HCons<T2, HNil>> {
    type HeadType = T1;
    type TailOutput = T2;

    fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
        (self.head, self.tail.head)
    }
}

impl<T, Tail> IntoTuple2 for HCons<T, Tail>
    where Tail: IntoTuple2
{
    type HeadType = T;
    type TailOutput = (<Tail as IntoTuple2>::HeadType, <Tail as IntoTuple2>::TailOutput);

    fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
        (self.head, self.tail.into_tuple2())
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_hcons() {
        let hlist1 = h_cons(1, HNil);
        let (h, _) = hlist1.pop();
        assert_eq!(h, 1);

        let hlist2 = h_cons("hello", h_cons(1, HNil));
        let (h2, tail2) = hlist2.pop();
        let (h1, _) = tail2.pop();
        assert_eq!(h2, "hello");
        assert_eq!(h1, 1);
    }

    struct HasHList<T: HList>(T);

    #[test]
    fn test_contained_list() {
        let c = HasHList(h_cons(1, HNil));
        let retrieved = c.0;
        assert_eq!(retrieved.length(), 1);
        let new_list = h_cons(2, retrieved);
        assert_eq!(new_list.length(), 2);
    }

    #[test]
    fn test_macro() {
        assert_eq!(hlist![], HNil);
        let h = hlist![1, "2", 3];
        let (h1, tail1) = h.pop();
        assert_eq!(h1, 1);
        assert_eq!(tail1, hlist!["2", 3]);
        let (h2, tail2) = tail1.pop();
        assert_eq!(h2, "2");
        assert_eq!(tail2, hlist![3]);
        let (h3, tail3) = tail2.pop();
        assert_eq!(h3, 3);
        assert_eq!(tail3, HNil);
    }

    #[test]
    fn test_add() {
        let h1 = hlist![true, "hi"];
        let h2 = hlist![1, 32f32];
        let combined = h1 + h2;
        assert_eq!(combined, hlist![true, "hi", 1, 32f32])
    }

}
<|endoftext|>


<commit_before>//! The parser module is responsible for parsing FITS files.

use std::str;
use std::str::FromStr;
use super::types::{Fits, PrimaryHeader, KeywordRecord, Keyword, BlankRecord};

named!(fits<&[u8], Fits>,
       do_parse!(
           ph: primary_header >>
               many0!(take!(2880)) >>
               (Fits::new(ph))
       ));

named!(primary_header<&[u8], PrimaryHeader>,
       do_parse!(
           records: many0!(keyword_record) >>
               end_record >>
               many0!(blank_record) >>
               (PrimaryHeader::new(records))
       ));

named!(keyword_record<&[u8], KeywordRecord>,
       do_parse!(
           key: keyword  >>
               tag!("=") >>
               take!(71) >>
               (KeywordRecord::new(key))
       ));

named!(keyword<&[u8], Keyword>,
       map_res!(
           map_res!(
               take!(8),
               str::from_utf8),
           Keyword::from_str
       ));

named!(end_record<&[u8], Keyword>,
       map!(
           pair!(tag!("END"), count!(tag!(" "), 77)),
           |_| { Keyword::END }
       ));

named!(blank_record<&[u8], BlankRecord>,
       map!(
           count!(tag!(" "), 80),
           |_| { BlankRecord }
       ));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::super::types::{Fits, PrimaryHeader, KeywordRecord, Keyword, BlankRecord};
    use super::{fits, primary_header, keyword_record, keyword, end_record, blank_record};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, f) => {
                assert_eq!(f, Fits::new(long_cadence_header()));
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn primary_header_should_parse_a_primary_header(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = primary_header(&data[0..(2*2880)]);

        match result {
            IResult::Done(_, h) => assert_eq!(h, long_cadence_header()),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    fn long_cadence_header() -> PrimaryHeader {
        PrimaryHeader::new(vec!(
            KeywordRecord::new(Keyword::SIMPLE),
            KeywordRecord::new(Keyword::BITPIX),
            KeywordRecord::new(Keyword::NAXIS),
            KeywordRecord::new(Keyword::EXTEND),
            KeywordRecord::new(Keyword::NEXTEND),
            KeywordRecord::new(Keyword::EXTNAME),
            KeywordRecord::new(Keyword::EXTVER),
            KeywordRecord::new(Keyword::ORIGIN),
            KeywordRecord::new(Keyword::DATE),
            KeywordRecord::new(Keyword::CREATOR),
            KeywordRecord::new(Keyword::PROCVER),
            KeywordRecord::new(Keyword::FILEVER),
            KeywordRecord::new(Keyword::TIMVERSN),
            KeywordRecord::new(Keyword::TELESCOP),
            KeywordRecord::new(Keyword::INSTRUME),
            KeywordRecord::new(Keyword::OBJECT),
            KeywordRecord::new(Keyword::KEPLERID),
            KeywordRecord::new(Keyword::CHANNEL),
            KeywordRecord::new(Keyword::MODULE),
            KeywordRecord::new(Keyword::OUTPUT),
            KeywordRecord::new(Keyword::CAMPAIGN),
            KeywordRecord::new(Keyword::DATA_REL),
            KeywordRecord::new(Keyword::OBSMODE),
            KeywordRecord::new(Keyword::MISSION),
            KeywordRecord::new(Keyword::TTABLEID),
            KeywordRecord::new(Keyword::RADESYS),
            KeywordRecord::new(Keyword::RA_OBJ),
            KeywordRecord::new(Keyword::DEC_OBJ),
            KeywordRecord::new(Keyword::EQUINOX),
            KeywordRecord::new(Keyword::PMRA),
            KeywordRecord::new(Keyword::PMDEC),
            KeywordRecord::new(Keyword::PMTOTAL),
            KeywordRecord::new(Keyword::PARALLAX),
            KeywordRecord::new(Keyword::GLON),
            KeywordRecord::new(Keyword::GLAT),
            KeywordRecord::new(Keyword::GMAG),
            KeywordRecord::new(Keyword::RMAG),
            KeywordRecord::new(Keyword::IMAG),
            KeywordRecord::new(Keyword::ZMAG),
            KeywordRecord::new(Keyword::JMAG),
            KeywordRecord::new(Keyword::HMAG),
            KeywordRecord::new(Keyword::KMAG),
            KeywordRecord::new(Keyword::KEPMAG),
            KeywordRecord::new(Keyword::GRCOLOR),
            KeywordRecord::new(Keyword::JKCOLOR),
            KeywordRecord::new(Keyword::GKCOLOR),
            KeywordRecord::new(Keyword::TEFF),
            KeywordRecord::new(Keyword::LOGG),
            KeywordRecord::new(Keyword::FEH),
            KeywordRecord::new(Keyword::EBMINUSV),
            KeywordRecord::new(Keyword::AV),
            KeywordRecord::new(Keyword::RADIUS),
            KeywordRecord::new(Keyword::TMINDEX),
            KeywordRecord::new(Keyword::CHECKSUM),
            KeywordRecord::new(Keyword::DATASUM),
        ))
    }

    #[test]
    fn keyword_record_should_parse_a_keyword_record(){
        let data = "OBJECT  = 'EPIC 200164267'     / string version of target id                    "
            .as_bytes();

        let result = keyword_record(data);

        match result {
            IResult::Done(_,k) => assert_eq!(k, KeywordRecord::new(Keyword::OBJECT)),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn keyword_should_parse_a_keyword(){
        let data = "OBJECT  "
            .as_bytes();

        let result = keyword(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::OBJECT),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_record_should_parse_an_END_record(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_record(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::END),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_record_should_parse_a_BLANK_record(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_record(data);

        match result {
            IResult::Done(_, record) => assert_eq!(record, BlankRecord),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<commit_msg>Use flatmap to extract 80 bytes for the end record<commit_after>//! The parser module is responsible for parsing FITS files.

use std::str;
use std::str::FromStr;
use super::types::{Fits, PrimaryHeader, KeywordRecord, Keyword, BlankRecord};

named!(fits<&[u8], Fits>,
       do_parse!(
           ph: primary_header >>
               many0!(take!(2880)) >>
               (Fits::new(ph))
       ));

named!(primary_header<&[u8], PrimaryHeader>,
       do_parse!(
           records: many0!(keyword_record) >>
               end_record >>
               many0!(blank_record) >>
               (PrimaryHeader::new(records))
       ));

named!(keyword_record<&[u8], KeywordRecord>,
       do_parse!(
           key: keyword  >>
               tag!("=") >>
               take!(71) >>
               (KeywordRecord::new(key))
       ));

named!(keyword<&[u8], Keyword>,
       map_res!(
           map_res!(
               take!(8),
               str::from_utf8),
           Keyword::from_str
       ));

named!(end_record<&[u8], Keyword>,
       map!(
           flat_map!(
               take!(80),
               pair!(tag!("END"), many0!(tag!(" ")))
           ),
           |_| { Keyword::END }
       ));

named!(blank_record<&[u8], BlankRecord>,
       map!(
           count!(tag!(" "), 80),
           |_| { BlankRecord }
       ));

#[cfg(test)]
mod tests {
    use nom::{IResult};
    use super::super::types::{Fits, PrimaryHeader, KeywordRecord, Keyword, BlankRecord};
    use super::{fits, primary_header, keyword_record, keyword, end_record, blank_record};

    #[test]
    fn it_should_parse_a_fits_file(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = fits(data);

        match result {
            IResult::Done(_, f) => {
                assert_eq!(f, Fits::new(long_cadence_header()));
            },
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn primary_header_should_parse_a_primary_header(){
        let data = include_bytes!("../../assets/images/k2-trappist1-unofficial-tpf-long-cadence.fits");

        let result = primary_header(&data[0..(2*2880)]);

        match result {
            IResult::Done(_, h) => assert_eq!(h, long_cadence_header()),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    fn long_cadence_header() -> PrimaryHeader {
        PrimaryHeader::new(vec!(
            KeywordRecord::new(Keyword::SIMPLE),
            KeywordRecord::new(Keyword::BITPIX),
            KeywordRecord::new(Keyword::NAXIS),
            KeywordRecord::new(Keyword::EXTEND),
            KeywordRecord::new(Keyword::NEXTEND),
            KeywordRecord::new(Keyword::EXTNAME),
            KeywordRecord::new(Keyword::EXTVER),
            KeywordRecord::new(Keyword::ORIGIN),
            KeywordRecord::new(Keyword::DATE),
            KeywordRecord::new(Keyword::CREATOR),
            KeywordRecord::new(Keyword::PROCVER),
            KeywordRecord::new(Keyword::FILEVER),
            KeywordRecord::new(Keyword::TIMVERSN),
            KeywordRecord::new(Keyword::TELESCOP),
            KeywordRecord::new(Keyword::INSTRUME),
            KeywordRecord::new(Keyword::OBJECT),
            KeywordRecord::new(Keyword::KEPLERID),
            KeywordRecord::new(Keyword::CHANNEL),
            KeywordRecord::new(Keyword::MODULE),
            KeywordRecord::new(Keyword::OUTPUT),
            KeywordRecord::new(Keyword::CAMPAIGN),
            KeywordRecord::new(Keyword::DATA_REL),
            KeywordRecord::new(Keyword::OBSMODE),
            KeywordRecord::new(Keyword::MISSION),
            KeywordRecord::new(Keyword::TTABLEID),
            KeywordRecord::new(Keyword::RADESYS),
            KeywordRecord::new(Keyword::RA_OBJ),
            KeywordRecord::new(Keyword::DEC_OBJ),
            KeywordRecord::new(Keyword::EQUINOX),
            KeywordRecord::new(Keyword::PMRA),
            KeywordRecord::new(Keyword::PMDEC),
            KeywordRecord::new(Keyword::PMTOTAL),
            KeywordRecord::new(Keyword::PARALLAX),
            KeywordRecord::new(Keyword::GLON),
            KeywordRecord::new(Keyword::GLAT),
            KeywordRecord::new(Keyword::GMAG),
            KeywordRecord::new(Keyword::RMAG),
            KeywordRecord::new(Keyword::IMAG),
            KeywordRecord::new(Keyword::ZMAG),
            KeywordRecord::new(Keyword::JMAG),
            KeywordRecord::new(Keyword::HMAG),
            KeywordRecord::new(Keyword::KMAG),
            KeywordRecord::new(Keyword::KEPMAG),
            KeywordRecord::new(Keyword::GRCOLOR),
            KeywordRecord::new(Keyword::JKCOLOR),
            KeywordRecord::new(Keyword::GKCOLOR),
            KeywordRecord::new(Keyword::TEFF),
            KeywordRecord::new(Keyword::LOGG),
            KeywordRecord::new(Keyword::FEH),
            KeywordRecord::new(Keyword::EBMINUSV),
            KeywordRecord::new(Keyword::AV),
            KeywordRecord::new(Keyword::RADIUS),
            KeywordRecord::new(Keyword::TMINDEX),
            KeywordRecord::new(Keyword::CHECKSUM),
            KeywordRecord::new(Keyword::DATASUM),
        ))
    }

    #[test]
    fn keyword_record_should_parse_a_keyword_record(){
        let data = "OBJECT  = 'EPIC 200164267'     / string version of target id                    "
            .as_bytes();

        let result = keyword_record(data);

        match result {
            IResult::Done(_,k) => assert_eq!(k, KeywordRecord::new(Keyword::OBJECT)),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn keyword_should_parse_a_keyword(){
        let data = "OBJECT  "
            .as_bytes();

        let result = keyword(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::OBJECT),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn end_record_should_parse_an_END_record(){
        let data = "END                                                                             "
            .as_bytes();

        let result = end_record(data);

        match result {
            IResult::Done(_, keyword) => assert_eq!(keyword, Keyword::END),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }

    #[test]
    fn blank_record_should_parse_a_BLANK_record(){
        let data = "                                                                                "
            .as_bytes();

        let result = blank_record(data);

        match result {
            IResult::Done(_, record) => assert_eq!(record, BlankRecord),
            IResult::Error(_) => panic!("Did not expect an error"),
            IResult::Incomplete(_) => panic!("Did not expect to be incomplete")
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2020 Fabian Schuiki

use heck::SnakeCase;
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{format_ident, quote, ToTokens};
use std::{cell::RefCell, collections::HashSet};
use syn::Generics;

// CAUTION: This is all wildly unstable and relies on the compiler maintaining
// a certain order between proc macro expansions. So this could break any
// minute. Better have a robust CI.
thread_local! {
    static CALLS: RefCell<Vec<Call>> = Default::default();
}

struct Call {
    name: String,
    generics: String,
}

pub(crate) fn add_call(name: &Ident, generics: &Generics) {
    // Map everything to a string here. Compiler panics horribly if we hand out
    // the actual idents and generics.
    let call = Call {
        name: name.to_string(),
        generics: generics.to_token_stream().to_string(),
    };
    CALLS.with(|c| c.borrow_mut().push(call));
}

pub(crate) fn visitor(_input: TokenStream) -> TokenStream {
    // Flush the accumulated calls.
    let calls = CALLS.with(|c| std::mem::take(&mut *c.borrow_mut()));

    // Determine a lifetime for the visitor.
    let lt: syn::Lifetime = syn::parse_str("'a").unwrap();

    // Generate some documentation.
    let mut doc = format!(
        "A visitor.\n\n\
        Use the `accept()` function to start visiting nodes. For example:\n\n\
        ```ignore\n\
        struct MagicVisitor;\n\n\
        impl Visitor for MagicVisitor {{\n\
        }}\n\n\
        node.accept(&mut MagicVisitor);\n\
        ```\n\n\
        "
    );
    doc.push_str("Implements the visitor pattern over the following nodes:\n\n");
    for call in &calls {
        doc.push_str(&format!("- `{}`\n", call.name));
    }

    // Generate the `visit_*` calls.
    let mut emitted = HashSet::new();
    let mut pre_calls = vec![];
    let mut post_calls = vec![];
    for call in calls {
        // Avoid duplicates.
        if !emitted.insert(call.name.clone()) {
            continue;
        }

        // Convert the names back to identifiers.
        let pre_visit_fn = format_ident!("pre_visit_{}", call.name.to_snake_case());
        let post_visit_fn = format_ident!("post_visit_{}", call.name.to_snake_case());
        let name = format_ident!("{}", call.name);

        // Parse the generics again and add appropriate lifetime bounds.
        let generics: syn::Generics = syn::parse_str(&call.generics).unwrap();
        let mut impl_generics = generics.clone();
        for ltdef in impl_generics.lifetimes_mut() {
            ltdef.bounds.push(lt.clone());
        }

        // Generate some documentation.
        let pre_doc = format!(
            "Called for every `{}` node before visiting its children.\n\n\
            Return `false` from this function to not visit the node's children.",
            name
        );
        let post_doc = format!(
            "Called for every `{}` node after visiting its children.",
            name
        );

        // Render the corresponding call.
        pre_calls.push(quote! {
            #[doc = #pre_doc]
            fn #pre_visit_fn (&mut self, node: &#lt #name #generics) -> bool {
                true
            }
        });
        post_calls.push(quote! {
            #[doc = #post_doc]
            fn #post_visit_fn (&mut self, node: &#lt #name #generics) {
            }
        });
    }

    let output = quote! {
        #[doc = #doc]
        pub trait Visitor<#lt> {
            #(#pre_calls)*
            #(#post_calls)*
        }
    };
    output.into()
}
<commit_msg>derive: Fix rust 1.38 compat<commit_after>// Copyright (c) 2016-2020 Fabian Schuiki

use heck::SnakeCase;
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{format_ident, quote, ToTokens};
use std::{cell::RefCell, collections::HashSet};
use syn::Generics;

// CAUTION: This is all wildly unstable and relies on the compiler maintaining
// a certain order between proc macro expansions. So this could break any
// minute. Better have a robust CI.
thread_local! {
    static CALLS: RefCell<Vec<Call>> = Default::default();
}

struct Call {
    name: String,
    generics: String,
}

pub(crate) fn add_call(name: &Ident, generics: &Generics) {
    // Map everything to a string here. Compiler panics horribly if we hand out
    // the actual idents and generics.
    let call = Call {
        name: name.to_string(),
        generics: generics.to_token_stream().to_string(),
    };
    CALLS.with(|c| c.borrow_mut().push(call));
}

pub(crate) fn visitor(_input: TokenStream) -> TokenStream {
    // Flush the accumulated calls.
    let calls = CALLS.with(|c| std::mem::replace(&mut *c.borrow_mut(), Default::default()));

    // Determine a lifetime for the visitor.
    let lt: syn::Lifetime = syn::parse_str("'a").unwrap();

    // Generate some documentation.
    let mut doc = format!(
        "A visitor.\n\n\
        Use the `accept()` function to start visiting nodes. For example:\n\n\
        ```ignore\n\
        struct MagicVisitor;\n\n\
        impl Visitor for MagicVisitor {{\n\
        }}\n\n\
        node.accept(&mut MagicVisitor);\n\
        ```\n\n\
        "
    );
    doc.push_str("Implements the visitor pattern over the following nodes:\n\n");
    for call in &calls {
        doc.push_str(&format!("- `{}`\n", call.name));
    }

    // Generate the `visit_*` calls.
    let mut emitted = HashSet::new();
    let mut pre_calls = vec![];
    let mut post_calls = vec![];
    for call in calls {
        // Avoid duplicates.
        if !emitted.insert(call.name.clone()) {
            continue;
        }

        // Convert the names back to identifiers.
        let pre_visit_fn = format_ident!("pre_visit_{}", call.name.to_snake_case());
        let post_visit_fn = format_ident!("post_visit_{}", call.name.to_snake_case());
        let name = format_ident!("{}", call.name);

        // Parse the generics again and add appropriate lifetime bounds.
        let generics: syn::Generics = syn::parse_str(&call.generics).unwrap();
        let mut impl_generics = generics.clone();
        for ltdef in impl_generics.lifetimes_mut() {
            ltdef.bounds.push(lt.clone());
        }

        // Generate some documentation.
        let pre_doc = format!(
            "Called for every `{}` node before visiting its children.\n\n\
            Return `false` from this function to not visit the node's children.",
            name
        );
        let post_doc = format!(
            "Called for every `{}` node after visiting its children.",
            name
        );

        // Render the corresponding call.
        pre_calls.push(quote! {
            #[doc = #pre_doc]
            fn #pre_visit_fn (&mut self, node: &#lt #name #generics) -> bool {
                true
            }
        });
        post_calls.push(quote! {
            #[doc = #post_doc]
            fn #post_visit_fn (&mut self, node: &#lt #name #generics) {
            }
        });
    }

    let output = quote! {
        #[doc = #doc]
        pub trait Visitor<#lt> {
            #(#pre_calls)*
            #(#post_calls)*
        }
    };
    output.into()
}
<|endoftext|>


<commit_before>//! Endpoints for user profiles.
<commit_msg>profile endpoints<commit_after>//! Endpoints for user profiles.

/// GET /_matrix/client/r0/profile/{userId}/displayname
///
/// [Matrix spec link](http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname)
pub mod get_display_name {
    use ruma_identifiers::UserId;

    /// This API endpoint's path parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct PathParams {
        pub user_id: UserId
    }

    /// This API endpoint's body parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Response {
        pub displayname: Option<String>
    }

    /// Details about this API endpoint.
    pub struct Endpoint;

    impl ::Endpoint for Endpoint {
        type BodyParams = ();
        type PathParams = PathParams;
        type QueryParams = ();
        type Response = Response;

        fn method() -> ::Method {
            ::Method::Get
        }

        fn request_path(params: Self::PathParams) -> String {
            format!(
                "/_matrix/client/r0/profile/{}/displayname",
                params.user_id
            )
        }

        fn router_path() -> String {
            "/_matrix/client/r0/profile/:user_id/displayname".to_string()
        }
    }
}

/// PUT /_matrix/client/r0/profile/{userId}/displayname
///
/// [Matrix spec link](http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname)
pub mod set_display_name {
    use ruma_identifiers::UserId;

    /// This API endpoint's path parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct PathParams {
        pub user_id: UserId
    }

    /// This API endpoint's body parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct BodyParams {
        pub displayname: Option<String>
    }

    /// Details about this API endpoint.
    pub struct Endpoint;

    impl ::Endpoint for Endpoint {
        type BodyParams = BodyParams;
        type PathParams = PathParams;
        type QueryParams = ();
        type Response = ();

        fn method() -> ::Method {
            ::Method::Put
        }

        fn request_path(params: Self::PathParams) -> String {
            format!(
                "/_matrix/client/r0/profile/{}/displayname",
                params.user_id
            )
        }

        fn router_path() -> String {
            "/_matrix/client/r0/profile/:user_id/displayname".to_string()
        }
    }
}

/// GET /_matrix/client/r0/profile/{userId}/avatar_url
///
/// [Matrix spec link](http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url)
pub mod get_avatar_url {
    use ruma_identifiers::UserId;

    /// This API endpoint's path parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct PathParams {
        pub user_id: UserId
    }

    /// This API endpoint's body parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Response {
        pub avatar_url: Option<String>
    }

    /// Details about this API endpoint.
    pub struct Endpoint;

    impl ::Endpoint for Endpoint {
        type BodyParams = ();
        type PathParams = PathParams;
        type QueryParams = ();
        type Response = Response;

        fn method() -> ::Method {
            ::Method::Get
        }

        fn request_path(params: Self::PathParams) -> String {
            format!(
                "/_matrix/client/r0/profile/{}/avatar_url",
                params.user_id
            )
        }

        fn router_path() -> String {
            "/_matrix/client/r0/profile/:user_id/avatar_url".to_string()
        }
    }
}

/// PUT /_matrix/client/r0/profile/{userId}/avatar_url
///
/// [Matrix spec link](http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url)
pub mod set_avatar_url {
    use ruma_identifiers::UserId;

    /// This API endpoint's path parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct PathParams {
        pub user_id: UserId
    }

    /// This API endpoint's body parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct BodyParams {
        pub avatar_url: Option<String>
    }

    /// Details about this API endpoint.
    pub struct Endpoint;

    impl ::Endpoint for Endpoint {
        type BodyParams = BodyParams;
        type PathParams = PathParams;
        type QueryParams = ();
        type Response = ();

        fn method() -> ::Method {
            ::Method::Put
        }

        fn request_path(params: Self::PathParams) -> String {
            format!(
                "/_matrix/client/r0/profile/{}/avatar_url",
                params.user_id
            )
        }

        fn router_path() -> String {
            "/_matrix/client/r0/profile/:user_id/avatar_url".to_string()
        }
    }
}

/// GET /_matrix/client/r0/profile/{userId}
///
/// [Matrix spec link](http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid)
pub mod get_profile {
    use ruma_identifiers::UserId;

    /// This API endpoint's path parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct PathParams {
        pub user_id: UserId
    }

    /// This API endpoint's body parameters.
    #[derive(Clone, Debug, Deserialize, Serialize)]
    pub struct Response {
        pub avatar_url: Option<String>,
        pub displayname: Option<String>
    }

    /// Details about this API endpoint.
    pub struct Endpoint;

    impl ::Endpoint for Endpoint {
        type BodyParams = ();
        type PathParams = PathParams;
        type QueryParams = ();
        type Response = Response;

        fn method() -> ::Method {
            ::Method::Get
        }

        fn request_path(params: Self::PathParams) -> String {
            format!(
                "/_matrix/client/r0/profile/{}",
                params.user_id
            )
        }

        fn router_path() -> String {
            "/_matrix/client/r0/profile/:user_id".to_string()
        }
    }
}
<|endoftext|>