<commit_before>use card_strategy::CardStrategy;

use card::Card;
use card::Suit;
use card::Rank;
use game_status::GameStatus;
use player::PlayerName;

use std::collections::BTreeMap;
use std::collections::BTreeSet;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

/*
    Play 2 of clubs if in hand.
    If going to win a deal, do so with highest ranking card.
*/
impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus) -> (i32, i32, i32, i32, i32, i32, i32) {
        let potential_points = Self::potential_points(card, game_status);
        let definite_points = if Self::will_win_deal(card, game_status) {
            potential_points
        } else {
            0
        };
        let later_potential_points = 0 - Self::later_potential_points(card, game_status);
        let card_penalty_to_me = Self::card_penalty_to_me(card, game_status);
        let card_penalty = 0 - Self::card_penalty(card, game_status);
        let trouble = Self::trouble_score(card, game_status);
        let card_rank = 0 - (u32::from(card.rank) as i32);
        (definite_points, potential_points, later_potential_points, card_penalty_to_me, card_penalty, trouble, card_rank)
    }

    fn card_penalty(card: &Card, game_status: &GameStatus) -> i32 {
        game_status.round_parameters.card_points.get(card)
            .map(|penalty| *penalty)
            .unwrap_or_default()
    }

    fn card_penalty_to_me(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            Self::card_penalty(card, game_status)
        } else {
            0
        }
    }

    fn trouble_score(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            let trouble = u32::from(card.rank) as i32;
            if Self::card_penalty_to_me(card, game_status) < 0 {
                0 - trouble
            } else {
                if Self::will_win_deal(card, game_status) {
                    u32::from(Rank::Ace) as i32
                } else {
                    trouble
                }
            }
        } else {
            0
        }
    }

    fn can_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        game_status.my_in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }

    fn will_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        Self::can_win_deal(card, game_status) && (Self::plays_left(game_status).len() == 0 ||
            Self::deal_suit(game_status)
                .and_then(|suit| Self::remaining_cards(game_status).iter()
                    .filter(|card| card.suit == *suit)
                    .max()
                    .map(|winning_card| card.rank > winning_card.rank)).unwrap_or(true))
    }

    fn deal_suit(game_status: &GameStatus) -> Option<&Suit> {
        game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit.as_ref())
    }

    fn plays_left(game_status: &GameStatus) -> BTreeSet<&PlayerName> {
        let mut players: BTreeSet<&PlayerName> = game_status.my_game_players.iter()
            .filter(|player| !player.has_turn)
            .map(|player| &player.team_name)
            .collect();

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                players.remove(&deal_card.player_name);
            }
        }

        players
    }

    fn remaining_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        let mut cards = Card::all();

        for deal in &game_status.my_game_deals {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        for card in &game_status.my_current_hand {
            cards.remove(&card);
        }

        cards
    }

    fn dealt_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        game_status.my_in_progress_deal.as_ref()
            .map(|deal| deal.deal_cards.iter().map(|deal_card| deal_card.card).collect())
            .unwrap_or_default()
    }

    fn potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            let cards = Self::remaining_cards(game_status);

            let dealt_points: i32 = Self::dealt_cards(game_status).iter()
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let suit_points: i32 = cards.iter()
                .filter(|other| other.suit == card.suit)
                .filter(|other| other.rank < card.rank)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let other_points: i32 = cards.iter()
                .filter(|other| other.suit != card.suit)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let suit_win_points = (Self::chance_of_win(card, game_status) * (suit_points as f32)) as i32;
            let other_win_points = (Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32;

            dealt_points + suit_win_points + other_win_points
        } else {
            0
        }
    }

    fn later_potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        let cards = Self::remaining_cards(game_status);

        let suit_points: i32 = cards.iter()
            .filter(|other| other.suit == card.suit)
            .filter(|other| other.rank < card.rank)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        let other_points: i32 = cards.iter()
            .filter(|other| other.suit != card.suit)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        suit_points + ((Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32)
    }

    fn chance_of_later_win(card: &Card, game_status: &GameStatus) -> f32 {
        let cards = Self::remaining_cards(game_status);
        let suit_cards = cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
        let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
        if suit_cards.is_empty() {
            1.0
        } else {
            (will_win_count as f32) / (suit_cards.len() as f32)
        }
    }

    fn chance_of_win(card: &Card, game_status: &GameStatus) -> f32 {
        if Self::will_win_deal(card, game_status) {
            1.0
        } else {
            if Self::deal_suit(game_status).map(|suit| suit == &card.suit).unwrap_or(true) && Self::plays_left(game_status).len() > 0 {
                let cards = Self::remaining_cards(game_status);
                let suit_cards = cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
                let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
                if suit_cards.is_empty() {
                    1.0
                } else {
                    (will_win_count as f32) / (suit_cards.len() as f32)
                }
            } else {
                0.0
            }
        }
    }

}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        game_status.my_initial_hand.iter()
            .map(|card| ((0 - Self::later_potential_points(card, game_status), card), card))
            .collect::<BTreeMap<_, &Card>>()
            .into_iter()
            .map(|kv| kv.1)
            .take(3)
            .collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let current_suit = game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit);
        let mut valid_cards = game_status.my_current_hand.iter()
            .filter(|card| Some(card.suit) == current_suit)
            .collect::<Vec<&Card>>();

        if valid_cards.is_empty() {
            valid_cards.extend(&game_status.my_current_hand);
        }

        let evaluation = valid_cards.into_iter()
            .map(|card| ((Self::score_card(card, game_status), card), card))
            .collect::<BTreeMap<_,&Card>>();

        // println!("Evaluation: {:?}", evaluation);

        evaluation.values()
            .next()
            .expect("No valid cards to play!")
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use player::PlayerName;
    use card::Card;
    use card::Rank::*;
    use card::Suit::*;
    use game_status::GameStatus;
    use game_status::dto::GameStatusDto;
    use card_strategy::CardStrategy;

    use try_from::TryFrom;
    use error::Error;

    extern crate serde;
    extern crate serde_json;

    use std::fs::File;
    use std::io::Read;

    fn open_scenario(name: &str) -> GameStatus {
        let file_name = format!("samples/scenarios/{}.json", name);
        let mut game_status_file = File::open(file_name).unwrap();
        let mut game_status_string = String::new();
        game_status_file.read_to_string(&mut game_status_string).unwrap();
        let game_status_dto: GameStatusDto = serde_json::from_str(&game_status_string).map_err(Error::from).unwrap();
        GameStatus::try_from(game_status_dto).unwrap()
    }

    fn should_play(name: &str, expected_card: Card) {
        let player_name = PlayerName::new("Derek Williams");
        let game_status = open_scenario(name);
        let card = DefensiveCardStrategy.play_card(&game_status, &player_name).clone();
        assert_eq!(expected_card, card);
    }

    #[test]
    fn normal_1() {
        should_play("normal 1", Jack.of(Diamond));
    }

    #[test]
    fn normal_2() {
        should_play("normal 2", King.of(Club));
    }

    #[test]
    fn normal_3() {
        should_play("normal 3", Six.of(Spade));
    }

    #[test]
    fn should_play_heart_1() {
        should_play("should play heart 1", Seven.of(Heart));
    }

    #[test]
    fn should_play_heart_2() {
        should_play("should play heart 2", Four.of(Heart));
    }

    #[test]
    fn should_play_high_rank_1() {
        should_play("should play high rank 1", King.of(Diamond));
    }

    #[test]
    fn should_play_high_rank_2() {
        should_play("should play high rank 2", Ace.of(Spade));
    }

    // #[test]
    // fn should_play_high_rank_3() { // Should get rid of high risk high rank
    //     should_play("should play high rank 3", Ace.of(Spade));
    // }

    #[test]
    fn should_not_crash_during_card_play() {
        should_play("crashed during card play", Ten.of(Heart));
    }

    #[test]
    fn should_play_low_negative_points_card_1() {
        should_play("should play low negative points card 1", Two.of(Diamond));
    }

    #[test]
    fn should_play_high_negative_points_card_1() {
        should_play("should play high negative points card 1", Ace.of(Diamond));
    }

}<commit_msg>add dev notes<commit_after>use card_strategy::CardStrategy;

use card::Card;
use card::Suit;
use card::Rank;
use game_status::GameStatus;
use player::PlayerName;

use std::collections::BTreeMap;
use std::collections::BTreeSet;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

