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

use entity::{Ability, Entity, EntityDetails};
use id::{Id, get_id};
use player::Player;
use timestamp::get_timestamp;
use zone::{Zone, ZoneDetails};

/// Represents the game's current phase
#[derive(Debug, Clone, PartialEq)]
pub enum GamePhase {
    Untap,
    Main,

    // TODO: Other game phases
}

impl GamePhase {
    /// Find the next phase for the game, if there is one.
    ///
    /// Returning `None` signifies that the turn should advance.
    pub fn next(&self) -> Option<GamePhase> {
        match *self {
            GamePhase::Untap => Some(GamePhase::Main),
            GamePhase::Main => None,
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum GameStatus {
    NeedsPlayerAction,

    // TODO: Represent winners and potential draw
    Ended,

    // TODO: Represent unrecoverable internal error?
}

/// Defines all of the actions that a player can take when they have
#[derive(Debug, Clone)]
pub enum PlayerAction {
    Concede,
    PassPriority,
    PlayLand {
        entity_id: Id,
    },
    ActivateAbility {
        entity_id: Id,
        ability_id: Id,
    },

    // TODO: Other possible player actions

    // Some potentially interesting actions:
    // * Flip a morph card (doesn't need priority)
}

/// Defines all of the issues we can run into when performing a player action
#[derive(Debug, Clone)]
pub enum PlayerActionError {
    /// Denotes anything that a player isn't allowed to do.
    ///
    /// TODO: Break this into more variants as needed
    NotAllowed(&'static str),
}

/// Will be used to define mutations to the game state. Mutations are defined as
/// objects so that effects can respond to and replace them.
#[derive(Debug, Clone)]
pub enum GameMutation {
}

/// Represents all of the important serializable information about a game.
///
/// `Game` cycles between two primary statuses:
/// * `GameStatus::Processing` -- the game is currently processing
/// * `GameStatus::NeedsPlayerAction` -- the game requires a response from the
///   player who has priority, defined by `priority_player`.
///
/// All mutation to the game will come from `PlayerAction` and `GameMutation`
/// objects, which will be filtered by effects created by the game's rules.
#[derive(Debug, Clone)]
pub struct Game {
    pub zones: HashMap<Id, Zone>,
    pub players: HashMap<Id, Player>,

    /// The base definition of each entity in the game, before being modified by
    /// the currently active effects.
    pub entities: HashMap<Id, Entity>,

    /// A simple representation of mana pools: the amount of green mana each
    /// player has. This will need to be expanded!
    pub mana_pools: HashMap<Id, usize>,

    /// The order that players have their turns in.
    pub player_turn_order: Vec<Id>,

    /// The player whose turn it is right now.
    ///
    /// I'm not sure if there's a game state in which there is no active player,
    /// but it simplifies initialization of `Game`!
    pub active_player: Option<Id>,

    /// The player who currently has priority.
    ///
    /// In certain parts of the game, like resolution of state-based-actions and
    /// the untap step, no players have priority.
    pub priority_player: Option<Id>,

    /// The current phase of the game, as defined in the game rules.
    pub current_phase: GamePhase,

    /// The current game status, defining the core interaction loop.
    pub current_status: GameStatus,

    // TODO: A reference to an entity descriptor pool, like what cards are legal
    //       in this format.
    // TODO: The stack, a Vec<Entity>?
    // TODO: A log of player actions
    // TODO: A list of currently active effects and their durations
}

impl Game {
    /// Checks that the given player has priority, and returns the appropriate
    /// `PlayerActionError` if they do not.
    fn check_priority(&self, acting_player_id: Id) -> Result<(), PlayerActionError> {
        match self.priority_player {
            Some(priority_id) => {
                if acting_player_id == priority_id {
                    Ok(())
                } else {
                    Err(PlayerActionError::NotAllowed("Player does not have priority"))
                }
            },
            None => Err(PlayerActionError::NotAllowed("No player has priority")),
        }
    }

    /// Process the given player action.
    pub fn do_player_action(&mut self, acting_player_id: Id, action: &PlayerAction) -> Result<(), PlayerActionError> {
        // Players can only take an action if the game can accept one!
        match self.current_status {
            GameStatus::NeedsPlayerAction => {},
            _ => return Err(PlayerActionError::NotAllowed("Game unable to accept actions at this time")),
        }

        // We'll break the actual action handling into a private routine
        // eventually
        match *action {
            PlayerAction::Concede => {
                self.current_status = GameStatus::Ended;

                Ok(())
            },
            PlayerAction::PassPriority => {
                self.check_priority(acting_player_id)?;

                let current_priority_index = self.player_turn_order
                    .iter()
                    .position(|&id| id == acting_player_id)
                    .expect("Player with priority is missing from player_turn_order!");

                let current_active_id = self.active_player.unwrap();
                let current_active_index = self.player_turn_order
                    .iter()
                    .position(|&id| id == current_active_id)
                    .expect("Player with priority is missing from player_turn_order!");

                let player_count = self.player_turn_order.len();

                let next_priority_index = (current_priority_index + 1) % player_count;
                let next_priority_id = self.player_turn_order[next_priority_index];

                // If priority would swing back around to the active player,
                // it's time to advance!
                if next_priority_id == current_active_id {
                    // TODO: Pop one item off the stack if not empty

                    // Are we at the end of the turn?
                    match self.current_phase.next() {
                        Some(next_phase) => {
                            // To the next phase!
                            self.current_phase = next_phase;
                            self.priority_player = self.active_player;

                            // TODO: Empty mana pools of all players
                        },
                        None => {
                            // We're out of phases, advance turns!
                            let next_active_index = (current_active_index + 1) % player_count;
                            let next_active_id = self.player_turn_order[next_active_index];

                            self.active_player = Some(next_active_id);
                            self.priority_player = Some(next_active_id);
                        },
                    }
                } else {
                    self.priority_player = Some(next_priority_id);
                }

                Ok(())
            },
            PlayerAction::PlayLand { entity_id } => {
                // TODO: Players can only play lands when it's their turn
                // TODO: Players can only play lands during a main phase
                // TODO: Players can only play lands when the stack is empty

                self.check_priority(acting_player_id)?;

                let player_hand_id = self
                    .find_zone_id(|zone| {
                        match zone.details {
                            ZoneDetails::Hand { player_id } => player_id == acting_player_id,
                            _ => false,
                        }
                    })
                    .expect("Unable to locate player's hand!");

                let battlefield_id = self
                    .find_zone_id(|zone| {
                        match zone.details {
                            ZoneDetails::Battlefield => true,
                            _ => false,
                        }
                    })
                    .expect("Unable to locate battlefield!");

                // We need to make sure we have a land to play!
                match self.entities.get(&entity_id) {
                    Some(entity) => {
                        // Make sure it's in our player's hand
                        if entity.zone != player_hand_id {
                            return Err(PlayerActionError::NotAllowed("Land not in player's hand"));
                        }

                        // Make sure it's a land
                        match entity.details {
                            EntityDetails::Forest { .. } => {},
                            // TODO: Other entity details, when they're introduced
                        }
                    }
                    None => return Err(PlayerActionError::NotAllowed("Entity not found")),
                }

                // We just checked to make sure this existed above!
                let entity = self.entities.remove(&entity_id).unwrap();

                // For now, let's just generate a new entity and put it on the
                // battlefield.
                let new_entity = Entity {
                    id: get_id(),
                    zone: battlefield_id,
                    timestamp: get_timestamp(),
                    details: entity.details,
                    abilities: entity.abilities,
                };

                // TODO: Use GameMutation instead?
                self.entities.insert(new_entity.id, new_entity);

                Ok(())
            },
            PlayerAction::ActivateAbility { entity_id, ability_id } => {
                // TODO: This function is definitely temporary, like the rest of
                //       the ability system.

                self.check_priority(acting_player_id)?;

                let mut entity = match self.entities.get_mut(&entity_id) {
                    Some(entity) => entity,
                    None => return Err(PlayerActionError::NotAllowed("Entity not found")),
                };

                // TODO: Make sure the acting player controls this entity!

                let ability = match entity.abilities.get(&ability_id) {
                    Some(ability) => ability,
                    None => return Err(PlayerActionError::NotAllowed("Ability not found on entity")),
                };

                // TODO: Make sure we can pay this cost, perhaps prompt player!

                //  such green
                //       so forest
                //    wow
                match *ability {
                    Ability::AddGreen => {
                        // TODO: Tap this land as a cost

                        // Make sure we're on the battlefield! Abilities will
                        // have to specify when they are valid to activate.
                        match self.zones.get(&entity.zone) {
                            Some(zone) => match zone.details {
                                ZoneDetails::Battlefield => {},
                                _ => return Err(PlayerActionError::NotAllowed("Entity not on battlefield")),
                            },
                            None => return Err(PlayerActionError::NotAllowed("Could not find entity's zone by ID")),
                        }

                        // Make sure our land is untapped
                        match entity.details {
                            EntityDetails::Forest { tapped } => {
                                if tapped {
                                    return Err(PlayerActionError::NotAllowed("Entity already tapped"));
                                }

                                // Tap the land!
                                entity.details = EntityDetails::Forest {
                                    tapped: true
                                };
                            }
                        }

                        // Find our mana pool and increment it
                        // Eventually, mana will be more complex than a usize
                        let mana_value = *self.mana_pools.get(&acting_player_id)
                            .expect("Player was missing their mana pool!");

                        self.mana_pools.insert(acting_player_id, mana_value + 1);
                    },
                }

                Ok(())
            },
        }
    }

    /// Apply all state-based actions, like creatures dying due to damage.
    pub fn process_state_based_actions(&mut self) {
        // TODO: State based actions!
    }

    /// Finds the zone that passes the given condition, if it exists.
    pub fn find_zone_id<F>(&self, predicate: F) -> Option<Id>
    where
        F: Fn(&Zone) -> bool
    {
        self.zones.values()
            .find(|zone| predicate(zone))
            .and_then(|zone| Some(zone.id))
    }

    /// Create a version of `Game` as viewed by the given player. This
    /// should collapse hidden zones and unknown information. Hopefully, it's
    /// also cheap, since the most naive way to implement client communication
    /// would be to send a full state on every change!
    pub fn view_as_player(&self, _player_id: Id) -> Game {
        // TODO: Hide entities that player has no knowledge of, like face-down
        //       permanents.

        // TODO: Hide zones that player has no knowledge of, like libraries, and
        //       face-down exile zones created by cards like Bomat Courier.

        self.clone()
    }

    /// Queries an entity in the game by ID.
    ///
    /// Entity representation may be unintuitive -- the `Entity` objects stored
    /// in the `Game` only contain their base state. When querying an
    /// entity's current state, we need to traverse a list of effects currently
    /// modifying that object in layer order.
    ///
    /// As an example, consider a Mountain enchanted by Spreading Seas:
    ///
    /// The definition of Mountain says that it has type `Basic Land - Mountain`
    /// and thus has `{T}: Add {R} to your mana pool`.
    ///
    /// Spreading Seas, when on the battlefield, has a replacement effect for
    /// that removes its target's abilities and changes its type line to `Basic
    /// Land - Island`.
    ///
    /// The mountain's current state (post-effect) thus says that it has type
    /// `Basic Land - Island` and taps for blue mana only!
    ///
    /// If we wanted to return the Mountain entity directly, we'd either have to
    /// modify the data whenever an effect enters/leaves the battlefield or just
    /// keep a list of active effects and calculate them on each observation.
    ///
    /// While the former technique is possible, I think that calculating effects
    /// on each individual observation event is simpler.
    pub fn view_entity(&self, entity_id: Id) -> Option<Entity> {
        let base_entity = self.entities.get(&entity_id)?;

        // TODO: Enumerate the set of effects that could affect this entity.

        Some(base_entity.clone())
    }
}
<commit_msg>Remove outdated comments<commit_after>use std::collections::HashMap;

use entity::{Ability, Entity, EntityDetails};
use id::{Id, get_id};
use player::Player;
use timestamp::get_timestamp;
use zone::{Zone, ZoneDetails};

/// Represents the game's current phase
#[derive(Debug, Clone, PartialEq)]
pub enum GamePhase {
    Untap,
    Main,

    // TODO: Other game phases
}

impl GamePhase {
    /// Find the next phase for the game, if there is one.
    ///
    /// Returning `None` signifies that the turn should advance.
    pub fn next(&self) -> Option<GamePhase> {
        match *self {
            GamePhase::Untap => Some(GamePhase::Main),
            GamePhase::Main => None,
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum GameStatus {
    NeedsPlayerAction,

    // TODO: Represent winners and potential draw
    Ended,

    // TODO: Represent unrecoverable internal error?
}

/// Defines all of the actions that a player can take when they have
#[derive(Debug, Clone)]
pub enum PlayerAction {
    Concede,
    PassPriority,
    PlayLand {
        entity_id: Id,
    },
    ActivateAbility {
        entity_id: Id,
        ability_id: Id,
    },

    // TODO: Other possible player actions

    // Some potentially interesting actions:
    // * Flip a morph card (doesn't need priority)
}

/// Defines all of the issues we can run into when performing a player action
#[derive(Debug, Clone)]
pub enum PlayerActionError {
    /// Denotes anything that a player isn't allowed to do.
    ///
    /// TODO: Break this into more variants as needed
    NotAllowed(&'static str),
}

/// Will be used to define mutations to the game state. Mutations are defined as
/// objects so that effects can respond to and replace them.
#[derive(Debug, Clone)]
pub enum GameMutation {
}

/// Represents all of the important serializable information about a game.
///
/// `Game` cycles between two primary statuses:
/// * `GameStatus::Processing` -- the game is currently processing
/// * `GameStatus::NeedsPlayerAction` -- the game requires a response from the
///   player who has priority, defined by `priority_player`.
///
/// All mutation to the game will come from `PlayerAction` and `GameMutation`
/// objects, which will be filtered by effects created by the game's rules.
#[derive(Debug, Clone)]
pub struct Game {
    pub zones: HashMap<Id, Zone>,
    pub players: HashMap<Id, Player>,

    /// The base definition of each entity in the game, before being modified by
    /// the currently active effects.
    pub entities: HashMap<Id, Entity>,

    /// A simple representation of mana pools: the amount of green mana each
    /// player has. This will need to be expanded!
    pub mana_pools: HashMap<Id, usize>,

    /// The order that players have their turns in.
    pub player_turn_order: Vec<Id>,

    /// The player whose turn it is right now.
    ///
    /// I'm not sure if there's a game state in which there is no active player,
    /// but it simplifies initialization of `Game`!
    pub active_player: Option<Id>,

    /// The player who currently has priority.
    ///
    /// In certain parts of the game, like resolution of state-based-actions and
    /// the untap step, no players have priority.
    pub priority_player: Option<Id>,

    /// The current phase of the game, as defined in the game rules.
    pub current_phase: GamePhase,

    /// The current game status, defining the core interaction loop.
    pub current_status: GameStatus,

    // TODO: A reference to an entity descriptor pool, like what cards are legal
    //       in this format.
    // TODO: The stack, a Vec<Entity>?
    // TODO: A log of player actions
    // TODO: A list of currently active effects and their durations
}

impl Game {
    /// Checks that the given player has priority, and returns the appropriate
    /// `PlayerActionError` if they do not.
    fn check_priority(&self, acting_player_id: Id) -> Result<(), PlayerActionError> {
        match self.priority_player {
            Some(priority_id) => {
                if acting_player_id == priority_id {
                    Ok(())
                } else {
                    Err(PlayerActionError::NotAllowed("Player does not have priority"))
                }
            },
            None => Err(PlayerActionError::NotAllowed("No player has priority")),
        }
    }

    /// Process the given player action.
    pub fn do_player_action(&mut self, acting_player_id: Id, action: &PlayerAction) -> Result<(), PlayerActionError> {
        // Players can only take an action if the game can accept one!
        match self.current_status {
            GameStatus::NeedsPlayerAction => {},
            _ => return Err(PlayerActionError::NotAllowed("Game unable to accept actions at this time")),
        }

        // We'll break the actual action handling into a private routine
        // eventually
        match *action {
            PlayerAction::Concede => {
                self.current_status = GameStatus::Ended;

                Ok(())
            },
            PlayerAction::PassPriority => {
                self.check_priority(acting_player_id)?;

                let current_priority_index = self.player_turn_order
                    .iter()
                    .position(|&id| id == acting_player_id)
                    .expect("Player with priority is missing from player_turn_order!");

                let current_active_id = self.active_player.unwrap();
                let current_active_index = self.player_turn_order
                    .iter()
                    .position(|&id| id == current_active_id)
                    .expect("Player with priority is missing from player_turn_order!");

                let player_count = self.player_turn_order.len();

                let next_priority_index = (current_priority_index + 1) % player_count;
                let next_priority_id = self.player_turn_order[next_priority_index];

                // If priority would swing back around to the active player,
                // it's time to advance!
                if next_priority_id == current_active_id {
                    // TODO: Pop one item off the stack if not empty

                    // Are we at the end of the turn?
                    match self.current_phase.next() {
                        Some(next_phase) => {
                            // To the next phase!
                            self.current_phase = next_phase;
                            self.priority_player = self.active_player;

                            // TODO: Empty mana pools of all players
                        },
                        None => {
                            // We're out of phases, advance turns!
                            let next_active_index = (current_active_index + 1) % player_count;
                            let next_active_id = self.player_turn_order[next_active_index];

                            self.active_player = Some(next_active_id);
                            self.priority_player = Some(next_active_id);
                        },
                    }
                } else {
                    self.priority_player = Some(next_priority_id);
                }

                Ok(())
            },
            PlayerAction::PlayLand { entity_id } => {
                // TODO: Players can only play lands when it's their turn
                // TODO: Players can only play lands during a main phase
                // TODO: Players can only play lands when the stack is empty

                self.check_priority(acting_player_id)?;

                let player_hand_id = self
                    .find_zone_id(|zone| {
                        match zone.details {
                            ZoneDetails::Hand { player_id } => player_id == acting_player_id,
                            _ => false,
                        }
                    })
                    .expect("Unable to locate player's hand!");

                let battlefield_id = self
                    .find_zone_id(|zone| {
                        match zone.details {
                            ZoneDetails::Battlefield => true,
                            _ => false,
                        }
                    })
                    .expect("Unable to locate battlefield!");

                // We need to make sure we have a land to play!
                match self.entities.get(&entity_id) {
                    Some(entity) => {
                        // Make sure it's in our player's hand
                        if entity.zone != player_hand_id {
                            return Err(PlayerActionError::NotAllowed("Land not in player's hand"));
                        }

                        // Make sure it's a land
                        match entity.details {
                            EntityDetails::Forest { .. } => {},
                            // TODO: Other entity details, when they're introduced
                        }
                    }
                    None => return Err(PlayerActionError::NotAllowed("Entity not found")),
                }

                // We just checked to make sure this existed above!
                let entity = self.entities.remove(&entity_id).unwrap();

                // For now, let's just generate a new entity and put it on the
                // battlefield.
                let new_entity = Entity {
                    id: get_id(),
                    zone: battlefield_id,
                    timestamp: get_timestamp(),
                    details: entity.details,
                    abilities: entity.abilities,
                };

                // TODO: Use GameMutation instead?
                self.entities.insert(new_entity.id, new_entity);

                Ok(())
            },
            PlayerAction::ActivateAbility { entity_id, ability_id } => {
                // TODO: This function is definitely temporary, like the rest of
                //       the ability system.

                self.check_priority(acting_player_id)?;

                let mut entity = match self.entities.get_mut(&entity_id) {
                    Some(entity) => entity,
                    None => return Err(PlayerActionError::NotAllowed("Entity not found")),
                };

                // TODO: Make sure the acting player controls this entity!

                let ability = match entity.abilities.get(&ability_id) {
                    Some(ability) => ability,
                    None => return Err(PlayerActionError::NotAllowed("Ability not found on entity")),
                };

                //  such green
                //       so forest
                //    wow
                match *ability {
                    Ability::AddGreen => {
                        // Make sure we're on the battlefield! Abilities will
                        // have to specify when they are valid to activate.
                        match self.zones.get(&entity.zone) {
                            Some(zone) => match zone.details {
                                ZoneDetails::Battlefield => {},
                                _ => return Err(PlayerActionError::NotAllowed("Entity not on battlefield")),
                            },
                            None => return Err(PlayerActionError::NotAllowed("Could not find entity's zone by ID")),
                        }

                        // Make sure our land is untapped
                        match entity.details {
                            EntityDetails::Forest { tapped } => {
                                if tapped {
                                    return Err(PlayerActionError::NotAllowed("Entity already tapped"));
                                }

                                // Tap the land!
                                entity.details = EntityDetails::Forest {
                                    tapped: true
                                };
                            }
                        }

                        // Find our mana pool and increment it
                        // Eventually, mana will be more complex than a usize
                        let mana_value = *self.mana_pools.get(&acting_player_id)
                            .expect("Player was missing their mana pool!");

                        self.mana_pools.insert(acting_player_id, mana_value + 1);
                    },
                }

                Ok(())
            },
        }
    }

    /// Apply all state-based actions, like creatures dying due to damage.
    pub fn process_state_based_actions(&mut self) {
        // TODO: State based actions!
    }

    /// Finds the zone that passes the given condition, if it exists.
    pub fn find_zone_id<F>(&self, predicate: F) -> Option<Id>
    where
        F: Fn(&Zone) -> bool
    {
        self.zones.values()
            .find(|zone| predicate(zone))
            .and_then(|zone| Some(zone.id))
    }

    /// Create a version of `Game` as viewed by the given player. This
    /// should collapse hidden zones and unknown information. Hopefully, it's
    /// also cheap, since the most naive way to implement client communication
    /// would be to send a full state on every change!
    pub fn view_as_player(&self, _player_id: Id) -> Game {
        // TODO: Hide entities that player has no knowledge of, like face-down
        //       permanents.

        // TODO: Hide zones that player has no knowledge of, like libraries, and
        //       face-down exile zones created by cards like Bomat Courier.

        self.clone()
    }

    /// Queries an entity in the game by ID.
    ///
    /// Entity representation may be unintuitive -- the `Entity` objects stored
    /// in the `Game` only contain their base state. When querying an
    /// entity's current state, we need to traverse a list of effects currently
    /// modifying that object in layer order.
    ///
    /// As an example, consider a Mountain enchanted by Spreading Seas:
    ///
    /// The definition of Mountain says that it has type `Basic Land - Mountain`
    /// and thus has `{T}: Add {R} to your mana pool`.
    ///
    /// Spreading Seas, when on the battlefield, has a replacement effect for
    /// that removes its target's abilities and changes its type line to `Basic
    /// Land - Island`.
    ///
    /// The mountain's current state (post-effect) thus says that it has type
    /// `Basic Land - Island` and taps for blue mana only!
    ///
    /// If we wanted to return the Mountain entity directly, we'd either have to
    /// modify the data whenever an effect enters/leaves the battlefield or just
    /// keep a list of active effects and calculate them on each observation.
    ///
    /// While the former technique is possible, I think that calculating effects
    /// on each individual observation event is simpler.
    pub fn view_entity(&self, entity_id: Id) -> Option<Entity> {
        let base_entity = self.entities.get(&entity_id)?;

        // TODO: Enumerate the set of effects that could affect this entity.

        Some(base_entity.clone())
    }
}
<|endoftext|>


<commit_before>use core::*;
use bitboard::*;
use moves::*;
use board::*;
use tables::*;

use std::collections::HashMap;
use std::num;

bitflags! {
    pub struct CastlingRights: u8 {
        const WHITE_KINGSIDE  = 0b0001;
        const WHITE_QUEENSIDE = 0b0010;
        const BLACK_KINGSIDE  = 0b0100;
        const BLACK_QUEENSIDE = 0b1000;
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Game {
    pub board: Board,
    pub to_move: Color,
    pub ep_square: Option<Square>,
    pub castling_rights: CastlingRights,
    pub fifty_move_count: u8,
    pub moves: u16
}

impl Game {
    pub fn starting_position() -> Game {
        Game {
            board: Board::starting_position(),
            to_move: Color::White,
            ep_square: None,
            castling_rights: CastlingRights::all(),
            fifty_move_count: 0,
            moves: 1,
        }
    }

    pub fn empty_position() -> Game {
        Game {
            board: Board::empty_position(),
            to_move: Color::White,
            ep_square: None,
            castling_rights: CastlingRights::empty(),
            fifty_move_count: 0,
            moves: 1
        }
    }

    pub fn to_fen(&self) -> String {
        use PieceType::*;
        use Color::*;

        println!("what");

        let mut board_str = String::new();
        let mut empty_tally = 0;

        for idx in (0..64).rev() {
            let sq = Square::new(idx);
            let wrapped_across_row = sq.unwrap() % 8 == 7;

            let maybe_piece = self.board.piece_at(sq);

            if (maybe_piece.is_some() || wrapped_across_row) && empty_tally > 0 {
                assert!(empty_tally <= 8);
                board_str.push_str(&empty_tally.to_string());
                empty_tally = 0;
            }

            if wrapped_across_row && idx < 63 {
                board_str.push('/');
            }

            match maybe_piece {
                Some(piece) => {
                    match (piece.color, piece.ptype) {
                        (Black , Pawn  ) => board_str.push('p'),
                        (Black , Knight) => board_str.push('n'),
                        (Black , Bishop) => board_str.push('b'),
                        (Black , Rook  ) => board_str.push('r'),
                        (Black , Queen ) => board_str.push('q'),
                        (Black , King  ) => board_str.push('k'),
                        (White , Pawn  ) => board_str.push('P'),
                        (White , Knight) => board_str.push('N'),
                        (White , Bishop) => board_str.push('B'),
                        (White , Rook  ) => board_str.push('R'),
                        (White , Queen ) => board_str.push('Q'),
                        (White , King  ) => board_str.push('K'),
                    }
                }
                None => empty_tally += 1
            }
        }

        let to_move_str = match self.to_move {
            White => "w".to_string(),
            Black => "b".to_string()
        };

        let mut castling_str = String::new();

        if (self.castling_rights == CastlingRights::empty()) {
            castling_str = "-".to_string();
        } else {
            if self.castling_rights.intersects(CastlingRights::WHITE_KINGSIDE) {
                castling_str.push('K');
            }
            if self.castling_rights.intersects(CastlingRights::WHITE_QUEENSIDE) {
                castling_str.push('Q');
            }
            if self.castling_rights.intersects(CastlingRights::BLACK_KINGSIDE) {
                castling_str.push('k');
            }
            if self.castling_rights.intersects(CastlingRights::BLACK_QUEENSIDE) {
                castling_str.push('q');
            }
        }

        let ep_square_str = match self.ep_square {
            Some(sq) => sq.to_algebraic().to_string(),
            None => "-".to_string()
        };

        return [board_str, to_move_str, castling_str, ep_square_str, self.fifty_move_count.to_string(), self.moves.to_string()].join(" ");
    }

    pub fn from_fen(fen: &'static str) -> Option<Game> {
        let words: Vec<&str> = fen.split(' ').collect();

        if words.len() != 6 {
            return None;
        }

        let mut game = Game::empty_position();

        use PieceType::*;
        use Color::*;

        { // build up the game board
            let mut current_square: Square = Square::new(63);

            let decrement_square = |sq: &mut Square, n: u32| {
                if sq.unwrap() > 0 {
                    *sq = Square::new(sq.unwrap() - n);
                }
            };

            let mut add_piece = |color: Color, piece: PieceType, sq: &mut Square| {
                game.board.set_piece_bit(color, piece, *sq);
                decrement_square(sq, 1);
            };

            for ch in words[0].chars() {
                match ch {
                    'p' => add_piece(Black , Pawn   , &mut current_square) ,
                    'n' => add_piece(Black , Knight , &mut current_square) ,
                    'b' => add_piece(Black , Bishop , &mut current_square) ,
                    'r' => add_piece(Black , Rook   , &mut current_square) ,
                    'q' => add_piece(Black , Queen  , &mut current_square) ,
                    'k' => add_piece(Black , King   , &mut current_square) ,
                    'P' => add_piece(White , Pawn   , &mut current_square) ,
                    'N' => add_piece(White , Knight , &mut current_square) ,
                    'B' => add_piece(White , Bishop , &mut current_square) ,
                    'R' => add_piece(White , Rook   , &mut current_square) ,
                    'Q' => add_piece(White , Queen  , &mut current_square) ,
                    'K' => add_piece(White , King   , &mut current_square) ,
                    '1' => decrement_square(&mut current_square, 1),
                    '2' => decrement_square(&mut current_square, 2),
                    '3' => decrement_square(&mut current_square, 3),
                    '4' => decrement_square(&mut current_square, 4),
                    '5' => decrement_square(&mut current_square, 5),
                    '6' => decrement_square(&mut current_square, 6),
                    '7' => decrement_square(&mut current_square, 7),
                    '8' => decrement_square(&mut current_square, 8),
                    '/' => {},
                    _ => return None
                }
            }
        }

        match words[1] {
            "w" => game.to_move = White,
            "b" => game.to_move = Black,
            _ => return None
        }

        for ch in words[2].chars() {
            match ch {
                'K' => game.castling_rights |= CastlingRights::WHITE_KINGSIDE,
                'Q' => game.castling_rights |= CastlingRights::WHITE_QUEENSIDE,
                'k' => game.castling_rights |= CastlingRights::BLACK_KINGSIDE,
                'q' => game.castling_rights |= CastlingRights::BLACK_QUEENSIDE,
                '-' => {},
                _ => return None
            }
        }

        match words[3] {
            "-" => game.ep_square = None,
            _ => match Square::from_algebraic(words[3]) {
                None => return None,
                Some(sq) => game.ep_square = Some(sq)
            }
        }

        match words[4].parse::<u8>() {
            Err(_) => return None,
            Ok(x) => game.fifty_move_count = x
        }

        match words[5].parse::<u16>() {
            Err(_) => return None,
            Ok(x) => game.moves = x
        }

        return Some(game);
    }

    pub fn make_move(&mut self, m: Move) {
        let from_sq        = m.from();
        let from_bit       = from_sq.bitrep();
        let to_sq          = m.to();
        let to_bit         = to_sq.bitrep();
        let from_to_bit    = from_bit | to_bit;
        let is_capture     = m.is_capture();
        let flag           = m.flag();
        let moving_color   = self.to_move;
        let opponent_color = !moving_color;

        use Color::*;
        use PieceType::*;

        let moved_piece = self.board.piece_at(from_sq).unwrap().ptype;
        let captured_piece =
            if flag == EP_CAPTURE_FLAG {
                match opponent_color {
                    White => self.board.piece_at(Square::new(self.ep_square.unwrap().unwrap() + 8)),
                    Black => self.board.piece_at(Square::new(self.ep_square.unwrap().unwrap() - 8))
                }
            } else {
                self.board.piece_at(to_sq)
            };

        assert!(is_capture == captured_piece.is_some());

        //TODO: add moving/captured piece type to Move structure
        match moved_piece {
            Pawn => {
                *self.board.get_pieces_mut(self.to_move, Pawn) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;

                if flag == DOUBLE_PAWN_PUSH_FLAG {
                    self.ep_square = match moving_color {
                        White => Some(Square::new(to_sq.unwrap() - 8)),
                        Black => Some(Square::new(to_sq.unwrap() + 8))
                    }
                }

                // TODO: promotions

                if is_capture {
                    if flag == EP_CAPTURE_FLAG {
                        assert!(self.ep_square.is_some());

                        let captured_bit = match moving_color {
                            White => self.ep_square.unwrap().bitrep().shifted_down(),
                            Black => self.ep_square.unwrap().bitrep().shifted_up()
                        };

                        *self.board.get_pieces_mut(opponent_color, Pawn) ^= captured_bit;
                        *self.board.occupied_by_mut(opponent_color) ^= captured_bit;
                    } else {
                        *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                        *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                    }
                }

            },

            Knight => {
                *self.board.get_pieces_mut(self.to_move, Knight) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;
                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            Bishop => {
                *self.board.get_pieces_mut(self.to_move, Bishop) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;
                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            Rook => {
                *self.board.get_pieces_mut(self.to_move, Rook) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;

                match moving_color {
                    White =>
                        if from_sq.idx() == 0 {
                            self.castling_rights.remove(CastlingRights::WHITE_KINGSIDE);
                        } else if from_sq.idx() == 7 {
                            self.castling_rights.remove(CastlingRights::WHITE_QUEENSIDE);
                        },

                    Black =>
                        if from_sq.idx() == 63 {
                            self.castling_rights.remove(CastlingRights::BLACK_QUEENSIDE);
                        } else if from_sq.idx() == 56 {
                            self.castling_rights.remove(CastlingRights::BLACK_KINGSIDE);
                        }
                }

                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            Queen => {
                *self.board.get_pieces_mut(self.to_move, Queen) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;
                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            King => {
                *self.board.get_pieces_mut(self.to_move, King) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;

                match moving_color {
                    White => {
                            self.castling_rights.remove(CastlingRights::WHITE_KINGSIDE);
                            self.castling_rights.remove(CastlingRights::WHITE_QUEENSIDE);
                    }

                    Black => {
                            self.castling_rights.remove(CastlingRights::BLACK_QUEENSIDE);
                            self.castling_rights.remove(CastlingRights::BLACK_KINGSIDE);
                    }
                }

                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },
        }

        if flag != DOUBLE_PAWN_PUSH_FLAG {
            self.ep_square = None;
        }

        if is_capture || moved_piece == Pawn {
            self.fifty_move_count = 0;
        } else {
            // self.fifty_move_count += 1;
        }

        self.to_move = !self.to_move;

    }
}
<commit_msg>added tests for to/from FEN notation<commit_after>use core::*;
use bitboard::*;
use moves::*;
use board::*;
use tables::*;

use std::collections::HashMap;
use std::num;

bitflags! {
    pub struct CastlingRights: u8 {
        const WHITE_KINGSIDE  = 0b0001;
        const WHITE_QUEENSIDE = 0b0010;
        const BLACK_KINGSIDE  = 0b0100;
        const BLACK_QUEENSIDE = 0b1000;
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Game {
    pub board: Board,
    pub to_move: Color,
    pub ep_square: Option<Square>,
    pub castling_rights: CastlingRights,
    pub fifty_move_count: u8,
    pub moves: u16
}

impl Game {
    pub fn starting_position() -> Game {
        Game {
            board: Board::starting_position(),
            to_move: Color::White,
            ep_square: None,
            castling_rights: CastlingRights::all(),
            fifty_move_count: 0,
            moves: 1,
        }
    }

    pub fn empty_position() -> Game {
        Game {
            board: Board::empty_position(),
            to_move: Color::White,
            ep_square: None,
            castling_rights: CastlingRights::empty(),
            fifty_move_count: 0,
            moves: 1
        }
    }

    pub fn to_fen(&self) -> String {
        use PieceType::*;
        use Color::*;

        let mut board_str = String::new();
        let mut empty_tally = 0;

        for idx in (0..64).rev() {
            let sq = Square::new(idx);
            let wrapped_across_row = sq.unwrap() % 8 == 7;

            let maybe_piece = self.board.piece_at(sq);

            if (maybe_piece.is_some() || wrapped_across_row) && empty_tally > 0 {
                assert!(empty_tally <= 8);
                board_str.push_str(&empty_tally.to_string());
                empty_tally = 0;
            }

            if wrapped_across_row && idx < 63 {
                board_str.push('/');
            }

            match maybe_piece {
                Some(piece) => {
                    match (piece.color, piece.ptype) {
                        (Black , Pawn  ) => board_str.push('p'),
                        (Black , Knight) => board_str.push('n'),
                        (Black , Bishop) => board_str.push('b'),
                        (Black , Rook  ) => board_str.push('r'),
                        (Black , Queen ) => board_str.push('q'),
                        (Black , King  ) => board_str.push('k'),
                        (White , Pawn  ) => board_str.push('P'),
                        (White , Knight) => board_str.push('N'),
                        (White , Bishop) => board_str.push('B'),
                        (White , Rook  ) => board_str.push('R'),
                        (White , Queen ) => board_str.push('Q'),
                        (White , King  ) => board_str.push('K'),
                    }
                }
                None => empty_tally += 1
            }
        }

        if empty_tally > 0 {
            board_str.push_str(&empty_tally.to_string());
        }

        let to_move_str = match self.to_move {
            White => "w".to_string(),
            Black => "b".to_string()
        };

        let mut castling_str = String::new();

        if (self.castling_rights == CastlingRights::empty()) {
            castling_str = "-".to_string();
        } else {
            if self.castling_rights.intersects(CastlingRights::WHITE_KINGSIDE) {
                castling_str.push('K');
            }
            if self.castling_rights.intersects(CastlingRights::WHITE_QUEENSIDE) {
                castling_str.push('Q');
            }
            if self.castling_rights.intersects(CastlingRights::BLACK_KINGSIDE) {
                castling_str.push('k');
            }
            if self.castling_rights.intersects(CastlingRights::BLACK_QUEENSIDE) {
                castling_str.push('q');
            }
        }

        let ep_square_str = match self.ep_square {
            Some(sq) => sq.to_algebraic().to_string(),
            None => "-".to_string()
        };

        return [board_str, to_move_str, castling_str, ep_square_str, self.fifty_move_count.to_string(), self.moves.to_string()].join(" ");
    }

    pub fn from_fen(fen: &'static str) -> Option<Game> {
        let words: Vec<&str> = fen.split(' ').collect();

        if words.len() != 6 {
            return None;
        }

        let mut game = Game::empty_position();

        use PieceType::*;
        use Color::*;

        { // build up the game board
            let mut current_square: Square = Square::new(63);

            let decrement_square = |sq: &mut Square, n: u32| {
                if sq.unwrap() > 0 {
                    *sq = Square::new(sq.unwrap() - n);
                }
            };

            let mut add_piece = |color: Color, piece: PieceType, sq: &mut Square| {
                game.board.set_piece_bit(color, piece, *sq);
                decrement_square(sq, 1);
            };

            for ch in words[0].chars() {
                match ch {
                    'p' => add_piece(Black , Pawn   , &mut current_square) ,
                    'n' => add_piece(Black , Knight , &mut current_square) ,
                    'b' => add_piece(Black , Bishop , &mut current_square) ,
                    'r' => add_piece(Black , Rook   , &mut current_square) ,
                    'q' => add_piece(Black , Queen  , &mut current_square) ,
                    'k' => add_piece(Black , King   , &mut current_square) ,
                    'P' => add_piece(White , Pawn   , &mut current_square) ,
                    'N' => add_piece(White , Knight , &mut current_square) ,
                    'B' => add_piece(White , Bishop , &mut current_square) ,
                    'R' => add_piece(White , Rook   , &mut current_square) ,
                    'Q' => add_piece(White , Queen  , &mut current_square) ,
                    'K' => add_piece(White , King   , &mut current_square) ,
                    '1' => decrement_square(&mut current_square, 1),
                    '2' => decrement_square(&mut current_square, 2),
                    '3' => decrement_square(&mut current_square, 3),
                    '4' => decrement_square(&mut current_square, 4),
                    '5' => decrement_square(&mut current_square, 5),
                    '6' => decrement_square(&mut current_square, 6),
                    '7' => decrement_square(&mut current_square, 7),
                    '8' => decrement_square(&mut current_square, 8),
                    '/' => {},
                    _ => return None
                }
            }
        }

        match words[1] {
            "w" => game.to_move = White,
            "b" => game.to_move = Black,
            _ => return None
        }

        for ch in words[2].chars() {
            match ch {
                'K' => game.castling_rights |= CastlingRights::WHITE_KINGSIDE,
                'Q' => game.castling_rights |= CastlingRights::WHITE_QUEENSIDE,
                'k' => game.castling_rights |= CastlingRights::BLACK_KINGSIDE,
                'q' => game.castling_rights |= CastlingRights::BLACK_QUEENSIDE,
                '-' => {},
                _ => return None
            }
        }

        match words[3] {
            "-" => game.ep_square = None,
            _ => match Square::from_algebraic(words[3]) {
                None => return None,
                Some(sq) => game.ep_square = Some(sq)
            }
        }

        match words[4].parse::<u8>() {
            Err(_) => return None,
            Ok(x) => game.fifty_move_count = x
        }

        match words[5].parse::<u16>() {
            Err(_) => return None,
            Ok(x) => game.moves = x
        }

        return Some(game);
    }

    pub fn make_move(&mut self, m: Move) {
        let from_sq        = m.from();
        let from_bit       = from_sq.bitrep();
        let to_sq          = m.to();
        let to_bit         = to_sq.bitrep();
        let from_to_bit    = from_bit | to_bit;
        let is_capture     = m.is_capture();
        let flag           = m.flag();
        let moving_color   = self.to_move;
        let opponent_color = !moving_color;

        use Color::*;
        use PieceType::*;

        let moved_piece = self.board.piece_at(from_sq).unwrap().ptype;
        let captured_piece =
            if flag == EP_CAPTURE_FLAG {
                match opponent_color {
                    White => self.board.piece_at(Square::new(self.ep_square.unwrap().unwrap() + 8)),
                    Black => self.board.piece_at(Square::new(self.ep_square.unwrap().unwrap() - 8))
                }
            } else {
                self.board.piece_at(to_sq)
            };

        assert!(is_capture == captured_piece.is_some());

        //TODO: add moving/captured piece type to Move structure
        match moved_piece {
            Pawn => {
                *self.board.get_pieces_mut(self.to_move, Pawn) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;

                if flag == DOUBLE_PAWN_PUSH_FLAG {
                    self.ep_square = match moving_color {
                        White => Some(Square::new(to_sq.unwrap() - 8)),
                        Black => Some(Square::new(to_sq.unwrap() + 8))
                    }
                }

                // TODO: promotions

                if is_capture {
                    if flag == EP_CAPTURE_FLAG {
                        assert!(self.ep_square.is_some());

                        let captured_bit = match moving_color {
                            White => self.ep_square.unwrap().bitrep().shifted_down(),
                            Black => self.ep_square.unwrap().bitrep().shifted_up()
                        };

                        *self.board.get_pieces_mut(opponent_color, Pawn) ^= captured_bit;
                        *self.board.occupied_by_mut(opponent_color) ^= captured_bit;
                    } else {
                        *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                        *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                    }
                }

            },

            Knight => {
                *self.board.get_pieces_mut(self.to_move, Knight) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;
                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            Bishop => {
                *self.board.get_pieces_mut(self.to_move, Bishop) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;
                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            Rook => {
                *self.board.get_pieces_mut(self.to_move, Rook) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;

                match moving_color {
                    White =>
                        if from_sq.idx() == 0 {
                            self.castling_rights.remove(CastlingRights::WHITE_KINGSIDE);
                        } else if from_sq.idx() == 7 {
                            self.castling_rights.remove(CastlingRights::WHITE_QUEENSIDE);
                        },

                    Black =>
                        if from_sq.idx() == 63 {
                            self.castling_rights.remove(CastlingRights::BLACK_QUEENSIDE);
                        } else if from_sq.idx() == 56 {
                            self.castling_rights.remove(CastlingRights::BLACK_KINGSIDE);
                        }
                }

                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            Queen => {
                *self.board.get_pieces_mut(self.to_move, Queen) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;
                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },

            King => {
                *self.board.get_pieces_mut(self.to_move, King) ^= from_to_bit;
                *self.board.occupied_by_mut(self.to_move) ^= from_to_bit;

                match moving_color {
                    White => {
                            self.castling_rights.remove(CastlingRights::WHITE_KINGSIDE);
                            self.castling_rights.remove(CastlingRights::WHITE_QUEENSIDE);
                    }

                    Black => {
                            self.castling_rights.remove(CastlingRights::BLACK_QUEENSIDE);
                            self.castling_rights.remove(CastlingRights::BLACK_KINGSIDE);
                    }
                }

                if is_capture {
                    *self.board.get_pieces_mut(opponent_color, captured_piece.unwrap().ptype) ^= to_bit;
                    *self.board.occupied_by_mut(opponent_color) ^= to_bit;
                }
            },
        }

        if flag != DOUBLE_PAWN_PUSH_FLAG {
            self.ep_square = None;
        }

        if is_capture || moved_piece == Pawn {
            self.fifty_move_count = 0;
        } else {
            // self.fifty_move_count += 1;
        }

        self.to_move = !self.to_move;

    }
}

#[cfg(test)]
mod test {
    use game::*;
    #[test]
    fn test_fen() {
        let fen_strings: Vec<&'static str> = vec![
            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
            "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
            "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2",
            "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2",
            "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8",
            "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10"
        ];

        for fen in fen_strings.iter() {
            let g = Game::from_fen(fen).unwrap();
            g.board.print();
            println!("{}", g.to_fen());
            assert!(&g.to_fen() == fen);
        }
    }
}
<|endoftext|>


<commit_before>use crate::{
    command_line::{CommandLine, Entry},
    model::{GlobalState, Model, Msg},
    schema::{
        default_renderer, Field, Kind, Schema, ValidatorContext, RUST_FUNCTION_CALL, SCHEMA, *,
    },
    types::{parent, Cursor, Hash, Mode, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
    old_props: Option<NodeProperties>,
    // Memoize callbacks?
    ondelete: Callback<()>,
}

#[derive(Properties, PartialEq, Clone)]
pub struct NodeProperties {
    pub global_state: Rc<GlobalState>,
    pub cursor: Cursor,
    pub selected_path: Vec<Selector>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub allowed_kinds: &'static [&'static str],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        let updatemodel = ctx.props().updatemodel.clone();
        Self {
            input_node_ref: NodeRef::default(),
            old_props: None,
            ondelete: Callback::from(move |()| updatemodel.emit(Msg::Parent)),
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
        let props = ctx.props();
        let global_state = &props.global_state;
        let cursor = &props.cursor;
        let selected = props.selected_path == cursor.path();
        if selected && global_state.mode == Mode::Edit {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let global_state = &props.global_state;
        let node_store = &global_state.node_store;
        let cursor = &props.cursor;
        let hash = &cursor.hash;
        let node = cursor.node(&node_store).unwrap_or(&default_node);
        let path = cursor.path();
        let _oninput = props.oninput.clone();
        let selected_path = &props.selected_path;
        let selected = selected_path == &cursor.path();
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if hash.is_empty() || node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let path = path.clone();
            let entries: Vec<Entry> = props
                .allowed_kinds
                .iter()
                .map(|kind_id| Entry {
                    label: SCHEMA
                        .get_kind(kind_id)
                        .map(|k| &(*k.name))
                        .unwrap_or("INVALID")
                        .to_string(),
                    description: "".to_string(),
                    action: Msg::ReplaceNode(
                        path.clone(),
                        Node {
                            kind: kind_id.to_string(),
                            value: "".to_string(),
                            links: BTreeMap::new(),
                        },
                        false,
                    ),
                    valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                })
                .collect();
            let onenter = {
                let _path = path.clone();
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            let placeholder = if hash.is_empty() {
                "***".to_string()
            } else {
                "".to_string()
            };
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                placeholder={ placeholder }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected && global_state.mode == Mode::Edit }
              />
            }
        } else {
            let renderer = if global_state.rich_render {
                kind.and_then(|k| k.renderer).unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                global_state: global_state.clone(),
                selected_path: selected_path.clone(),
                cursor: cursor.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if global_state.mode == Mode::Edit && selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let mut all_entries = vec![];
                        let mut field_entries = k
                            .get_fields()
                            .iter()
                            .map(|(field_id, field)| Entry {
                                label: field.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(path.to_vec(), **field_id),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect();
                        // TODO: macros
                        let mut macro_entries = vec![
                            Entry {
                                label: "delete".to_string(),
                                description: "".to_string(),
                                action: Msg::DeleteItem,
                                valid_classes: vec![],
                            },
                            // TODO: should apply to literals too.
                            Entry {
                                label: "call".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    path.clone(),
                                    Node {
                                        kind: RUST_FUNCTION_CALL.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "array".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    path.clone(),
                                    Node {
                                        kind: RUST_ARRAY_TYPE.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "move up".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    parent(&path).to_vec(),
                                    node.clone(),
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                        ];
                        all_entries.append(&mut field_entries);
                        all_entries.append(&mut macro_entries);
                        all_entries
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let _onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {
                    <div class="pl-3 absolute bg-white">
                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ self.ondelete.clone() }
                            enabled=true
                        />
                    </div>
                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let path = path;
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {
            <div
              class={ classes.join(" ") }
              tabindex="0"
              onclick={ onclick }
            >
              { inner }
            </div>
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        let same = false;
        log::debug!("Node changed");
        log::debug!("same props: {:?}", same);
        let new_props = ctx.props();
        let same = if let Some(old_props) = &self.old_props {
            log::debug!(
                "same global_state: {:?}",
                old_props.global_state == new_props.global_state
            );
            log::debug!("same cursor: {:?}", old_props.cursor == new_props.cursor);
            log::debug!(
                "same selected_path: {:?}",
                old_props.selected_path == new_props.selected_path
            );
            log::debug!(
                "same updatemodel: {:?}",
                old_props.updatemodel == new_props.updatemodel
            );
            log::debug!("same oninput: {:?}", old_props.oninput == new_props.oninput);
            old_props.global_state == new_props.global_state
                || old_props.cursor == new_props.cursor
                || old_props.selected_path == new_props.selected_path
        } else {
            false
        };
        self.old_props = Some(new_props.clone());
        !same
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        if let Some(i) = input_node.cast::<HtmlInputElement>() {
            i.focus().unwrap();
        }
    }
}
<commit_msg>Node path<commit_after>use crate::{
    command_line::{CommandLine, Entry},
    model::{GlobalState, Model, Msg},
    schema::{
        default_renderer, Field, Kind, Schema, ValidatorContext, RUST_FUNCTION_CALL, SCHEMA, *,
    },
    types::{parent, Cursor, Hash, Mode, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
    old_props: Option<NodeProperties>,
    // Memoize callbacks?
    ondelete: Callback<()>,
}

#[derive(Properties, PartialEq, Clone)]
pub struct NodeProperties {
    pub global_state: Rc<GlobalState>,
    pub cursor: Cursor,
    pub selected_path: Vec<Selector>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub allowed_kinds: &'static [&'static str],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        let updatemodel = ctx.props().updatemodel.clone();
        Self {
            input_node_ref: NodeRef::default(),
            old_props: None,
            ondelete: Callback::from(move |()| updatemodel.emit(Msg::Parent)),
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
        let props = ctx.props();
        let global_state = &props.global_state;
        let cursor = &props.cursor;
        let selected = props.selected_path == cursor.path();
        if selected && global_state.mode == Mode::Edit {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let global_state = &props.global_state;
        let node_store = &global_state.node_store;
        let cursor = &props.cursor;
        let hash = &cursor.hash;
        let node = cursor.node(&node_store).unwrap_or(&default_node);
        let node_path = cursor.path();
        let _oninput = props.oninput.clone();
        let selected_path = &props.selected_path;
        let selected = selected_path == &node_path;
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if hash.is_empty() || node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let node_path = node_path.clone();
            let entries: Vec<Entry> = props
                .allowed_kinds
                .iter()
                .map(|kind_id| Entry {
                    label: SCHEMA
                        .get_kind(kind_id)
                        .map(|k| &(*k.name))
                        .unwrap_or("INVALID")
                        .to_string(),
                    description: "".to_string(),
                    action: Msg::ReplaceNode(
                        node_path.clone(),
                        Node {
                            kind: kind_id.to_string(),
                            value: "".to_string(),
                            links: BTreeMap::new(),
                        },
                        false,
                    ),
                    valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                })
                .collect();
            let onenter = {
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            let placeholder = if hash.is_empty() {
                "***".to_string()
            } else {
                "".to_string()
            };
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                placeholder={ placeholder }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(node_path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected && global_state.mode == Mode::Edit }
              />
            }
        } else {
            let renderer = if global_state.rich_render {
                kind.and_then(|k| k.renderer).unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                global_state: global_state.clone(),
                selected_path: selected_path.clone(),
                cursor: cursor.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if global_state.mode == Mode::Edit && selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let mut all_entries = vec![];
                        let mut field_entries = k
                            .get_fields()
                            .iter()
                            .map(|(field_id, field)| Entry {
                                label: field.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(node_path.to_vec(), **field_id),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect();
                        // TODO: macros
                        let mut macro_entries = vec![
                            Entry {
                                label: "delete".to_string(),
                                description: "".to_string(),
                                action: Msg::DeleteItem,
                                valid_classes: vec![],
                            },
                            // TODO: should apply to literals too.
                            Entry {
                                label: "call".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    node_path.clone(),
                                    Node {
                                        kind: RUST_FUNCTION_CALL.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "array".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    node_path.clone(),
                                    Node {
                                        kind: RUST_ARRAY_TYPE.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "move up".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    parent(&node_path).to_vec(),
                                    node.clone(),
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                        ];
                        all_entries.append(&mut field_entries);
                        all_entries.append(&mut macro_entries);
                        all_entries
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let _onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {
                    <div class="pl-3 absolute bg-white">
                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ self.ondelete.clone() }
                            enabled=true
                        />
                    </div>
                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let node_path = node_path;
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(node_path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {
            <div
              class={ classes.join(" ") }
              tabindex="0"
              onclick={ onclick }
            >
              { inner }
            </div>
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        log::debug!("Node changed");
        let new_props = ctx.props();
        let same = if let Some(old_props) = &self.old_props {
            log::debug!(
                "same global_state: {:?}",
                old_props.global_state == new_props.global_state
            );
            log::debug!("same cursor: {:?}", old_props.cursor == new_props.cursor);
            log::debug!(
                "same selected_path: {:?}",
                old_props.selected_path == new_props.selected_path
            );
            log::debug!("new selected_path: {:?}", new_props.selected_path);
            log::debug!(
                "same updatemodel: {:?}",
                old_props.updatemodel == new_props.updatemodel
            );
            log::debug!("same oninput: {:?}", old_props.oninput == new_props.oninput);
            old_props.global_state == new_props.global_state
                || old_props.cursor == new_props.cursor
                || old_props.selected_path == new_props.selected_path
        } else {
            false
        };
        self.old_props = Some(new_props.clone());
        !same
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        if let Some(i) = input_node.cast::<HtmlInputElement>() {
            i.focus().unwrap();
        }
    }
}
<|endoftext|>


<commit_before>//! Networking code for sending and receiving fixed size `Vec<u8>` between machines.

use std::io::{Read, Result};
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::thread;
use std::thread::sleep;
use std::time::Duration;

use abomonation::{encode, decode};

/// Framing data for each `Vec<u8>` transmission, indicating a typed channel, the source and
/// destination workers, and the length in bytes.
#[derive(Abomonation, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct MessageHeader {
    /// index of channel.
    pub channel:    usize,
    /// index of worker sending message.
    pub source:     usize,
    /// index of worker receiving message.
    pub target:     usize,
    /// number of bytes in message.
    pub length:     usize,
    /// sequence number.
    pub seqno:      usize,
}

impl MessageHeader {
    /// Returns a header when there is enough supporting data
    #[inline(always)]
    pub fn try_read(bytes: &mut [u8]) -> Option<MessageHeader> {
        unsafe { decode::<MessageHeader>(bytes) }
            .and_then(|(header, remaining)| {
                if remaining.len() >= header.length {
                    Some(header.clone())
                }
                else {
                    None
                }
            })
    }

    /// Writes the header as binary data.
    #[inline(always)]
    pub fn write_to<W: ::std::io::Write>(&self, writer: &mut W) -> ::std::io::Result<()> {
        unsafe { encode(self, writer) }
    }

    /// The number of bytes required for the header and data.
    #[inline(always)]
    pub fn required_bytes(&self) -> usize {
        ::std::mem::size_of::<MessageHeader>() + self.length
    }
}

/// Creates socket connections from a list of host addresses.
///
/// The item at index i in the resulting vec, is a Some(TcpSocket) to process i, except
/// for item `my_index` which is None (no socket to self).
pub fn create_sockets(addresses: Vec<String>, my_index: usize, noisy: bool) -> Result<Vec<Option<TcpStream>>> {

    let hosts1 = Arc::new(addresses);
    let hosts2 = hosts1.clone();

    let start_task = thread::spawn(move || start_connections(hosts1, my_index, noisy));
    let await_task = thread::spawn(move || await_connections(hosts2, my_index, noisy));

    let mut results = start_task.join().unwrap()?;
    results.push(None);
    let to_extend = await_task.join().unwrap()?;
    results.extend(to_extend.into_iter());

    if noisy { println!("worker {}:\tinitialization complete", my_index) }

    Ok(results)
}

/// Result contains connections [0, my_index - 1].
pub fn start_connections(addresses: Arc<Vec<String>>, my_index: usize, noisy: bool) -> Result<Vec<Option<TcpStream>>> {
    let mut results: Vec<_> = (0..my_index).map(|_| None).collect();
    for index in 0..my_index {
        let mut connected = false;
        while !connected {
            match TcpStream::connect(&addresses[index][..]) {
                Ok(mut stream) => {
                    stream.set_nodelay(true).expect("set_nodelay call failed");
                    unsafe { encode(&(my_index as u64), &mut stream) }.expect("failed to encode/send worker index");
                    results[index as usize] = Some(stream);
                    if noisy { println!("worker {}:\tconnection to worker {}", my_index, index); }
                    connected = true;
                },
                Err(error) => {
                    println!("worker {}:\terror connecting to worker {}: {}; retrying", my_index, index, error);
                    sleep(Duration::from_secs(1));
                },
            }
        }
    }

    Ok(results)
}

/// Result contains connections [my_index + 1, addresses.len() - 1].
pub fn await_connections(addresses: Arc<Vec<String>>, my_index: usize, noisy: bool) -> Result<Vec<Option<TcpStream>>> {
    let mut results: Vec<_> = (0..(addresses.len() - my_index - 1)).map(|_| None).collect();
    let listener = try!(TcpListener::bind(&addresses[my_index][..]));

    for _ in (my_index + 1) .. addresses.len() {
        let mut stream = listener.accept()?.0;
        stream.set_nodelay(true).expect("set_nodelay call failed");
        let mut buffer = [0u8;8];
        stream.read_exact(&mut buffer).expect("failed to read worker index");
        let identifier = unsafe { decode::<u64>(&mut buffer) }.expect("failed to decode worker index").0.clone() as usize;
        results[identifier - my_index - 1] = Some(stream);
        if noisy { println!("worker {}:\tconnection from worker {}", my_index, identifier); }
    }

    Ok(results)
}
<commit_msg>Avoid some mutable state in communication::networking::start_connections<commit_after>//! Networking code for sending and receiving fixed size `Vec<u8>` between machines.

use std::io::{Read, Result};
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::thread;
use std::thread::sleep;
use std::time::Duration;

use abomonation::{encode, decode};

/// Framing data for each `Vec<u8>` transmission, indicating a typed channel, the source and
/// destination workers, and the length in bytes.
#[derive(Abomonation, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct MessageHeader {
    /// index of channel.
    pub channel:    usize,
    /// index of worker sending message.
    pub source:     usize,
    /// index of worker receiving message.
    pub target:     usize,
    /// number of bytes in message.
    pub length:     usize,
    /// sequence number.
    pub seqno:      usize,
}

impl MessageHeader {
    /// Returns a header when there is enough supporting data
    #[inline(always)]
    pub fn try_read(bytes: &mut [u8]) -> Option<MessageHeader> {
        unsafe { decode::<MessageHeader>(bytes) }
            .and_then(|(header, remaining)| {
                if remaining.len() >= header.length {
                    Some(header.clone())
                }
                else {
                    None
                }
            })
    }

    /// Writes the header as binary data.
    #[inline(always)]
    pub fn write_to<W: ::std::io::Write>(&self, writer: &mut W) -> ::std::io::Result<()> {
        unsafe { encode(self, writer) }
    }

    /// The number of bytes required for the header and data.
    #[inline(always)]
    pub fn required_bytes(&self) -> usize {
        ::std::mem::size_of::<MessageHeader>() + self.length
    }
}

/// Creates socket connections from a list of host addresses.
///
/// The item at index i in the resulting vec, is a Some(TcpSocket) to process i, except
/// for item `my_index` which is None (no socket to self).
pub fn create_sockets(addresses: Vec<String>, my_index: usize, noisy: bool) -> Result<Vec<Option<TcpStream>>> {

    let hosts1 = Arc::new(addresses);
    let hosts2 = hosts1.clone();

    let start_task = thread::spawn(move || start_connections(hosts1, my_index, noisy));
    let await_task = thread::spawn(move || await_connections(hosts2, my_index, noisy));

    let mut results = start_task.join().unwrap()?;
    results.push(None);
    let to_extend = await_task.join().unwrap()?;
    results.extend(to_extend.into_iter());

    if noisy { println!("worker {}:\tinitialization complete", my_index) }

    Ok(results)
}

/// Result contains connections [0, my_index - 1].
pub fn start_connections(addresses: Arc<Vec<String>>, my_index: usize, noisy: bool) -> Result<Vec<Option<TcpStream>>> {
    let results = addresses.iter().take(my_index).enumerate().map(|(index, address)| {
        loop {
            match TcpStream::connect(address) {
                Ok(mut stream) => {
                    stream.set_nodelay(true).expect("set_nodelay call failed");
                    unsafe { encode(&(my_index as u64), &mut stream) }.expect("failed to encode/send worker index");
                    if noisy { println!("worker {}:\tconnection to worker {}", my_index, index); }
                    break Some(stream);
                },
                Err(error) => {
                    println!("worker {}:\terror connecting to worker {}: {}; retrying", my_index, index, error);
                    sleep(Duration::from_secs(1));
                },
            }
        }
    }).collect();

    Ok(results)
}

/// Result contains connections [my_index + 1, addresses.len() - 1].
pub fn await_connections(addresses: Arc<Vec<String>>, my_index: usize, noisy: bool) -> Result<Vec<Option<TcpStream>>> {
    let mut results: Vec<_> = (0..(addresses.len() - my_index - 1)).map(|_| None).collect();
    let listener = try!(TcpListener::bind(&addresses[my_index][..]));

    for _ in (my_index + 1) .. addresses.len() {
        let mut stream = listener.accept()?.0;
        stream.set_nodelay(true).expect("set_nodelay call failed");
        let mut buffer = [0u8;8];
        stream.read_exact(&mut buffer).expect("failed to read worker index");
        let identifier = unsafe { decode::<u64>(&mut buffer) }.expect("failed to decode worker index").0.clone() as usize;
        results[identifier - my_index - 1] = Some(stream);
        if noisy { println!("worker {}:\tconnection from worker {}", my_index, identifier); }
    }

    Ok(results)
}
<|endoftext|>