/*
    Play 2 of clubs if in hand.
    If going to win a deal, do so with highest ranking card.
*/
impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus) -> (i32, i32, i32, i32, i32, i32, i32) {
        let potential_points = Self::potential_points(card, game_status);
        let definite_points = if Self::will_win_deal(card, game_status) {
            potential_points
        } else {
            0
        };
        let later_potential_points = 0 - Self::later_potential_points(card, game_status);
        let card_penalty_to_me = Self::card_penalty_to_me(card, game_status);
        let card_penalty = 0 - Self::card_penalty(card, game_status);
        let trouble = Self::trouble_score(card, game_status);
        let card_rank = 0 - (u32::from(card.rank) as i32);
        (definite_points, potential_points, later_potential_points, card_penalty_to_me, card_penalty, trouble, card_rank)
    }

    fn card_penalty(card: &Card, game_status: &GameStatus) -> i32 {
        game_status.round_parameters.card_points.get(card)
            .map(|penalty| *penalty)
            .unwrap_or_default()
    }

    fn card_penalty_to_me(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            Self::card_penalty(card, game_status)
        } else {
            0
        }
    }

    fn trouble_score(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            let trouble = u32::from(card.rank) as i32;
            if Self::card_penalty_to_me(card, game_status) < 0 {
                0 - trouble
            } else {
                if Self::will_win_deal(card, game_status) {
                    u32::from(Rank::Ace) as i32
                } else {
                    trouble
                }
            }
        } else {
            0
        }
    }

    fn can_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        game_status.my_in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }

    fn will_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        Self::can_win_deal(card, game_status) && (Self::plays_left(game_status).len() == 0 ||
            Self::deal_suit(game_status)
                .and_then(|suit| Self::remaining_cards(game_status).iter()
                    .filter(|card| card.suit == *suit)
                    .max()
                    .map(|winning_card| card.rank > winning_card.rank)).unwrap_or(true))
    }

    fn deal_suit(game_status: &GameStatus) -> Option<&Suit> {
        game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit.as_ref())
    }

    fn plays_left(game_status: &GameStatus) -> BTreeSet<&PlayerName> {
        let mut players: BTreeSet<&PlayerName> = game_status.my_game_players.iter()
            .filter(|player| !player.has_turn)
            .map(|player| &player.team_name)
            .collect();

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                players.remove(&deal_card.player_name);
            }
        }

        players
    }

    fn remaining_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        let mut cards = Card::all();

        for deal in &game_status.my_game_deals {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        for card in &game_status.my_current_hand {
            cards.remove(&card);
        }

        cards
    }

    fn dealt_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        game_status.my_in_progress_deal.as_ref()
            .map(|deal| deal.deal_cards.iter().map(|deal_card| deal_card.card).collect())
            .unwrap_or_default()
    }

    // FIXME: needs to take into account card that will be played!
    fn potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            let cards = Self::remaining_cards(game_status);

            let dealt_points: i32 = Self::dealt_cards(game_status).iter()
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let suit_points: i32 = cards.iter()
                .filter(|other| other.suit == card.suit)
                .filter(|other| other.rank < card.rank)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let other_points: i32 = cards.iter()
                .filter(|other| other.suit != card.suit)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let suit_win_points = (Self::chance_of_win(card, game_status) * (suit_points as f32)) as i32;
            let other_win_points = (Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32;

            dealt_points + suit_win_points + other_win_points
        } else {
            0
        }
    }

    fn later_potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        let cards = Self::remaining_cards(game_status);

        let suit_points: i32 = cards.iter()
            .filter(|other| other.suit == card.suit)
            .filter(|other| other.rank < card.rank)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        let other_points: i32 = cards.iter()
            .filter(|other| other.suit != card.suit)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        suit_points + ((Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32)
    }

    fn chance_of_later_win(card: &Card, game_status: &GameStatus) -> f32 {
        let cards = Self::remaining_cards(game_status);
        let suit_cards = cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
        let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
        if suit_cards.is_empty() {
            1.0
        } else {
            (will_win_count as f32) / (suit_cards.len() as f32)
        }
    }

    fn chance_of_win(card: &Card, game_status: &GameStatus) -> f32 {
        if Self::will_win_deal(card, game_status) {
            1.0
        } else {
            if Self::deal_suit(game_status).map(|suit| suit == &card.suit).unwrap_or(true) && Self::plays_left(game_status).len() > 0 {
                let cards = Self::remaining_cards(game_status);
                let suit_cards = cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
                let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
                if suit_cards.is_empty() {
                    1.0
                } else {
                    (will_win_count as f32) / (suit_cards.len() as f32)
                }
            } else {
                0.0
            }
        }
    }

}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        game_status.my_initial_hand.iter()
            .map(|card| ((0 - Self::later_potential_points(card, game_status), card), card))
            .collect::<BTreeMap<_, &Card>>()
            .into_iter()
            .map(|kv| kv.1)
            .take(3)
            .collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let current_suit = game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit);
        let mut valid_cards = game_status.my_current_hand.iter()
            .filter(|card| Some(card.suit) == current_suit)
            .collect::<Vec<&Card>>();

        if valid_cards.is_empty() {
            valid_cards.extend(&game_status.my_current_hand);
        }

        let evaluation = valid_cards.into_iter()
            .map(|card| ((Self::score_card(card, game_status), card), card))
            .collect::<BTreeMap<_,&Card>>();

        // println!("Evaluation: {:?}", evaluation);

        evaluation.values()
            .next()
            .expect("No valid cards to play!")
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use player::PlayerName;
    use card::Card;
    use card::Rank::*;
    use card::Suit::*;
    use game_status::GameStatus;
    use game_status::dto::GameStatusDto;
    use card_strategy::CardStrategy;

    use try_from::TryFrom;
    use error::Error;

    extern crate serde;
    extern crate serde_json;

    use std::fs::File;
    use std::io::Read;

    fn open_scenario(name: &str) -> GameStatus {
        let file_name = format!("samples/scenarios/{}.json", name);
        let mut game_status_file = File::open(file_name).unwrap();
        let mut game_status_string = String::new();
        game_status_file.read_to_string(&mut game_status_string).unwrap();
        let game_status_dto: GameStatusDto = serde_json::from_str(&game_status_string).map_err(Error::from).unwrap();
        GameStatus::try_from(game_status_dto).unwrap()
    }

    fn should_play(name: &str, expected_card: Card) {
        let player_name = PlayerName::new("Derek Williams");
        let game_status = open_scenario(name);
        let card = DefensiveCardStrategy.play_card(&game_status, &player_name).clone();
        assert_eq!(expected_card, card);
    }

    #[test]
    fn normal_1() {
        should_play("normal 1", Jack.of(Diamond));
    }

    #[test]
    fn normal_2() {
        should_play("normal 2", King.of(Club));
    }

    #[test]
    fn normal_3() {
        should_play("normal 3", Six.of(Spade));
    }

    #[test]
    fn should_play_heart_1() {
        should_play("should play heart 1", Seven.of(Heart));
    }

    #[test]
    fn should_play_heart_2() {
        should_play("should play heart 2", Four.of(Heart));
    }

    #[test]
    fn should_play_high_rank_1() {
        should_play("should play high rank 1", King.of(Diamond));
    }

    #[test]
    fn should_play_high_rank_2() {
        should_play("should play high rank 2", Ace.of(Spade));
    }

    // #[test]
    // fn should_play_high_rank_3() { // Should get rid of high risk high rank
    //     should_play("should play high rank 3", Ace.of(Spade));
    // }

    #[test]
    fn should_not_crash_during_card_play() {
        should_play("crashed during card play", Ten.of(Heart));
    }

    #[test]
    fn should_play_low_negative_points_card_1() {
        should_play("should play low negative points card 1", Two.of(Diamond));
    }

    #[test]
    fn should_play_high_negative_points_card_1() {
        should_play("should play high negative points card 1", Ace.of(Diamond));
    }

}<|endoftext|>


<commit_before>use super::*;
use std::sync::atomic::fence;
use std::sync::atomic::Ordering::AcqRel;
use std::sync::atomic::Ordering::Acquire;
use std::sync::atomic::Ordering::Release;

pub enum InsertSearchResult {
    External,
    Internal(NodeCellRef),
    RightNode(NodeCellRef),
}

pub enum InsertToNodeResult {
    NoSplit,
    Split(NodeWriteGuard, NodeCellRef, Option<EntryKey>),
    SplitParentChanged,
}

pub enum RemoveSearchResult {
    External,
    RightNode(NodeCellRef),
    Internal(NodeCellRef),
}

pub enum SubNodeStatus {
    ExtNodeEmpty,
    InNodeEmpty,
    Relocate(usize, usize),
    Merge(usize, usize),
    Ok,
}

pub struct NodeSplit {
    pub new_right_node: NodeCellRef,
    pub left_node_latch: NodeWriteGuard,
    pub pivot: EntryKey,
    pub parent_latch: NodeWriteGuard,
}

pub struct RebalancingNodes {
    pub left_guard: NodeWriteGuard,
    pub left_ref: NodeCellRef,
    pub right_guard: NodeWriteGuard,
    pub right_right_guard: NodeWriteGuard, // for external pointer modification
    pub parent: NodeWriteGuard,
    pub parent_pos: usize,
}

pub struct RemoveResult {
    pub rebalancing: Option<RebalancingNodes>,
    pub empty: bool,
    pub removed: bool,
}

pub enum NodeData {
    External(Box<ExtNode>),
    Internal(Box<InNode>),
    Empty(Box<EmptyNode>),
    None,
}

pub struct EmptyNode {
    pub left: Option<NodeCellRef>,
    pub right: NodeCellRef,
}

impl NodeData {
    pub fn is_none(&self) -> bool {
        match self {
            &NodeData::None => true,
            _ => false,
        }
    }
    pub fn is_empty(&self) -> bool {
        match self {
            &NodeData::Empty(ref n) => true,
            &NodeData::External(ref n) => n.len == 0,
            &NodeData::Internal(ref n) => n.len == 0,
            &NodeData::None => unreachable!(),
        }
    }
    pub fn is_empty_node(&self) -> bool {
        match self {
            &NodeData::Empty(ref n) => true,
            _ => false
        }
    }
    pub fn search(&self, key: &EntryKey) -> usize {
        let len = self.len();
        if self.is_ext() {
            self.extnode().search(key)
        } else {
            self.innode().search(key)
        }
    }

    pub fn remove(&mut self, pos: usize) {
        match self {
            &mut NodeData::External(ref mut node) => node.remove_at(pos),
            &mut NodeData::Internal(ref mut node) => node.remove_at(pos),
            &mut NodeData::None | &mut NodeData::Empty(_) => unreachable!(),
        }
    }
    pub fn is_ext(&self) -> bool {
        match self {
            &NodeData::External(_) => true,
            &NodeData::Internal(_) => false,
            &NodeData::None | &NodeData::Empty(_) => panic!(),
        }
    }
    pub fn keys(&self) -> &[EntryKey] {
        if self.is_ext() {
            &self.extnode().keys
        } else {
            &self.innode().keys
        }
    }

    pub fn first_key(&self) -> &EntryKey {
        &self.keys()[0]
    }

    pub fn last_key(&self) -> &EntryKey {
        &self.keys()[self.len() - 1]
    }

    pub fn len(&self) -> usize {
        match self {
            &NodeData::Internal(ref node) => node.len,
            &NodeData::External(ref node) => node.len,
            &NodeData::Empty(_) => 0,
            &NodeData::None => unreachable!(),
        }
    }

    // check if the node will be half full after an item have been removed
    pub fn is_half_full(&self) -> bool {
        if self.is_none() {
            true
        } else {
            let len = self.len();
            if len == 0 {
                return false;
            }
            len >= NUM_KEYS / 2 && len > 1
        }
    }

    pub fn cannot_merge(&self) -> bool {
        self.len() > NUM_KEYS / 2
    }

    pub fn extnode_mut(&mut self) -> &mut ExtNode {
        match self {
            &mut NodeData::External(ref mut node) => node,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn innode_mut(&mut self) -> &mut InNode {
        match self {
            &mut NodeData::Internal(ref mut n) => n,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn extnode(&self) -> &ExtNode {
        match self {
            &NodeData::External(ref node) => node,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn ext_id(&self) -> Id {
        match self {
            &NodeData::External(ref node) => node.id,
            &NodeData::None => Id::unit_id(),
            &NodeData::Internal(_) | &NodeData::Empty(_) => unreachable!(self.type_name()),
        }
    }
    pub fn innode(&self) -> &InNode {
        match self {
            &NodeData::Internal(ref n) => n,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn type_name(&self) -> &'static str {
        match self {
            &NodeData::Internal(_) => "internal",
            &NodeData::External(_) => "external",
            &NodeData::None => "none",
            &NodeData::Empty(_) => "empty",
        }
    }
    pub fn key_at_right_node(&self, key: &EntryKey) -> Option<&NodeCellRef> {
        if self.is_empty_node() || self.len() > 0 && self.last_key() < key {
            let right_node = self.right_ref().unwrap().read_unchecked();
            if !right_node.is_none() && (self.is_empty_node() || right_node.len() > 0 && right_node.first_key() <= key) {
                debug!(
                    "found key to put to right page {:?}/{:?}",
                    key, right_node.first_key()
                );
                return Some(self.right_ref().unwrap())
            }
        }
        return None;
    }

    pub fn left_ref_mut(&mut self) -> Option<&mut NodeCellRef> {
        match self {
            &mut NodeData::External(ref mut n) => Some(&mut n.prev),
            _ => None,
        }
    }

    pub fn left_ref(&self) -> Option<&NodeCellRef> {
        match self {
            &NodeData::External(ref n) => Some(&n.prev),
            _ => None,
        }
    }

    pub fn right_ref_mut(&mut self) -> Option<&mut NodeCellRef> {
        match self {
            &mut NodeData::External(ref mut n) => Some(&mut n.next),
            &mut NodeData::Internal(ref mut n) => Some(&mut n.right),
            &mut NodeData::Empty(ref mut n) => Some(&mut n.right),
            &mut NodeData::None => None,
        }
    }

    pub fn right_ref(&self) -> Option<&NodeCellRef> {
        match self {
            &NodeData::External(ref n) => Some(&n.next),
            &NodeData::Internal(ref n) => Some(&n.right),
            &NodeData::Empty(ref n) => Some(&n.right),
            &NodeData::None => None,
        }
    }

    pub fn has_vaild_right_node(&self) -> bool {
        self.right_ref().is_some()
    }
}

pub fn write_key_page(
    search_page: NodeWriteGuard,
    search_ref: &NodeCellRef,
    key: &EntryKey,
) -> (NodeWriteGuard, NodeCellRef) {
    if search_page.is_empty_node() || search_page.len() > 0 && search_page.last_key() < key {
        let right_ref = search_page.right_ref().unwrap();
        let right_node = write_node(right_ref);
        if !right_node.is_none() && (search_page.is_empty_node() || right_node.len() > 0 && right_node.first_key() <= key) {
            debug!("Will write {:?} from left {} node to right page, start with {:?}, keys {:?}",
                   key, search_page.type_name(),
                   if right_node.is_empty_node() { smallvec!(0) } else { right_node.first_key().clone() },
                   right_node.keys());
            return write_key_page(right_node, right_ref, key);
        }
    }
    return (search_page, search_ref.clone());
}

const LATCH_FLAG: usize = !(!0 >> 1);

pub struct Node {
    data: UnsafeCell<NodeData>,
    cc: AtomicUsize,
}

impl Node {
    pub fn new(data: NodeData) -> Self {
        Node {
            data: UnsafeCell::new(data),
            cc: AtomicUsize::new(0),
        }
    }

    pub fn internal(innode: InNode) -> Self {
        Self::new(NodeData::Internal(box innode))
    }

    pub fn external(extnode: ExtNode) -> Self {
        Self::new(NodeData::External(box extnode))
    }

    pub fn none() -> Self {
        Self::new(NodeData::None)
    }
    pub fn none_ref() -> NodeCellRef {
        Arc::new(Node::none())
    }
    pub fn new_external(id: Id) -> Self {
        Self::external(ExtNode::new(id))
    }

    pub fn read_unchecked(&self) -> &NodeData {
        unsafe { &*self.data.get() }
    }

    pub fn version(&self) -> usize {
        node_version(self.cc.load(SeqCst))
    }
}

pub fn node_version(cc_num: usize) -> usize {
    cc_num & (!LATCH_FLAG)
}

pub fn write_node(node: &NodeCellRef) -> NodeWriteGuard {
    // debug!("acquiring node write lock");
    let cc = &node.cc;
    loop {
        let cc_num = cc.load(Relaxed);
        let expected = cc_num & (!LATCH_FLAG);
        debug_assert_eq!(expected & LATCH_FLAG, 0);
        match cc.compare_exchange_weak(expected, cc_num | LATCH_FLAG, Acquire, Relaxed) {
            Ok(num) if num == expected => {
                return NodeWriteGuard {
                    data: node.data.get(),
                    cc: &node.cc as *const AtomicUsize,
                    version: node_version(cc_num),
                    node_ref: node.clone()
                }
            }
            _ => {}
        }
    }
}

pub fn read_node<'a, F: FnMut(&NodeReadHandler) -> R + 'a, R: 'a>(node: &NodeCellRef, mut func: F) -> R {
    let mut handler = NodeReadHandler {
        ptr: node.data.get(),
        version: 0,
        node_ref: node.clone()
    };
    let cc = &node.cc;
    loop {
        let cc_num = cc.load(SeqCst);
        if cc_num & LATCH_FLAG == LATCH_FLAG {
            // debug!("read have a latch, retry {:b}", cc_num);
            continue;
        }
        handler.version = cc_num & (!LATCH_FLAG);
        let res = func(&handler);
        let new_cc_num = cc.load(SeqCst);
        if new_cc_num == cc_num {
            return res;
        }
        // debug!("read version changed, retry {:b}", cc_num);
    }
}

pub struct NodeWriteGuard {
    pub data: *mut NodeData,
    pub cc: *const AtomicUsize,
    pub version: usize,
    node_ref: NodeCellRef
}

impl Deref for NodeWriteGuard {
    type Target = NodeData;

    fn deref(&self) -> &<Self as Deref>::Target {
        debug_assert_ne!(self.data as usize, 0);
        unsafe { &*self.data }
    }
}

impl DerefMut for NodeWriteGuard {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        debug_assert_ne!(self.data as usize, 0);
        unsafe { &mut *self.data }
    }
}

impl Drop for NodeWriteGuard {
    fn drop(&mut self) {
        // cope with null pointer
        if self.cc as usize != 0 {
            let cc = unsafe { &*self.cc };
            let cc_num = cc.load(SeqCst);
            debug_assert_eq!(cc_num & LATCH_FLAG, LATCH_FLAG);
            cc.store((cc_num & (!LATCH_FLAG)) + 1, Release);
        }
    }
}

impl Default for NodeWriteGuard {
    fn default() -> Self {
        NodeWriteGuard {
            data: 0 as *mut NodeData,
            cc: 0 as *const AtomicUsize,
            version: 0,
            node_ref: Node::none_ref()
        }
    }
}

unsafe impl Sync for Node {}

pub struct NodeReadHandler {
    pub ptr: *const NodeData,
    pub version: usize,
    node_ref: NodeCellRef,
}

impl Deref for NodeReadHandler {
    type Target = NodeData;

    fn deref(&self) -> &<Self as Deref>::Target {
        unsafe { &*self.ptr }
    }
}

pub struct RemoveStatus {
    pub item_found: bool,
    pub removed: bool,
}

impl Default for Node {
    fn default() -> Self {
        Node::none()
    }
}

pub fn insert_into_split<T, S>(
    item: T,
    x: &mut S,
    y: &mut S,
    xlen: &mut usize,
    ylen: &mut usize,
    pos: usize,
) where
    S: Slice<Item = T> + BTreeSlice<T>,
    T: Default,
{
    debug!(
        "insert into split left len {}, right len {}, pos {}",
        xlen, ylen, pos
    );
    if pos < *xlen {
        debug!("insert into left part, pos: {}", pos);
        x.insert_at(item, pos, xlen);
    } else {
        let right_pos = pos - *xlen;
        debug!("insert into right part, pos: {}", right_pos);
        y.insert_at(item, right_pos, ylen);
    }
}
<commit_msg>fix  exception caused by logging<commit_after>use super::*;
use std::sync::atomic::fence;
use std::sync::atomic::Ordering::AcqRel;
use std::sync::atomic::Ordering::Acquire;
use std::sync::atomic::Ordering::Release;

pub enum InsertSearchResult {
    External,
    Internal(NodeCellRef),
    RightNode(NodeCellRef),
}

pub enum InsertToNodeResult {
    NoSplit,
    Split(NodeWriteGuard, NodeCellRef, Option<EntryKey>),
    SplitParentChanged,
}

pub enum RemoveSearchResult {
    External,
    RightNode(NodeCellRef),
    Internal(NodeCellRef),
}

pub enum SubNodeStatus {
    ExtNodeEmpty,
    InNodeEmpty,
    Relocate(usize, usize),
    Merge(usize, usize),
    Ok,
}

pub struct NodeSplit {
    pub new_right_node: NodeCellRef,
    pub left_node_latch: NodeWriteGuard,
    pub pivot: EntryKey,
    pub parent_latch: NodeWriteGuard,
}

pub struct RebalancingNodes {
    pub left_guard: NodeWriteGuard,
    pub left_ref: NodeCellRef,
    pub right_guard: NodeWriteGuard,
    pub right_right_guard: NodeWriteGuard, // for external pointer modification
    pub parent: NodeWriteGuard,
    pub parent_pos: usize,
}

pub struct RemoveResult {
    pub rebalancing: Option<RebalancingNodes>,
    pub empty: bool,
    pub removed: bool,
}

pub enum NodeData {
    External(Box<ExtNode>),
    Internal(Box<InNode>),
    Empty(Box<EmptyNode>),
    None,
}

pub struct EmptyNode {
    pub left: Option<NodeCellRef>,
    pub right: NodeCellRef,
}

impl NodeData {
    pub fn is_none(&self) -> bool {
        match self {
            &NodeData::None => true,
            _ => false,
        }
    }
    pub fn is_empty(&self) -> bool {
        match self {
            &NodeData::Empty(ref n) => true,
            &NodeData::External(ref n) => n.len == 0,
            &NodeData::Internal(ref n) => n.len == 0,
            &NodeData::None => unreachable!(),
        }
    }
    pub fn is_empty_node(&self) -> bool {
        match self {
            &NodeData::Empty(ref n) => true,
            _ => false
        }
    }
    pub fn search(&self, key: &EntryKey) -> usize {
        let len = self.len();
        if self.is_ext() {
            self.extnode().search(key)
        } else {
            self.innode().search(key)
        }
    }

    pub fn remove(&mut self, pos: usize) {
        match self {
            &mut NodeData::External(ref mut node) => node.remove_at(pos),
            &mut NodeData::Internal(ref mut node) => node.remove_at(pos),
            &mut NodeData::None | &mut NodeData::Empty(_) => unreachable!(),
        }
    }
    pub fn is_ext(&self) -> bool {
        match self {
            &NodeData::External(_) => true,
            &NodeData::Internal(_) => false,
            &NodeData::None | &NodeData::Empty(_) => panic!(),
        }
    }
    pub fn keys(&self) -> &[EntryKey] {
        if self.is_ext() {
            &self.extnode().keys
        } else {
            &self.innode().keys
        }
    }

    pub fn first_key(&self) -> &EntryKey {
        &self.keys()[0]
    }

    pub fn last_key(&self) -> &EntryKey {
        &self.keys()[self.len() - 1]
    }

    pub fn len(&self) -> usize {
        match self {
            &NodeData::Internal(ref node) => node.len,
            &NodeData::External(ref node) => node.len,
            &NodeData::Empty(_) => 0,
            &NodeData::None => unreachable!(),
        }
    }

    // check if the node will be half full after an item have been removed
    pub fn is_half_full(&self) -> bool {
        if self.is_none() {
            true
        } else {
            let len = self.len();
            if len == 0 {
                return false;
            }
            len >= NUM_KEYS / 2 && len > 1
        }
    }

    pub fn cannot_merge(&self) -> bool {
        self.len() > NUM_KEYS / 2
    }

    pub fn extnode_mut(&mut self) -> &mut ExtNode {
        match self {
            &mut NodeData::External(ref mut node) => node,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn innode_mut(&mut self) -> &mut InNode {
        match self {
            &mut NodeData::Internal(ref mut n) => n,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn extnode(&self) -> &ExtNode {
        match self {
            &NodeData::External(ref node) => node,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn ext_id(&self) -> Id {
        match self {
            &NodeData::External(ref node) => node.id,
            &NodeData::None => Id::unit_id(),
            &NodeData::Internal(_) | &NodeData::Empty(_) => unreachable!(self.type_name()),
        }
    }
    pub fn innode(&self) -> &InNode {
        match self {
            &NodeData::Internal(ref n) => n,
            _ => unreachable!(self.type_name()),
        }
    }
    pub fn type_name(&self) -> &'static str {
        match self {
            &NodeData::Internal(_) => "internal",
            &NodeData::External(_) => "external",
            &NodeData::None => "none",
            &NodeData::Empty(_) => "empty",
        }
    }
    pub fn key_at_right_node(&self, key: &EntryKey) -> Option<&NodeCellRef> {
        if self.is_empty_node() || self.len() > 0 && self.last_key() < key {
            let right_node = self.right_ref().unwrap().read_unchecked();
            if !right_node.is_none() && (self.is_empty_node() || right_node.len() > 0 && right_node.first_key() <= key) {
                debug!(
                    "found key to put to right page {:?}/{:?}",
                    key, right_node.first_key()
                );
                return Some(self.right_ref().unwrap())
            }
        }
        return None;
    }

    pub fn left_ref_mut(&mut self) -> Option<&mut NodeCellRef> {
        match self {
            &mut NodeData::External(ref mut n) => Some(&mut n.prev),
            _ => None,
        }
    }

    pub fn left_ref(&self) -> Option<&NodeCellRef> {
        match self {
            &NodeData::External(ref n) => Some(&n.prev),
            _ => None,
        }
    }

    pub fn right_ref_mut(&mut self) -> Option<&mut NodeCellRef> {
        match self {
            &mut NodeData::External(ref mut n) => Some(&mut n.next),
            &mut NodeData::Internal(ref mut n) => Some(&mut n.right),
            &mut NodeData::Empty(ref mut n) => Some(&mut n.right),
            &mut NodeData::None => None,
        }
    }

    pub fn right_ref(&self) -> Option<&NodeCellRef> {
        match self {
            &NodeData::External(ref n) => Some(&n.next),
            &NodeData::Internal(ref n) => Some(&n.right),
            &NodeData::Empty(ref n) => Some(&n.right),
            &NodeData::None => None,
        }
    }

    pub fn has_vaild_right_node(&self) -> bool {
        self.right_ref().is_some()
    }
}

pub fn write_key_page(
    search_page: NodeWriteGuard,
    search_ref: &NodeCellRef,
    key: &EntryKey,
) -> (NodeWriteGuard, NodeCellRef) {
    if search_page.is_empty_node() || search_page.len() > 0 && search_page.last_key() < key {
        let right_ref = search_page.right_ref().unwrap();
        let right_node = write_node(right_ref);
        if !right_node.is_none() && (search_page.is_empty_node() || right_node.len() > 0 && right_node.first_key() <= key) {
            debug!("Will write {:?} from left {} node to right page, start with {:?}, keys {:?}",
                   key, search_page.type_name(),
                   if right_node.is_empty_node() { smallvec!(0) } else { right_node.first_key().clone() },
                   if right_node.is_empty_node() { &[] } else { right_node.keys() });
            return write_key_page(right_node, right_ref, key);
        }
    }
    return (search_page, search_ref.clone());
}

const LATCH_FLAG: usize = !(!0 >> 1);

pub struct Node {
    data: UnsafeCell<NodeData>,
    cc: AtomicUsize,
}

impl Node {
    pub fn new(data: NodeData) -> Self {
        Node {
            data: UnsafeCell::new(data),
            cc: AtomicUsize::new(0),
        }
    }

    pub fn internal(innode: InNode) -> Self {
        Self::new(NodeData::Internal(box innode))
    }

    pub fn external(extnode: ExtNode) -> Self {
        Self::new(NodeData::External(box extnode))
    }

    pub fn none() -> Self {
        Self::new(NodeData::None)
    }
    pub fn none_ref() -> NodeCellRef {
        Arc::new(Node::none())
    }
    pub fn new_external(id: Id) -> Self {
        Self::external(ExtNode::new(id))
    }

    pub fn read_unchecked(&self) -> &NodeData {
        unsafe { &*self.data.get() }
    }

    pub fn version(&self) -> usize {
        node_version(self.cc.load(SeqCst))
    }
}

pub fn node_version(cc_num: usize) -> usize {
    cc_num & (!LATCH_FLAG)
}

pub fn write_node(node: &NodeCellRef) -> NodeWriteGuard {
    // debug!("acquiring node write lock");
    let cc = &node.cc;
    loop {
        let cc_num = cc.load(Relaxed);
        let expected = cc_num & (!LATCH_FLAG);
        debug_assert_eq!(expected & LATCH_FLAG, 0);
        match cc.compare_exchange_weak(expected, cc_num | LATCH_FLAG, Acquire, Relaxed) {
            Ok(num) if num == expected => {
                return NodeWriteGuard {
                    data: node.data.get(),
                    cc: &node.cc as *const AtomicUsize,
                    version: node_version(cc_num),
                    node_ref: node.clone()
                }
            }
            _ => {}
        }
    }
}

pub fn read_node<'a, F: FnMut(&NodeReadHandler) -> R + 'a, R: 'a>(node: &NodeCellRef, mut func: F) -> R {
    let mut handler = NodeReadHandler {
        ptr: node.data.get(),
        version: 0,
        node_ref: node.clone()
    };
    let cc = &node.cc;
    loop {
        let cc_num = cc.load(SeqCst);
        if cc_num & LATCH_FLAG == LATCH_FLAG {
            // debug!("read have a latch, retry {:b}", cc_num);
            continue;
        }
        handler.version = cc_num & (!LATCH_FLAG);
        let res = func(&handler);
        let new_cc_num = cc.load(SeqCst);
        if new_cc_num == cc_num {
            return res;
        }
        // debug!("read version changed, retry {:b}", cc_num);
    }
}

pub struct NodeWriteGuard {
    pub data: *mut NodeData,
    pub cc: *const AtomicUsize,
    pub version: usize,
    node_ref: NodeCellRef
}

impl Deref for NodeWriteGuard {
    type Target = NodeData;

    fn deref(&self) -> &<Self as Deref>::Target {
        debug_assert_ne!(self.data as usize, 0);
        unsafe { &*self.data }
    }
}

impl DerefMut for NodeWriteGuard {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        debug_assert_ne!(self.data as usize, 0);
        unsafe { &mut *self.data }
    }
}

impl Drop for NodeWriteGuard {
    fn drop(&mut self) {
        // cope with null pointer
        if self.cc as usize != 0 {
            let cc = unsafe { &*self.cc };
            let cc_num = cc.load(SeqCst);
            debug_assert_eq!(cc_num & LATCH_FLAG, LATCH_FLAG);
            cc.store((cc_num & (!LATCH_FLAG)) + 1, Release);
        }
    }
}

impl Default for NodeWriteGuard {
    fn default() -> Self {
        NodeWriteGuard {
            data: 0 as *mut NodeData,
            cc: 0 as *const AtomicUsize,
            version: 0,
            node_ref: Node::none_ref()
        }
    }
}

unsafe impl Sync for Node {}

pub struct NodeReadHandler {
    pub ptr: *const NodeData,
    pub version: usize,
    node_ref: NodeCellRef,
}

impl Deref for NodeReadHandler {
    type Target = NodeData;

    fn deref(&self) -> &<Self as Deref>::Target {
        unsafe { &*self.ptr }
    }
}

pub struct RemoveStatus {
    pub item_found: bool,
    pub removed: bool,
}

impl Default for Node {
    fn default() -> Self {
        Node::none()
    }
}

pub fn insert_into_split<T, S>(
    item: T,
    x: &mut S,
    y: &mut S,
    xlen: &mut usize,
    ylen: &mut usize,
    pos: usize,
) where
    S: Slice<Item = T> + BTreeSlice<T>,
    T: Default,
{
    debug!(
        "insert into split left len {}, right len {}, pos {}",
        xlen, ylen, pos
    );
    if pos < *xlen {
        debug!("insert into left part, pos: {}", pos);
        x.insert_at(item, pos, xlen);
    } else {
        let right_pos = pos - *xlen;
        debug!("insert into right part, pos: {}", right_pos);
        y.insert_at(item, right_pos, ylen);
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: GPL-2.0
//

use serde_ini;

use chrono::{DateTime, Duration, Utc};

use std::io;
use std::path::Path;
use std::path::PathBuf;

use serde_helpers::{de, ser};

#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct RuntimeSettings {
    pub polling: RuntimePolling,
    pub update: RuntimeUpdate,
    #[serde(skip)] path: PathBuf,
}

impl RuntimeSettings {
    pub fn new() -> Self {
        RuntimeSettings::default()
    }

    pub fn load(mut self, path: &str) -> Result<Self, RuntimeSettingsError> {
        use std::fs::File;
        use std::io::Read;

        let path = Path::new(path);

        if path.exists() {
            info!("Loading runtime settings from '{}'...",
                  path.to_string_lossy());

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;
            self = RuntimeSettings::parse(&content)?;
        } else {
            debug!("Runtime settings file {} does not exists.",
                   path.to_string_lossy());
            info!("Using default runtime settings...");
        }

        self.path = path.to_path_buf();
        Ok(self)
    }

    fn parse(content: &str) -> Result<Self, RuntimeSettingsError> {
        Ok(serde_ini::from_str::<RuntimeSettings>(content)?)
    }

    pub fn save(&self) -> Result<usize, RuntimeSettingsError> {
        use std::fs::File;
        use std::io::Write;

        debug!("Saving runtime settings from '{}'...",
               &self.path.to_string_lossy());

        Ok(File::create(&self.path)?.write(self.serialize()?.as_bytes())?)
    }

    fn serialize(&self) -> Result<String, RuntimeSettingsError> {
        Ok(serde_ini::to_string(&self)?)
    }
}

#[derive(Debug)]
pub enum RuntimeSettingsError {
    Io(io::Error),
    IniDeserialize(serde_ini::de::Error),
    IniSerialize(serde_ini::ser::Error),
}

impl From<io::Error> for RuntimeSettingsError {
    fn from(err: io::Error) -> RuntimeSettingsError {
        RuntimeSettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for RuntimeSettingsError {
    fn from(err: serde_ini::de::Error) -> RuntimeSettingsError {
        RuntimeSettingsError::IniDeserialize(err)
    }
}

impl From<serde_ini::ser::Error> for RuntimeSettingsError {
    fn from(err: serde_ini::ser::Error) -> RuntimeSettingsError {
        RuntimeSettingsError::IniSerialize(err)
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct RuntimePolling {
    #[serde(rename = "LastPoll")] pub last: DateTime<Utc>,
    #[serde(rename = "FirstPoll")] pub first: DateTime<Utc>,
    #[serde(deserialize_with = "de::duration_from_int")]
    #[serde(serialize_with = "ser::duration_to_int")]
    pub extra_interval: Duration,
    pub retries: usize,
    #[serde(rename = "ProbeASAP")]
    #[serde(deserialize_with = "de::bool_from_str")]
    #[serde(serialize_with = "ser::bool_to_string")]
    pub now: bool,
}

impl Default for RuntimePolling {
    fn default() -> Self {
        RuntimePolling { last: Utc::now(),
                         first: Utc::now(),
                         extra_interval: Duration::seconds(0),
                         retries: 0,
                         now: false, }
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct RuntimeUpdate {
    #[serde(rename = "UpgradeToInstallation")] pub upgrading_to: i8,
}

impl Default for RuntimeUpdate {
    fn default() -> Self {
        RuntimeUpdate { upgrading_to: -1 }
    }
}

#[test]
fn de() {
    let ini = r"
[Polling]
LastPoll=2017-01-01T00:00:00Z
FirstPoll=2017-02-02T00:00:00Z
ExtraInterval=4
Retries=5
ProbeASAP=false

[Update]
UpgradeToInstallation=1
";
    let expected =
        RuntimeSettings { polling: RuntimePolling { last: "2017-01-01T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap(),
                                                    first: "2017-02-02T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap(),
                                                    extra_interval: Duration::seconds(4),
                                                    retries: 5,
                                                    now: false, },
                          update: RuntimeUpdate { upgrading_to: 1 },
                          ..Default::default() };

    assert!(serde_ini::from_str::<RuntimeSettings>(ini).map_err(|e| println!("{}", e))
                                                       .as_ref()
                                                       .ok() == Some(&expected));
    assert!(RuntimeSettings::parse(ini).as_ref().ok() == Some(&expected));
}

#[test]
fn default() {
    let settings = RuntimeSettings::new();
    let expected = RuntimeSettings { polling: RuntimePolling { last: settings.polling.last,
                                                               first: settings.polling.first,
                                                               extra_interval: Duration::seconds(0),
                                                               retries: 0,
                                                               now: false, },
                                     update: RuntimeUpdate { upgrading_to: -1 },
                                     path: PathBuf::new(), };

    assert!(Some(settings) == Some(expected));
}

#[test]
fn ser() {
    let settings =
        RuntimeSettings { polling: RuntimePolling { last: "2017-01-01T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap(),
                                                    first: "2017-02-02T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap(),
                                                    extra_interval: Duration::seconds(4),
                                                    retries: 5,
                                                    now: false, },
                          update: RuntimeUpdate { upgrading_to: 1 },
                          ..Default::default() };

    assert!(serde_ini::from_str(&settings.serialize().ok().unwrap()).map_err(|e| println!("{}", e))
                                                                    .ok() == Some(settings));
}

#[test]
fn load_and_save() {
    use mktemp::Temp;

    let settings_file = Temp::new_file().unwrap().to_path_buf();

    let mut settings = RuntimeSettings::new().load(settings_file.to_str().unwrap())
                                             .unwrap();

    assert_eq!(settings.polling.now, false);
    settings.polling.now = true;

    assert_eq!(settings.polling.now, true);
    settings.save()
            .expect("Failed to save the runtime settings");

    let new_settings = RuntimeSettings::new().load(settings_file.to_str().unwrap())
                                             .unwrap();

    assert!(settings.update == new_settings.update);
}
<commit_msg>runtime_settings: Allow use of None as last poll<commit_after>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: GPL-2.0
//

use serde_ini;

use chrono::{DateTime, Duration, Utc};

use std::io;
use std::path::Path;
use std::path::PathBuf;

use serde_helpers::{de, ser};

#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct RuntimeSettings {
    pub polling: RuntimePolling,
    pub update: RuntimeUpdate,
    #[serde(skip)] path: PathBuf,
}

impl RuntimeSettings {
    pub fn new() -> Self {
        RuntimeSettings::default()
    }

    pub fn load(mut self, path: &str) -> Result<Self, RuntimeSettingsError> {
        use std::fs::File;
        use std::io::Read;

        let path = Path::new(path);

        if path.exists() {
            info!("Loading runtime settings from '{}'...",
                  path.to_string_lossy());

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;
            self = RuntimeSettings::parse(&content)?;
        } else {
            debug!("Runtime settings file {} does not exists.",
                   path.to_string_lossy());
            info!("Using default runtime settings...");
        }

        self.path = path.to_path_buf();
        Ok(self)
    }

    fn parse(content: &str) -> Result<Self, RuntimeSettingsError> {
        Ok(serde_ini::from_str::<RuntimeSettings>(content)?)
    }

    pub fn save(&self) -> Result<usize, RuntimeSettingsError> {
        use std::fs::File;
        use std::io::Write;

        debug!("Saving runtime settings from '{}'...",
               &self.path.to_string_lossy());

        Ok(File::create(&self.path)?.write(self.serialize()?.as_bytes())?)
    }

    fn serialize(&self) -> Result<String, RuntimeSettingsError> {
        Ok(serde_ini::to_string(&self)?)
    }
}

#[derive(Debug)]
pub enum RuntimeSettingsError {
    Io(io::Error),
    IniDeserialize(serde_ini::de::Error),
    IniSerialize(serde_ini::ser::Error),
}

impl From<io::Error> for RuntimeSettingsError {
    fn from(err: io::Error) -> RuntimeSettingsError {
        RuntimeSettingsError::Io(err)
    }
}

impl From<serde_ini::de::Error> for RuntimeSettingsError {
    fn from(err: serde_ini::de::Error) -> RuntimeSettingsError {
        RuntimeSettingsError::IniDeserialize(err)
    }
}

impl From<serde_ini::ser::Error> for RuntimeSettingsError {
    fn from(err: serde_ini::ser::Error) -> RuntimeSettingsError {
        RuntimeSettingsError::IniSerialize(err)
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct RuntimePolling {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "LastPoll")]
    pub last: Option<DateTime<Utc>>,
    #[serde(rename = "FirstPoll")] pub first: DateTime<Utc>,
    #[serde(deserialize_with = "de::duration_from_int")]
    #[serde(serialize_with = "ser::duration_to_int")]
    pub extra_interval: Duration,
    pub retries: usize,
    #[serde(rename = "ProbeASAP")]
    #[serde(deserialize_with = "de::bool_from_str")]
    #[serde(serialize_with = "ser::bool_to_string")]
    pub now: bool,
}

impl Default for RuntimePolling {
    fn default() -> Self {
        RuntimePolling { last: None,
                         first: Utc::now(),
                         extra_interval: Duration::seconds(0),
                         retries: 0,
                         now: false, }
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct RuntimeUpdate {
    #[serde(rename = "UpgradeToInstallation")] pub upgrading_to: i8,
}

impl Default for RuntimeUpdate {
    fn default() -> Self {
        RuntimeUpdate { upgrading_to: -1 }
    }
}

#[test]
fn de() {
    let ini = r"
[Polling]
LastPoll=2017-01-01T00:00:00Z
FirstPoll=2017-02-02T00:00:00Z
ExtraInterval=4
Retries=5
ProbeASAP=false

[Update]
UpgradeToInstallation=1
";
    let expected =
        RuntimeSettings { polling: RuntimePolling { last: Some("2017-01-01T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap()),
                                                    first: "2017-02-02T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap(),
                                                    extra_interval: Duration::seconds(4),
                                                    retries: 5,
                                                    now: false, },
                          update: RuntimeUpdate { upgrading_to: 1 },
                          ..Default::default() };

    assert!(serde_ini::from_str::<RuntimeSettings>(ini).map_err(|e| println!("{}", e))
                                                       .as_ref()
                                                       .ok() == Some(&expected));
    assert!(RuntimeSettings::parse(ini).as_ref().ok() == Some(&expected));
}

#[test]
fn default() {
    let settings = RuntimeSettings::new();
    let expected = RuntimeSettings { polling: RuntimePolling { last: settings.polling.last,
                                                               first: settings.polling.first,
                                                               extra_interval: Duration::seconds(0),
                                                               retries: 0,
                                                               now: false, },
                                     update: RuntimeUpdate { upgrading_to: -1 },
                                     path: PathBuf::new(), };

    assert!(Some(settings) == Some(expected));
}

#[test]
fn ser() {
    let settings =
        RuntimeSettings { polling: RuntimePolling { last: Some("2017-01-01T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap()),
                                                    first: "2017-02-02T00:00:00Z".parse::<DateTime<Utc>>()
                                                                                .unwrap(),
                                                    extra_interval: Duration::seconds(4),
                                                    retries: 5,
                                                    now: false, },
                          update: RuntimeUpdate { upgrading_to: 1 },
                          ..Default::default() };

    assert!(serde_ini::from_str(&settings.serialize().ok().unwrap()).map_err(|e| println!("{}", e))
                                                                    .ok() == Some(settings));
}

#[test]
fn load_and_save() {
    use mktemp::Temp;

    let settings_file = Temp::new_file().unwrap().to_path_buf();

    let mut settings = RuntimeSettings::new().load(settings_file.to_str().unwrap())
                                             .unwrap();

    assert_eq!(settings.polling.now, false);
    settings.polling.now = true;

    assert_eq!(settings.polling.now, true);
    settings.save()
            .expect("Failed to save the runtime settings");

    let new_settings = RuntimeSettings::new().load(settings_file.to_str().unwrap())
                                             .unwrap();

    assert_eq!(settings.update, new_settings.update);
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use rustc_hash::FxHashSet;

use ra_syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange,
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
    ArgList,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if !visited_comments.contains(&comment) {
                        if let Some(range) =
                            contiguous_range_for_comment(comment, &mut visited_comments)
                        {
                            res.push(Fold { range, kind: FoldKind::Comment })
                        }
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
                {
                    if let Some(range) =
                        contiguous_range_for_group_unless(&node, has_visibility, &mut visited_mods)
                    {
                        res.push(Fold { range, kind: FoldKind::Mods })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        USE_ITEM => Some(FoldKind::Imports),
        ARG_LIST => Some(FoldKind::ArgList),
        RECORD_FIELD_DEF_LIST
        | RECORD_FIELD_PAT_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK_EXPR
        | MATCH_ARM_LIST
        | ENUM_VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::new(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        visited.insert(c.clone());
                        last = c;
                        continue;
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::new(first.syntax().text_range().start(), last.syntax().text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

#[cfg(test)]
mod tests {
    use test_utils::extract_tags;

    use super::*;

    fn check(ra_fixture: &str) {
        let (ranges, text) = extract_tags(ra_fixture, "fold");

        let parse = SourceFile::parse(&text);
        let folds = folding_ranges(&parse.tree());
        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );

        for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());

            let kind = match fold.kind {
                FoldKind::Comment => "comment",
                FoldKind::Imports => "imports",
                FoldKind::Mods => "mods",
                FoldKind::Block => "block",
                FoldKind::ArgList => "arglist",
            };
            assert_eq!(kind, &attr.unwrap());
        }
    }

    #[test]
    fn test_fold_comments() {
        check(
            r#"
<fold comment>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold block>{
    <fold comment>// We should
    // also
    // fold
    // this one.</fold>
    <fold comment>//! But this one is different
    //! because it has another flavor</fold>
    <fold comment>/* As does this
    multiline comment */</fold>
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_imports() {
        check(
            r#"
<fold imports>use std::<fold block>{
    str,
    vec,
    io as iop
}</fold>;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_mods() {
        check(
            r#"

pub mod foo;
<fold mods>mod after_pub;
mod after_pub_next;</fold>

<fold mods>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold mods>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_and_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

<fold imports>use std::collections::<fold block>{
    HashMap,
    VecDeque,
}</fold>;</fold>
// Some random comment

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_folds_macros() {
        check(
            r#"
macro_rules! foo <fold block>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_match_arms() {
        check(
            r#"
fn main() <fold block>{
    match 0 <fold block>{
        0 => 0,
        _ => 1,
    }</fold>
}</fold>"#,
        );
    }

    #[test]
    fn fold_big_calls() {
        check(
            r#"
fn main() <fold block>{
    frobnicate<fold arglist>(
        1,
        2,
        3,
    )</fold>
}</fold>
        "#,
        )
    }
}
<commit_msg>Fold struct literals<commit_after>//! FIXME: write short doc here

use rustc_hash::FxHashSet;

use ra_syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange,
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
    ArgList,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if !visited_comments.contains(&comment) {
                        if let Some(range) =
                            contiguous_range_for_comment(comment, &mut visited_comments)
                        {
                            res.push(Fold { range, kind: FoldKind::Comment })
                        }
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
                {
                    if let Some(range) =
                        contiguous_range_for_group_unless(&node, has_visibility, &mut visited_mods)
                    {
                        res.push(Fold { range, kind: FoldKind::Mods })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        USE_ITEM => Some(FoldKind::Imports),
        ARG_LIST => Some(FoldKind::ArgList),
        RECORD_FIELD_DEF_LIST
        | RECORD_FIELD_PAT_LIST
        | RECORD_FIELD_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK_EXPR
        | MATCH_ARM_LIST
        | ENUM_VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::new(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        visited.insert(c.clone());
                        last = c;
                        continue;
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::new(first.syntax().text_range().start(), last.syntax().text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

#[cfg(test)]
mod tests {
    use test_utils::extract_tags;

    use super::*;

    fn check(ra_fixture: &str) {
        let (ranges, text) = extract_tags(ra_fixture, "fold");

        let parse = SourceFile::parse(&text);
        let folds = folding_ranges(&parse.tree());
        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );

        for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());

            let kind = match fold.kind {
                FoldKind::Comment => "comment",
                FoldKind::Imports => "imports",
                FoldKind::Mods => "mods",
                FoldKind::Block => "block",
                FoldKind::ArgList => "arglist",
            };
            assert_eq!(kind, &attr.unwrap());
        }
    }

    #[test]
    fn test_fold_comments() {
        check(
            r#"
<fold comment>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold block>{
    <fold comment>// We should
    // also
    // fold
    // this one.</fold>
    <fold comment>//! But this one is different
    //! because it has another flavor</fold>
    <fold comment>/* As does this
    multiline comment */</fold>
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_imports() {
        check(
            r#"
<fold imports>use std::<fold block>{
    str,
    vec,
    io as iop
}</fold>;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_mods() {
        check(
            r#"

pub mod foo;
<fold mods>mod after_pub;
mod after_pub_next;</fold>

<fold mods>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold mods>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_fold_import_and_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

<fold imports>use std::collections::<fold block>{
    HashMap,
    VecDeque,
}</fold>;</fold>
// Some random comment

fn main() <fold block>{
}</fold>"#,
        );
    }

    #[test]
    fn test_folds_macros() {
        check(
            r#"
macro_rules! foo <fold block>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_match_arms() {
        check(
            r#"
fn main() <fold block>{
    match 0 <fold block>{
        0 => 0,
        _ => 1,
    }</fold>
}</fold>
"#,
        );
    }

    #[test]
    fn fold_big_calls() {
        check(
            r#"
fn main() <fold block>{
    frobnicate<fold arglist>(
        1,
        2,
        3,
    )</fold>
}</fold>
"#,
        )
    }

    #[test]
    fn fold_record_literals() {
        check(
            r#"
const _: S = S <fold block>{

}</fold>;
"#,
        )
    }
}
<|endoftext|>


<commit_before>//! Handle process life-time and message passing for proc-macro client

use crossbeam_channel::{bounded, Receiver, Sender};
use ra_tt::Subtree;

use crate::msg::{ErrorCode, Message, Request, Response, ResponseError};
use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};

use io::{BufRead, BufReader};
use std::{
    convert::{TryFrom, TryInto},
    ffi::OsStr,
    io::{self, Write},
    path::{Path, PathBuf},
    process::{Child, Command, Stdio},
    sync::{Arc, Weak},
};

#[derive(Debug, Default)]
pub(crate) struct ProcMacroProcessSrv {
    inner: Option<Weak<Sender<Task>>>,
}

#[derive(Debug)]
pub(crate) struct ProcMacroProcessThread {
    // XXX: drop order is significant
    sender: Arc<Sender<Task>>,
    handle: jod_thread::JoinHandle<()>,
}

struct Task {
    req: Request,
    result_tx: Sender<Option<Response>>,
}

struct Process {
    path: PathBuf,
    child: Child,
}

impl Drop for Process {
    fn drop(&mut self) {
        let _ = self.child.kill();
    }
}

impl Process {
    fn run(
        process_path: PathBuf,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<Process> {
        let child = Command::new(&process_path)
            .args(args)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::null())
            .spawn()?;

        Ok(Process { path: process_path, child })
    }

    fn restart(&mut self) -> io::Result<()> {
        let _ = self.child.kill();
        self.child = Command::new(&self.path)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::null())
            .spawn()?;
        Ok(())
    }

    fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
        let stdin = self.child.stdin.take()?;
        let stdout = self.child.stdout.take()?;
        let read = BufReader::new(stdout);

        Some((stdin, read))
    }
}

impl ProcMacroProcessSrv {
    pub fn run(
        process_path: PathBuf,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<(ProcMacroProcessThread, ProcMacroProcessSrv)> {
        let process = Process::run(process_path, args)?;

        let (task_tx, task_rx) = bounded(0);
        let handle = jod_thread::spawn(move || {
            client_loop(task_rx, process);
        });

        let task_tx = Arc::new(task_tx);
        let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
        let thread = ProcMacroProcessThread { handle, sender: task_tx };

        Ok((thread, srv))
    }

    pub fn find_proc_macros(
        &self,
        dylib_path: &Path,
    ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> {
        let task = ListMacrosTask { lib: dylib_path.to_path_buf() };

        let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
        Ok(result.macros)
    }

    pub fn custom_derive(
        &self,
        dylib_path: &Path,
        subtree: &Subtree,
        derive_name: &str,
    ) -> Result<Subtree, ra_tt::ExpansionError> {
        let task = ExpansionTask {
            macro_body: subtree.clone(),
            macro_name: derive_name.to_string(),
            attributes: None,
            lib: dylib_path.to_path_buf(),
        };

        let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?;
        Ok(result.expansion)
    }

    pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError>
    where
        R: TryFrom<Response, Error = &'static str>,
    {
        let sender = match &self.inner {
            None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())),
            Some(it) => it,
        };

        let (result_tx, result_rx) = bounded(0);
        let sender = match sender.upgrade() {
            None => {
                return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
            }
            Some(it) => it,
        };
        sender.send(Task { req: req.into(), result_tx }).unwrap();
        let res = result_rx
            .recv()
            .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;

        match res {
            Some(Response::Error(err)) => {
                return Err(ra_tt::ExpansionError::ExpansionError(err.message));
            }
            Some(res) => Ok(res.try_into().map_err(|err| {
                ra_tt::ExpansionError::Unknown(format!(
                    "Fail to get response, reason : {:#?} ",
                    err
                ))
            })?),
            None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())),
        }
    }
}

fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
    let (mut stdin, mut stdout) = match process.stdio() {
        None => return,
        Some(it) => it,
    };

    for task in task_rx {
        let Task { req, result_tx } = task;

        match send_request(&mut stdin, &mut stdout, req) {
            Ok(res) => result_tx.send(res).unwrap(),
            Err(_err) => {
                let res = Response::Error(ResponseError {
                    code: ErrorCode::ServerErrorEnd,
                    message: "Server closed".into(),
                });
                result_tx.send(res.into()).unwrap();
                // Restart the process
                if process.restart().is_err() {
                    break;
                }
                let stdio = match process.stdio() {
                    None => break,
                    Some(it) => it,
                };
                stdin = stdio.0;
                stdout = stdio.1;
            }
        }
    }
}

fn send_request(
    mut writer: &mut impl Write,
    mut reader: &mut impl BufRead,
    req: Request,
) -> io::Result<Option<Response>> {
    req.write(&mut writer)?;
    Ok(Response::read(&mut reader)?)
}
<commit_msg>Fix restart missing arguments in proc-macro-srv<commit_after>//! Handle process life-time and message passing for proc-macro client

use crossbeam_channel::{bounded, Receiver, Sender};
use ra_tt::Subtree;

use crate::msg::{ErrorCode, Message, Request, Response, ResponseError};
use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};

use io::{BufRead, BufReader};
use std::{
    convert::{TryFrom, TryInto},
    ffi::{OsStr, OsString},
    io::{self, Write},
    path::{Path, PathBuf},
    process::{Child, Command, Stdio},
    sync::{Arc, Weak},
};

#[derive(Debug, Default)]
pub(crate) struct ProcMacroProcessSrv {
    inner: Option<Weak<Sender<Task>>>,
}

#[derive(Debug)]
pub(crate) struct ProcMacroProcessThread {
    // XXX: drop order is significant
    sender: Arc<Sender<Task>>,
    handle: jod_thread::JoinHandle<()>,
}

struct Task {
    req: Request,
    result_tx: Sender<Option<Response>>,
}

struct Process {
    path: PathBuf,
    args: Vec<OsString>,
    child: Child,
}

impl Drop for Process {
    fn drop(&mut self) {
        let _ = self.child.kill();
    }
}

impl Process {
    fn run(
        path: PathBuf,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<Process> {
        let args = args.into_iter().map(|s| s.as_ref().into()).collect();

        let child = Command::new(&path)
            .args(&args)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::null())
            .spawn()?;

        Ok(Process { path, args, child })
    }

    fn restart(&mut self) -> io::Result<()> {
        let _ = self.child.kill();
        self.child = Command::new(&self.path)
            .args(&self.args)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::null())
            .spawn()?;
        Ok(())
    }

    fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
        let stdin = self.child.stdin.take()?;
        let stdout = self.child.stdout.take()?;
        let read = BufReader::new(stdout);

        Some((stdin, read))
    }
}

impl ProcMacroProcessSrv {
    pub fn run(
        process_path: PathBuf,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<(ProcMacroProcessThread, ProcMacroProcessSrv)> {
        let process = Process::run(process_path, args)?;

        let (task_tx, task_rx) = bounded(0);
        let handle = jod_thread::spawn(move || {
            client_loop(task_rx, process);
        });

        let task_tx = Arc::new(task_tx);
        let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
        let thread = ProcMacroProcessThread { handle, sender: task_tx };

        Ok((thread, srv))
    }

    pub fn find_proc_macros(
        &self,
        dylib_path: &Path,
    ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> {
        let task = ListMacrosTask { lib: dylib_path.to_path_buf() };

        let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
        Ok(result.macros)
    }

    pub fn custom_derive(
        &self,
        dylib_path: &Path,
        subtree: &Subtree,
        derive_name: &str,
    ) -> Result<Subtree, ra_tt::ExpansionError> {
        let task = ExpansionTask {
            macro_body: subtree.clone(),
            macro_name: derive_name.to_string(),
            attributes: None,
            lib: dylib_path.to_path_buf(),
        };

        let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?;
        Ok(result.expansion)
    }

    pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError>
    where
        R: TryFrom<Response, Error = &'static str>,
    {
        let sender = match &self.inner {
            None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())),
            Some(it) => it,
        };

        let (result_tx, result_rx) = bounded(0);
        let sender = match sender.upgrade() {
            None => {
                return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
            }
            Some(it) => it,
        };
        sender.send(Task { req: req.into(), result_tx }).unwrap();
        let res = result_rx
            .recv()
            .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;

        match res {
            Some(Response::Error(err)) => {
                return Err(ra_tt::ExpansionError::ExpansionError(err.message));
            }
            Some(res) => Ok(res.try_into().map_err(|err| {
                ra_tt::ExpansionError::Unknown(format!(
                    "Fail to get response, reason : {:#?} ",
                    err
                ))
            })?),
            None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())),
        }
    }
}

fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
    let (mut stdin, mut stdout) = match process.stdio() {
        None => return,
        Some(it) => it,
    };

    for task in task_rx {
        let Task { req, result_tx } = task;

        match send_request(&mut stdin, &mut stdout, req) {
            Ok(res) => result_tx.send(res).unwrap(),
            Err(_err) => {
                let res = Response::Error(ResponseError {
                    code: ErrorCode::ServerErrorEnd,
                    message: "Server closed".into(),
                });
                result_tx.send(res.into()).unwrap();
                // Restart the process
                if process.restart().is_err() {
                    break;
                }
                let stdio = match process.stdio() {
                    None => break,
                    Some(it) => it,
                };
                stdin = stdio.0;
                stdout = stdio.1;
            }
        }
    }
}

fn send_request(
    mut writer: &mut impl Write,
    mut reader: &mut impl BufRead,
    req: Request,
) -> io::Result<Option<Response>> {
    req.write(&mut writer)?;
    Ok(Response::read(&mut reader)?)
}
<|endoftext|>