<commit_before>//! Auctions and bidding during the first phase of the game.

use std::fmt;
use std::str::FromStr;

use super::game;
use super::cards;
use super::pos;

use rustc_serialize;

/// Goal set by a contract.
///
/// Determines the winning conditions and the score on success.
#[derive(PartialEq,Clone,Copy,Debug)]
pub enum Target {
    /// Team must get 80 points
    Contract80,
    /// Team must get 90 points
    Contract90,
    /// Team must get 100 points
    Contract100,
    /// Team must get 110 points
    Contract110,
    /// Team must get 120 points
    Contract120,
    /// Team must get 130 points
    Contract130,
    /// Team must get 140 points
    Contract140,
    /// Team must get 150 points
    Contract150,
    /// Team must get 160 points
    Contract160,
    /// Team must win all tricks
    ContractCapot,
}

impl rustc_serialize::Encodable for Target {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_str(match self {
            &Target::Contract80 => "80",
            &Target::Contract90 => "90",
            &Target::Contract100 => "100",
            &Target::Contract110 => "110",
            &Target::Contract120 => "120",
            &Target::Contract130 => "130",
            &Target::Contract140 => "140",
            &Target::Contract150 => "150",
            &Target::Contract160 => "160",
            &Target::ContractCapot => "Capot",
        })
    }
}

impl rustc_serialize::Decodable for Target {
    fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
        match try!(d.read_str()).as_ref() {
            "80" => Ok(Target::Contract80),
            "90" => Ok(Target::Contract90),
            "100" => Ok(Target::Contract100),
            "110" => Ok(Target::Contract110),
            "120" => Ok(Target::Contract120),
            "130" => Ok(Target::Contract130),
            "140" => Ok(Target::Contract140),
            "150" => Ok(Target::Contract150),
            "160" => Ok(Target::Contract160),
            "Capot" => Ok(Target::ContractCapot),
            _ => Err(d.error("unknown contract"))
        }
    }
}

impl Target {
    /// Returns the score this target would give on success.
    pub fn score(&self) -> i32 {
        match *self {
            Target::Contract80 => 80,
            Target::Contract90 => 90,
            Target::Contract100 => 100,
            Target::Contract110 => 110,
            Target::Contract120 => 120,
            Target::Contract130 => 130,
            Target::Contract140 => 140,
            Target::Contract150 => 150,
            Target::Contract160 => 160,
            Target::ContractCapot => 250,
        }
    }

    /// Determines whether this target was reached.
    pub fn victory(&self, points: i32, capot: bool) -> bool {
        match *self {
            Target::Contract80 => points >= 80,
            Target::Contract90 => points >= 90,
            Target::Contract100 => points >= 100,
            Target::Contract110 => points >= 110,
            Target::Contract120 => points >= 120,
            Target::Contract130 => points >= 130,
            Target::Contract140 => points >= 140,
            Target::Contract150 => points >= 150,
            Target::Contract160 => points >= 160,
            Target::ContractCapot => capot,
        }
    }
}

impl FromStr for Target {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        match s {
            "80" => Ok(Target::Contract80),
            "90" => Ok(Target::Contract90),
            "100" => Ok(Target::Contract100),
            "110" => Ok(Target::Contract110),
            "120" => Ok(Target::Contract120),
            "130" => Ok(Target::Contract130),
            "140" => Ok(Target::Contract140),
            "150" => Ok(Target::Contract150),
            "160" => Ok(Target::Contract160),
            "Capot" => Ok(Target::ContractCapot),
            _ => Err(format!("invalid target: {}", s)),
        }
    }
}

/// Contract taken by a team.
///
/// Composed of a trump suit and a target to reach.
#[derive(Clone,Debug,RustcEncodable,RustcDecodable)]
pub struct Contract {
    /// Initial author of the contract.
    pub author: pos::PlayerPos,
    /// Trump suit for this game.
    pub trump: cards::Suit,
    /// Target for the contract.
    pub target: Target,
    /// Level of coinche:
    ///
    /// * `0`: not coinched
    /// * `1`: coinched
    /// * `2`: surcoinched
    pub coinche_level: i32,
}

impl Contract {
    fn new(author: pos::PlayerPos, trump: cards::Suit, target: Target) -> Self {
        Contract {
            author: author,
            trump: trump,
            target: target,
            coinche_level: 0,
        }
    }
}

/// Current state of an auction
#[derive(PartialEq,Clone,Copy,Debug)]
pub enum AuctionState {
    /// Players are still bidding for the highest contract
    Bidding,
    /// One player coinched, maybe another one will surcoinche?
    Coinching,
    /// Auction is over, game will begin
    Over,
    /// No contract was taken, a new game will start
    Cancelled,
}

/// Represents the entire auction process.
pub struct Auction {
    history: Vec<Contract>,
    pass_count: usize,
    first: pos::PlayerPos,
    state: AuctionState,
    players: [cards::Hand; 4],
}

/// Possible error occuring during an Auction.
#[derive(PartialEq,Debug)]
pub enum BidError {
    /// The auction was closed and does not accept more contracts.
    AuctionClosed,
    /// A player tried bidding before his turn.
    TurnError,
    /// The given bid was not higher than the previous one.
    NonRaisedTarget,
    /// Cannot complete the auction when it is still running.
    AuctionRunning,
    /// No contract was offered during the auction, it cannot complete.
    NoContract,
    /// The contract was coinched too many times.
    OverCoinche,
}

impl fmt::Display for BidError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &BidError::AuctionClosed => write!(f, "auctions are closed"),
            &BidError::TurnError => write!(f, "invalid turn order"),
            &BidError::NonRaisedTarget => write!(f, "bid must be higher than current contract"),
            &BidError::AuctionRunning => write!(f, "the auction are still running"),
            &BidError::NoContract => write!(f, "no contract was offered"),
            &BidError::OverCoinche => write!(f, "contract is already sur-coinched"),
        }
    }
}

impl Auction {
    /// Starts a new auction, starting with the player `first`.
    pub fn new(first: pos::PlayerPos) -> Self {
        Auction {
            history: Vec::new(),
            pass_count: 0,
            state: AuctionState::Bidding,
            first: first,
            players: super::deal_hands(),
        }
    }

    /// Returns the current state of the auctions.
    pub fn get_state(&self) -> AuctionState {
        self.state
    }

    fn can_bid(&self, target: Target) -> Result<(), BidError> {
        if self.state != AuctionState::Bidding {
            return Err(BidError::AuctionClosed);
        }

        if !self.history.is_empty() {
            if target.score() <= self.history[self.history.len() - 1].target.score() {
                return Err(BidError::NonRaisedTarget);
            }
        }

        Ok(())
    }

    /// Returns the player that is expected to play next.
    pub fn next_player(&self) -> pos::PlayerPos {
        let base = if let Some(contract) = self.history.last() {
            contract.author.next()
        } else {
            self.first
        };
        base.next_n(self.pass_count)
    }

    /// Bid a new, higher contract.
    pub fn bid(&mut self,
               pos: pos::PlayerPos,
               trump: cards::Suit,
               target: Target)
               -> Result<AuctionState, BidError> {
        if pos != self.next_player() {
            return Err(BidError::TurnError);
        }

        match self.can_bid(target) {
            Err(err) => return Err(err),
            Ok(_) => (),
        }

        // If we're all the way to the top, there's nowhere else to go
        if target == Target::ContractCapot {
            self.state = AuctionState::Coinching;
        }

        let contract = Contract::new(pos, trump, target);
        self.history.push(contract);
        self.pass_count = 0;

        // Only stops the bids if the guy asked for a capot
        Ok(self.state)
    }

    /// Look at the last offered contract.
    ///
    /// Returns `None` if no contract was offered yet.
    pub fn current_contract(&self) -> Option<&Contract> {
        if self.history.is_empty() {
            None
        } else {
            Some(&self.history[self.history.len() - 1])
        }
    }

    /// Returns the players cards.
    pub fn hands(&self) -> [cards::Hand; 4] {
        self.players
    }

    /// The current player passes his turn.
    ///
    /// Returns the new auction state :
    ///
    /// * `AuctionState::Cancelled` if all players passed
    /// * `AuctionState::Over` if 3 players passed in a row
    /// * The previous state otherwise
    pub fn pass(&mut self, pos: pos::PlayerPos) -> Result<AuctionState, BidError> {
        if pos != self.next_player() {
            return Err(BidError::TurnError);
        }

        self.pass_count += 1;

        // After 3 passes, we're back to the contract author, and we can start.
        if !self.history.is_empty() {
            if self.pass_count >= 3 {
                self.state = AuctionState::Over;
            }
        } else {
            if self.pass_count >= 4 {
                self.state = AuctionState::Cancelled;
            }
        };

        Ok(self.state)
    }

    /// Attempt to coinche the current contract.
    pub fn coinche(&mut self, pos: pos::PlayerPos) -> Result<AuctionState, BidError> {
        if pos != self.next_player() {
            return Err(BidError::TurnError);
        }

        if self.history.is_empty() {
            return Err(BidError::NoContract);
        }

        let i = self.history.len() - 1;
        if self.history[i].coinche_level > 1 {
            return Err(BidError::OverCoinche);
        }

        self.history[i].coinche_level += 1;
        // Stop if we are already sur-coinching
        self.state = if self.history[i].coinche_level == 2 {
            AuctionState::Over
        } else {
            AuctionState::Coinching
        };

        Ok(self.state)
    }

    /// Consumes a complete auction to enter the second game phase.
    ///
    /// If the auction was ready, returns `Ok<GameState>`
    pub fn complete(&mut self) -> Result<game::GameState, BidError> {
        if self.state != AuctionState::Over {
            Err(BidError::AuctionRunning)
        } else if self.history.is_empty() {
            Err(BidError::NoContract)
        } else {
            Ok(game::GameState::new(self.first,
                                    self.players,
                                    self.history.pop().expect("contract history empty")))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {cards, pos};

    #[test]
    fn test_auction() {
        let mut auction = Auction::new(pos::PlayerPos(0));

        assert!(auction.state == AuctionState::Bidding);

        // First three people pass.
        assert_eq!(auction.pass(pos::P0), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P1), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P2), Ok(AuctionState::Bidding));

        assert_eq!(auction.pass(pos::P1), Err(BidError::TurnError));
        assert_eq!(auction.coinche(pos::P2), Err(BidError::TurnError));

        // Someone bids.
        assert_eq!(auction.bid(pos::P3, cards::HEART, Target::Contract80),
                   Ok(AuctionState::Bidding));
        assert_eq!(auction.bid(pos::P0, cards::CLUB, Target::Contract80)
                          .err(),
                   Some(BidError::NonRaisedTarget));
        assert_eq!(auction.bid(pos::P1, cards::CLUB, Target::Contract100)
                          .err(),
                   Some(BidError::TurnError));
        assert_eq!(auction.pass(pos::P0), Ok(AuctionState::Bidding));
        // Partner surbids
        assert_eq!(auction.bid(pos::P1, cards::HEART, Target::Contract100),
                   Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P2), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P3), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P0), Ok(AuctionState::Over));

        assert!(auction.state == AuctionState::Over);

        match auction.complete() {
            Err(_) => assert!(false),
            _ => {}
        }
    }
}
<commit_msg>Add ToString to bid::Target<commit_after>//! Auctions and bidding during the first phase of the game.

use std::fmt;
use std::str::FromStr;

use super::game;
use super::cards;
use super::pos;

use rustc_serialize;

/// Goal set by a contract.
///
/// Determines the winning conditions and the score on success.
#[derive(PartialEq,Clone,Copy,Debug)]
pub enum Target {
    /// Team must get 80 points
    Contract80,
    /// Team must get 90 points
    Contract90,
    /// Team must get 100 points
    Contract100,
    /// Team must get 110 points
    Contract110,
    /// Team must get 120 points
    Contract120,
    /// Team must get 130 points
    Contract130,
    /// Team must get 140 points
    Contract140,
    /// Team must get 150 points
    Contract150,
    /// Team must get 160 points
    Contract160,
    /// Team must win all tricks
    ContractCapot,
}

impl rustc_serialize::Encodable for Target {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_str(self.to_str())
    }
}

impl rustc_serialize::Decodable for Target {
    fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
        let content = try!(d.read_str());
        Self::from_str(&content).map_err(|s| d.error(&s))
    }
}

impl Target {
    /// Returns the score this target would give on success.
    pub fn score(&self) -> i32 {
        match *self {
            Target::Contract80 => 80,
            Target::Contract90 => 90,
            Target::Contract100 => 100,
            Target::Contract110 => 110,
            Target::Contract120 => 120,
            Target::Contract130 => 130,
            Target::Contract140 => 140,
            Target::Contract150 => 150,
            Target::Contract160 => 160,
            Target::ContractCapot => 250,
        }
    }

    pub fn to_str(&self) -> &'static str {
        match self {
            &Target::Contract80 => "80",
            &Target::Contract90 => "90",
            &Target::Contract100 => "100",
            &Target::Contract110 => "110",
            &Target::Contract120 => "120",
            &Target::Contract130 => "130",
            &Target::Contract140 => "140",
            &Target::Contract150 => "150",
            &Target::Contract160 => "160",
            &Target::ContractCapot => "Capot",
        }
    }

    /// Determines whether this target was reached.
    pub fn victory(&self, points: i32, capot: bool) -> bool {
        match *self {
            Target::ContractCapot => capot,
            other => points >= other.score(),
        }
    }
}

impl FromStr for Target {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        match s {
            "80" => Ok(Target::Contract80),
            "90" => Ok(Target::Contract90),
            "100" => Ok(Target::Contract100),
            "110" => Ok(Target::Contract110),
            "120" => Ok(Target::Contract120),
            "130" => Ok(Target::Contract130),
            "140" => Ok(Target::Contract140),
            "150" => Ok(Target::Contract150),
            "160" => Ok(Target::Contract160),
            "Capot" => Ok(Target::ContractCapot),
            _ => Err(format!("invalid target: {}", s)),
        }
    }
}

impl ToString for Target {
    fn to_string(&self) -> String {
        self.to_str().to_string()
    }
}

/// Contract taken by a team.
///
/// Composed of a trump suit and a target to reach.
#[derive(Clone,Debug,RustcEncodable,RustcDecodable)]
pub struct Contract {
    /// Initial author of the contract.
    pub author: pos::PlayerPos,
    /// Trump suit for this game.
    pub trump: cards::Suit,
    /// Target for the contract.
    pub target: Target,
    /// Level of coinche:
    ///
    /// * `0`: not coinched
    /// * `1`: coinched
    /// * `2`: surcoinched
    pub coinche_level: i32,
}

impl Contract {
    fn new(author: pos::PlayerPos, trump: cards::Suit, target: Target) -> Self {
        Contract {
            author: author,
            trump: trump,
            target: target,
            coinche_level: 0,
        }
    }
}

/// Current state of an auction
#[derive(PartialEq,Clone,Copy,Debug)]
pub enum AuctionState {
    /// Players are still bidding for the highest contract
    Bidding,
    /// One player coinched, maybe another one will surcoinche?
    Coinching,
    /// Auction is over, game will begin
    Over,
    /// No contract was taken, a new game will start
    Cancelled,
}

/// Represents the entire auction process.
pub struct Auction {
    history: Vec<Contract>,
    pass_count: usize,
    first: pos::PlayerPos,
    state: AuctionState,
    players: [cards::Hand; 4],
}

/// Possible error occuring during an Auction.
#[derive(PartialEq,Debug)]
pub enum BidError {
    /// The auction was closed and does not accept more contracts.
    AuctionClosed,
    /// A player tried bidding before his turn.
    TurnError,
    /// The given bid was not higher than the previous one.
    NonRaisedTarget,
    /// Cannot complete the auction when it is still running.
    AuctionRunning,
    /// No contract was offered during the auction, it cannot complete.
    NoContract,
    /// The contract was coinched too many times.
    OverCoinche,
}

impl fmt::Display for BidError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &BidError::AuctionClosed => write!(f, "auctions are closed"),
            &BidError::TurnError => write!(f, "invalid turn order"),
            &BidError::NonRaisedTarget => write!(f, "bid must be higher than current contract"),
            &BidError::AuctionRunning => write!(f, "the auction are still running"),
            &BidError::NoContract => write!(f, "no contract was offered"),
            &BidError::OverCoinche => write!(f, "contract is already sur-coinched"),
        }
    }
}

impl Auction {
    /// Starts a new auction, starting with the player `first`.
    pub fn new(first: pos::PlayerPos) -> Self {
        Auction {
            history: Vec::new(),
            pass_count: 0,
            state: AuctionState::Bidding,
            first: first,
            players: super::deal_hands(),
        }
    }

    /// Returns the current state of the auctions.
    pub fn get_state(&self) -> AuctionState {
        self.state
    }

    fn can_bid(&self, target: Target) -> Result<(), BidError> {
        if self.state != AuctionState::Bidding {
            return Err(BidError::AuctionClosed);
        }

        if !self.history.is_empty() {
            if target.score() <= self.history[self.history.len() - 1].target.score() {
                return Err(BidError::NonRaisedTarget);
            }
        }

        Ok(())
    }

    /// Returns the player that is expected to play next.
    pub fn next_player(&self) -> pos::PlayerPos {
        let base = if let Some(contract) = self.history.last() {
            contract.author.next()
        } else {
            self.first
        };
        base.next_n(self.pass_count)
    }

    /// Bid a new, higher contract.
    pub fn bid(&mut self,
               pos: pos::PlayerPos,
               trump: cards::Suit,
               target: Target)
        -> Result<AuctionState, BidError> {
            if pos != self.next_player() {
                return Err(BidError::TurnError);
            }

            match self.can_bid(target) {
                Err(err) => return Err(err),
                Ok(_) => (),
            }

            // If we're all the way to the top, there's nowhere else to go
            if target == Target::ContractCapot {
                self.state = AuctionState::Coinching;
            }

            let contract = Contract::new(pos, trump, target);
            self.history.push(contract);
            self.pass_count = 0;

            // Only stops the bids if the guy asked for a capot
            Ok(self.state)
        }

    /// Look at the last offered contract.
    ///
    /// Returns `None` if no contract was offered yet.
    pub fn current_contract(&self) -> Option<&Contract> {
        if self.history.is_empty() {
            None
        } else {
            Some(&self.history[self.history.len() - 1])
        }
    }

    /// Returns the players cards.
    pub fn hands(&self) -> [cards::Hand; 4] {
        self.players
    }

    /// The current player passes his turn.
    ///
    /// Returns the new auction state :
    ///
    /// * `AuctionState::Cancelled` if all players passed
    /// * `AuctionState::Over` if 3 players passed in a row
    /// * The previous state otherwise
    pub fn pass(&mut self, pos: pos::PlayerPos) -> Result<AuctionState, BidError> {
        if pos != self.next_player() {
            return Err(BidError::TurnError);
        }

        self.pass_count += 1;

        // After 3 passes, we're back to the contract author, and we can start.
        if !self.history.is_empty() {
            if self.pass_count >= 3 {
                self.state = AuctionState::Over;
            }
        } else {
            if self.pass_count >= 4 {
                self.state = AuctionState::Cancelled;
            }
        };

        Ok(self.state)
    }

    /// Attempt to coinche the current contract.
    pub fn coinche(&mut self, pos: pos::PlayerPos) -> Result<AuctionState, BidError> {
        if pos != self.next_player() {
            return Err(BidError::TurnError);
        }

        if self.history.is_empty() {
            return Err(BidError::NoContract);
        }

        let i = self.history.len() - 1;
        if self.history[i].coinche_level > 1 {
            return Err(BidError::OverCoinche);
        }

        self.history[i].coinche_level += 1;
        // Stop if we are already sur-coinching
        self.state = if self.history[i].coinche_level == 2 {
            AuctionState::Over
        } else {
            AuctionState::Coinching
        };

        Ok(self.state)
    }

    /// Consumes a complete auction to enter the second game phase.
    ///
    /// If the auction was ready, returns `Ok<GameState>`
    pub fn complete(&mut self) -> Result<game::GameState, BidError> {
        if self.state != AuctionState::Over {
            Err(BidError::AuctionRunning)
        } else if self.history.is_empty() {
            Err(BidError::NoContract)
        } else {
            Ok(game::GameState::new(self.first,
                                    self.players,
                                    self.history.pop().expect("contract history empty")))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {cards, pos};

    #[test]
    fn test_auction() {
        let mut auction = Auction::new(pos::PlayerPos(0));

        assert!(auction.state == AuctionState::Bidding);

        // First three people pass.
        assert_eq!(auction.pass(pos::P0), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P1), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P2), Ok(AuctionState::Bidding));

        assert_eq!(auction.pass(pos::P1), Err(BidError::TurnError));
        assert_eq!(auction.coinche(pos::P2), Err(BidError::TurnError));

        // Someone bids.
        assert_eq!(auction.bid(pos::P3, cards::HEART, Target::Contract80),
        Ok(AuctionState::Bidding));
        assert_eq!(auction.bid(pos::P0, cards::CLUB, Target::Contract80)
                   .err(),
                   Some(BidError::NonRaisedTarget));
        assert_eq!(auction.bid(pos::P1, cards::CLUB, Target::Contract100)
                   .err(),
                   Some(BidError::TurnError));
        assert_eq!(auction.pass(pos::P0), Ok(AuctionState::Bidding));
        // Partner surbids
        assert_eq!(auction.bid(pos::P1, cards::HEART, Target::Contract100),
        Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P2), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P3), Ok(AuctionState::Bidding));
        assert_eq!(auction.pass(pos::P0), Ok(AuctionState::Over));

        assert!(auction.state == AuctionState::Over);

        match auction.complete() {
            Err(_) => assert!(false),
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::mem;

use self::WillOrMight::{Will,Might};
use self::BufferedParserState::{Beginning,Middle,End};
use self::MatchResult::{WillMatch, MightMatch, WontMatch, MatchedAll, MatchedSome};

// ----------- Types for matching -------------

// Borrowing encoding of paramaterized types from
// https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#encoding-higher-kinded-types

pub trait RefType<'a> {
    type Ref;
}

pub type Ref<'a,T> where T: RefType<'a> = T::Ref;

pub struct ConstRef<T> (T);

pub type UnitRef = ConstRef<()>;

impl<'a,T> RefType<'a> for ConstRef<T> {
    type Ref = T;
}

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum MatchResult<T> {
    WillMatch,
    MightMatch,
    WontMatch,
    MatchedAll,
    MatchedSome(T,T),
}

pub trait Matcher<T> where T: 'static+for<'a> RefType<'a> {
    // If push returns MightMatch or WontMatch, it is side-effect-free
    fn push<'a>(&mut self, value: Ref<'a,T>) -> MatchResult<Ref<'a,T>>;
    // Resets the matcher state back to its initial state
    fn done(&mut self);
}

// ----------- Types for parsers ------------

pub trait Consumer<T> where T: 'static+for<'a> RefType<'a> {
    fn accept<'a>(&mut self, arg: Ref<'a,T>);
}

pub trait Parser<S,T> where S: 'static+for<'a> RefType<'a>, T: 'static+for<'a> RefType<'a> {
    fn push<'a>(&mut self, value: Ref<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<Ref<'a,S>>;
    fn done(&mut self, downstream: &mut Consumer<T>);
}

pub trait BufferableMatcher<S,T> where S: 'static+for<'a> RefType<'a>, T: Parser<S,S> {
    fn buffer(self) -> T;
}

// ----------- Matching strings -------------

pub struct StrRef;

impl<'a> RefType<'a> for StrRef {
    type Ref = &'a str;
}

// ----------- Constant parsers -------------

pub struct ConstantParser {
    constant: String,
    offset: Option<usize>,
}

impl Parser<StrRef,UnitRef> for ConstantParser {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<UnitRef>) -> MatchResult<&'a str> {
        match self.offset.take() {
            None => MatchedSome("",string),
            Some(index) if string == &self.constant[index..]           => { downstream.accept(()); MatchedAll },
            Some(index) if string.starts_with(&self.constant[index..]) => { downstream.accept(()); MatchedSome(&string[..index],&string[index..]) },
            Some(index) if self.constant[index..].starts_with(string)  => { self.offset = Some(index + string.len()); MightMatch },
            Some(_)                                                    => { WontMatch },
        }
    }
    fn done(&mut self, _: &mut Consumer<UnitRef>) {
        self.offset = Some(0);
    }
}

pub fn constant(string: String) -> ConstantParser {
    ConstantParser{ constant: string, offset: Some(0) }
}

// If m is a Matcher<StrRef> then m.buffer() is a Parser<StrRef,StrRef>.
// It does as little buffering as it can, but it does allocate as buffer for the case
// where the boundary marker of the input is misaligned with that of the matcher.
// For example, m is matching string literals, and the input is '"abc' followed by 'def"'
// we have to buffer up '"abc'.

enum WillOrMight {
    Will,
    Might,
}

enum BufferedParserState {
    Beginning,
    Middle(String,WillOrMight),
    End,
}

pub struct BufferedParser<S> {
    matcher: S,
    state: BufferedParserState,
}

impl<S> Parser<StrRef,StrRef> for BufferedParser<S> where S: Matcher<StrRef> {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<StrRef>) -> MatchResult<&'a str> {
        match mem::replace(&mut self.state, End) {
            Beginning => {
                let result = self.matcher.push(string);
                match result {
                    WillMatch               => self.state = Middle(String::from(string), Will),
                    MightMatch              => self.state = Middle(String::from(string), Might),
                    WontMatch               => (),
                    MatchedAll              => downstream.accept(string),
                    MatchedSome(matched, _) => downstream.accept(matched),
                }
                result
            },
            Middle(mut buffer, _) => {
                let result = self.matcher.push(string);
                match result {
                    WillMatch               => { buffer.push_str(string); self.state = Middle(buffer, Will); },
                    MightMatch              => { buffer.push_str(string); self.state = Middle(buffer, Might); },
                    WontMatch               => (),
                    MatchedAll              => { buffer.push_str(string); downstream.accept(&*buffer); },
                    MatchedSome(matched, _) => { buffer.push_str(matched); downstream.accept(&*buffer); },
                }
                result
            }
            End => MatchedSome("",string),
        }
    }
    fn done(&mut self, downstream: &mut Consumer<StrRef>) {
        match mem::replace(&mut self.state, Beginning) {
            Middle(buffer, Will) => downstream.accept(&*buffer),
            _                    => (),
        }
    }
}

impl<S> BufferableMatcher<StrRef,BufferedParser<S>> for S where S: Matcher<StrRef> {
    fn buffer(self) -> BufferedParser<S> {
        BufferedParser{ matcher: self, state: Beginning }
    }
}
<commit_msg>Added draft (and buggy) implementation of and_then.<commit_after>use std::mem;

use self::WillOrMight::{Will,Might};
use self::BufferedParserState::{Beginning,Middle,End};
use self::MatchResult::{WillMatch, MightMatch, WontMatch, MatchedAll, MatchedSome};

// ----------- Types for matching -------------

// Borrowing encoding of paramaterized types from
// https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#encoding-higher-kinded-types

pub trait RefType<'a> {
    type Ref;
}

pub type Ref<'a,T> where T: RefType<'a> = T::Ref;

pub struct ConstRef<T> (T);

pub type UnitRef = ConstRef<()>;

impl<'a,T> RefType<'a> for ConstRef<T> {
    type Ref = T;
}

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum MatchResult<T> {
    WillMatch,
    MightMatch,
    WontMatch,
    MatchedAll,
    MatchedSome(T,T),
}

pub trait Matcher<T> where T: 'static+for<'a> RefType<'a> {
    // If push returns MightMatch or WontMatch, it is side-effect-free
    fn push<'a>(&mut self, value: Ref<'a,T>) -> MatchResult<Ref<'a,T>>;
    // Resets the matcher state back to its initial state
    fn done(&mut self);
}

// ----------- Types for parsers ------------

pub trait Consumer<T> where T: 'static+for<'a> RefType<'a> {
    fn accept<'a>(&mut self, arg: Ref<'a,T>);
}

pub trait Parser<S,T> where S: 'static+for<'a> RefType<'a>, T: 'static+for<'a> RefType<'a> {
    fn push<'a>(&mut self, value: Ref<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<Ref<'a,S>>;
    fn done(&mut self, downstream: &mut Consumer<T>);
    // fn and_then<R: Parser<S,T>>(self, other: R) -> AndThenParser<Self,R> {
    //     AndThenParser{ lhs: self, rhs: other, in_lhs: true }
    // }
}

pub trait BufferableMatcher<S,T> where S: 'static+for<'a> RefType<'a>, T: Parser<S,S> {
    fn buffer(self) -> T;
}

// ----------- Sequencing ---------------

pub struct AndThenParser<L,R> {
    lhs: L,
    rhs: R,
    in_lhs: bool,
}

impl<S,T,L,R> Parser<S,T> for AndThenParser<L,R> where L: Parser<S,T>, R: Parser<S,T>, S: 'static+for<'a> RefType<'a>, T: 'static+for<'a> RefType<'a>  {
    fn push<'a>(&mut self, value: Ref<'a,S>, downstream: &mut Consumer<T>) -> MatchResult<Ref<'a,S>> {
        if self.in_lhs {
            match self.lhs.push(value, downstream) {
                WillMatch => MightMatch, // TODO: we are returning MightMatch even though lhs may have side-effects
                MightMatch => MightMatch,
                WontMatch => WontMatch,
                MatchedAll => { self.in_lhs = false; MightMatch },
                MatchedSome(_,rest) => { self.in_lhs = false; self.rhs.push(rest, downstream) } // TODO: if this returns MatchedSome(matched,rest) then this is the wrong matched
            }
        } else {
            self.rhs.push(value, downstream)
        }
    }
    fn done(&mut self, downstream: &mut Consumer<T>) {
        self.lhs.done(downstream);
        self.rhs.done(downstream);
        self.in_lhs = true;
    }
}

// ----------- Matching strings -------------

pub struct StrRef;

impl<'a> RefType<'a> for StrRef {
    type Ref = &'a str;
}

// ----------- Constant parsers -------------

pub struct ConstantParser {
    constant: String,
    offset: Option<usize>,
}

impl Parser<StrRef,UnitRef> for ConstantParser {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<UnitRef>) -> MatchResult<&'a str> {
        match self.offset.take() {
            None => MatchedSome("",string),
            Some(index) if string == &self.constant[index..]           => { downstream.accept(()); MatchedAll },
            Some(index) if string.starts_with(&self.constant[index..]) => { downstream.accept(()); MatchedSome(&string[..index],&string[index..]) },
            Some(index) if self.constant[index..].starts_with(string)  => { self.offset = Some(index + string.len()); MightMatch },
            Some(_)                                                    => { WontMatch },
        }
    }
    fn done(&mut self, _: &mut Consumer<UnitRef>) {
        self.offset = Some(0);
    }
}

pub fn constant(string: String) -> ConstantParser {
    ConstantParser{ constant: string, offset: Some(0) }
}

// If m is a Matcher<StrRef> then m.buffer() is a Parser<StrRef,StrRef>.
// It does as little buffering as it can, but it does allocate as buffer for the case
// where the boundary marker of the input is misaligned with that of the matcher.
// For example, m is matching string literals, and the input is '"abc' followed by 'def"'
// we have to buffer up '"abc'.

enum WillOrMight {
    Will,
    Might,
}

enum BufferedParserState {
    Beginning,
    Middle(String,WillOrMight),
    End,
}

pub struct BufferedParser<S> {
    matcher: S,
    state: BufferedParserState,
}

impl<S> Parser<StrRef,StrRef> for BufferedParser<S> where S: Matcher<StrRef> {
    fn push<'a>(&mut self, string: &'a str, downstream: &mut Consumer<StrRef>) -> MatchResult<&'a str> {
        match mem::replace(&mut self.state, End) {
            Beginning => {
                let result = self.matcher.push(string);
                match result {
                    WillMatch               => self.state = Middle(String::from(string), Will),
                    MightMatch              => self.state = Middle(String::from(string), Might),
                    WontMatch               => (),
                    MatchedAll              => downstream.accept(string),
                    MatchedSome(matched, _) => downstream.accept(matched),
                }
                result
            },
            Middle(mut buffer, _) => {
                let result = self.matcher.push(string);
                match result {
                    WillMatch               => { buffer.push_str(string); self.state = Middle(buffer, Will); },
                    MightMatch              => { buffer.push_str(string); self.state = Middle(buffer, Might); },
                    WontMatch               => (),
                    MatchedAll              => { buffer.push_str(string); downstream.accept(&*buffer); },
                    MatchedSome(matched, _) => { buffer.push_str(matched); downstream.accept(&*buffer); },
                }
                result
            }
            End => MatchedSome("",string),
        }
    }
    fn done(&mut self, downstream: &mut Consumer<StrRef>) {
        match mem::replace(&mut self.state, Beginning) {
            Middle(buffer, Will) => downstream.accept(&*buffer),
            _                    => (),
        }
    }
}

impl<S> BufferableMatcher<StrRef,BufferedParser<S>> for S where S: Matcher<StrRef> {
    fn buffer(self) -> BufferedParser<S> {
        BufferedParser{ matcher: self, state: Beginning }
    }
}
<|endoftext|>


<commit_before>//! Diagnostics produced by `hir_def`.

use std::any::Any;
use std::fmt::Write;

use cfg::{CfgExpr, CfgOptions, DnfExpr};
use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
use syntax::{ast, AstPtr, SyntaxNodePtr};

use hir_expand::{HirFileId, InFile};

// Diagnostic: unresolved-module
//
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
#[derive(Debug)]
pub struct UnresolvedModule {
    pub file: HirFileId,
    pub decl: AstPtr<ast::Module>,
    pub candidate: String,
}

impl Diagnostic for UnresolvedModule {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-module")
    }
    fn message(&self) -> String {
        "unresolved module".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.decl.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

// Diagnostic: unresolved-extern-crate
//
// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
#[derive(Debug)]
pub struct UnresolvedExternCrate {
    pub file: HirFileId,
    pub item: AstPtr<ast::ExternCrate>,
}

impl Diagnostic for UnresolvedExternCrate {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-extern-crate")
    }
    fn message(&self) -> String {
        "unresolved extern crate".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.item.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

// Diagnostic: unresolved-import
//
// This diagnostic is triggered if rust-analyzer is unable to discover imported module.
#[derive(Debug)]
pub struct UnresolvedImport {
    pub file: HirFileId,
    pub node: AstPtr<ast::UseTree>,
}

impl Diagnostic for UnresolvedImport {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-import")
    }
    fn message(&self) -> String {
        "unresolved import".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.node.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
    fn is_experimental(&self) -> bool {
        // This currently results in false positives in the following cases:
        // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
        // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
        // - proc macros and/or proc macro generated code
        true
    }
}

// Diagnostic: unconfigured-code
//
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
#[derive(Debug)]
pub struct InactiveCode {
    pub file: HirFileId,
    pub node: SyntaxNodePtr,
    pub cfg: CfgExpr,
    pub opts: CfgOptions,
}

impl Diagnostic for InactiveCode {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("inactive-code")
    }
    fn message(&self) -> String {
        let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
        let mut buf = "code is inactive due to #[cfg] directives".to_string();

        if let Some(inactive) = inactive {
            write!(buf, ": {}", inactive).unwrap();
        }

        buf
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.node.clone())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}
<commit_msg>Use format_to<commit_after>//! Diagnostics produced by `hir_def`.

use std::any::Any;
use stdx::format_to;

use cfg::{CfgExpr, CfgOptions, DnfExpr};
use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
use hir_expand::{HirFileId, InFile};
use syntax::{ast, AstPtr, SyntaxNodePtr};

// Diagnostic: unresolved-module
//
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
#[derive(Debug)]
pub struct UnresolvedModule {
    pub file: HirFileId,
    pub decl: AstPtr<ast::Module>,
    pub candidate: String,
}

impl Diagnostic for UnresolvedModule {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-module")
    }
    fn message(&self) -> String {
        "unresolved module".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.decl.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

// Diagnostic: unresolved-extern-crate
//
// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
#[derive(Debug)]
pub struct UnresolvedExternCrate {
    pub file: HirFileId,
    pub item: AstPtr<ast::ExternCrate>,
}

impl Diagnostic for UnresolvedExternCrate {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-extern-crate")
    }
    fn message(&self) -> String {
        "unresolved extern crate".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.item.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}

// Diagnostic: unresolved-import
//
// This diagnostic is triggered if rust-analyzer is unable to discover imported module.
#[derive(Debug)]
pub struct UnresolvedImport {
    pub file: HirFileId,
    pub node: AstPtr<ast::UseTree>,
}

impl Diagnostic for UnresolvedImport {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("unresolved-import")
    }
    fn message(&self) -> String {
        "unresolved import".to_string()
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.node.clone().into())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
    fn is_experimental(&self) -> bool {
        // This currently results in false positives in the following cases:
        // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
        // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
        // - proc macros and/or proc macro generated code
        true
    }
}

// Diagnostic: unconfigured-code
//
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
#[derive(Debug)]
pub struct InactiveCode {
    pub file: HirFileId,
    pub node: SyntaxNodePtr,
    pub cfg: CfgExpr,
    pub opts: CfgOptions,
}

impl Diagnostic for InactiveCode {
    fn code(&self) -> DiagnosticCode {
        DiagnosticCode("inactive-code")
    }
    fn message(&self) -> String {
        let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
        let mut buf = "code is inactive due to #[cfg] directives".to_string();

        if let Some(inactive) = inactive {
            format_to!(buf, ": {}", inactive);
        }

        buf
    }
    fn display_source(&self) -> InFile<SyntaxNodePtr> {
        InFile::new(self.file, self.node.clone())
    }
    fn as_any(&self) -> &(dyn Any + Send + 'static) {
        self
    }
}
<|endoftext|>


<commit_before>use antidote::RwLock;
use irc::client::prelude::*;
use std::thread::{sleep, spawn};
use std::{fmt, sync::Arc, time::Duration};

use chrono::{self, NaiveDateTime};
use time;

use plugin::*;
pub mod database;
mod parser;
use self::database::Database;
use self::parser::CommandParser;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::ResultExt;

fn get_time() -> NaiveDateTime {
    let tm = time::now().to_timespec();
    NaiveDateTime::from_timestamp(tm.sec, 0u32)
}

fn get_events<T: Database>(db: &RwLock<T>, in_next: chrono::Duration) -> Vec<database::Event> {
    loop {
        let before = get_time() + in_next;
        match db.read().get_events_before(&before) {
            Ok(events) => return events,
            Err(e) => {
                if e.kind() != ErrorKind::NotFound {
                    error!("Failed to get events: {}", e);
                }
            }
        }

        sleep(in_next.to_std().expect("Failed to convert look ahead time"));
    }
}

fn run<T: Database>(client: &IrcClient, db: Arc<RwLock<T>>) {
    let look_ahead = chrono::Duration::minutes(2);

    let mut events = get_events(&db, look_ahead);

    let mut sleep_time = look_ahead
        .to_std()
        .expect("Failed to convert look ahead time");

    loop {
        let now = get_time();
        for event in events {
            if event.time <= now {
                let msg = format!("Reminder from {}: {}", event.author, event.content);
                if let Err(e) = client.send_notice(&event.receiver, &msg) {
                    error!("Failed to send reminder: {}", e);
                } else {
                    debug!("Sent reminder {:?}", event);

                    if let Some(repeat) = event.repeat {
                        let next_time = event.time + chrono::Duration::seconds(repeat as i64);

                        if let Err(e) = db.write().update_event_time(event.id, &next_time) {
                            error!("Failed to update reminder: {}", e);
                        } else {
                            debug!("Updated time on: {:?}", event);
                        }
                    } else if let Err(e) = db.write().delete_event(event.id) {
                        error!("Failed to delete reminder: {}", e);
                    }
                }
            } else {
                let until_event = (event.time - now)
                    .to_std()
                    .expect("Failed to convert until event time");

                if until_event < sleep_time {
                    sleep_time = until_event + Duration::from_secs(1);
                }
            }
        }

        sleep(sleep_time);
        sleep_time = Duration::from_secs(120);

        events = get_events(&db, look_ahead);
    }
}

#[derive(PluginName)]
pub struct Remind<T: 'static + Database> {
    events: Arc<RwLock<T>>,
    has_reminder: RwLock<bool>,
}

impl<T: 'static + Database> Remind<T> {
    pub fn new(db: T) -> Self {
        let events = Arc::new(RwLock::new(db));

        Remind {
            events: events,
            has_reminder: RwLock::new(false),
        }
    }

    fn set(&self, command: PluginCommand) -> Result<&str, RemindError> {
        let parser = CommandParser::try_from_tokens(command.tokens)?;
        debug!("parser: {:?}", parser);

        let mut target = parser.get_target();
        if target == "me" {
            target = &command.source;
        }

        let event = database::NewEvent {
            receiver: target,
            content: &parser.get_message(),
            author: &command.source,
            time: &parser.get_time(Duration::from_secs(120))?,
            repeat: parser
                .get_repeat(Duration::from_secs(300))?
                .map(|d| d.as_secs()),
        };

        debug!("New event: {:?}", event);

        Ok(self.events.write().insert_event(&event).map(|()| "Got it")?)
    }

    fn list(&self, user: &str) -> Result<String, RemindError> {
        let mut events = self.events.read().get_user_events(user)?;

        let mut list = events.remove(0).to_string();
        for ev in events {
            list.push_str("\r\n");
            list.push_str(&ev.to_string());
        }

        Ok(list)
    }

    fn delete(&self, mut command: PluginCommand) -> Result<&str, RemindError> {
        let id = command
            .tokens
            .remove(0)
            .parse::<i64>()
            .context(ErrorKind::Parsing)?;
        let event = self.events.read().get_event(id)?;

        if event.receiver.eq_ignore_ascii_case(&command.source)
            || event.author.eq_ignore_ascii_case(&command.source)
        {
            self.events
                .write()
                .delete_event(id)
                .map(|()| "Successfully deleted")
        } else {
            Ok("Only the author or receiver can delete a reminder")
        }
    }

    fn help(&self) -> &str {
        "usage: remind <subcommand>\r\n\
         subcommands: new, list, delete, help"
    }
}

impl<T: Database> Plugin for Remind<T> {
    fn execute(&self, client: &IrcClient, _: &Message) -> ExecutionStatus {
        let mut has_reminder = self.has_reminder.write();
        if !*has_reminder {
            let events = Arc::clone(&self.events);
            let client = client.clone();

            spawn(move || run(&client, events));

            *has_reminder = true;
        }

        ExecutionStatus::Done
    }

    fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
        panic!("Remind should not use frippy's threading")
    }

    fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            return Ok(client
                .send_notice(&command.source, &ErrorKind::InvalidCommand.to_string())
                .context(FrippyErrorKind::Connection)?);
        }

        let source = command.source.clone();

        let sub_command = command.tokens.remove(0);
        let response = match sub_command.as_ref() {
            "new" => self.set(command).map(|s| s.to_owned()),
            "delete" => self.delete(command).map(|s| s.to_owned()),
            "list" => self.list(&source),
            "help" => Ok(self.help().to_owned()),
            _ => Err(ErrorKind::InvalidCommand.into()),
        };

        let result = match response {
            Ok(msg) => client
                .send_notice(&source, &msg)
                .context(FrippyErrorKind::Connection)?,
            Err(e) => {
                let message = e.to_string();

                client
                    .send_notice(&source, &message)
                    .context(FrippyErrorKind::Connection)?;

                Err(e).context(FrippyErrorKind::Remind)?
            }
        };

        Ok(result)
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from(
            "Evaluation of commands is not implemented for remind at this time",
        ))
    }
}

impl<T: Database> fmt::Debug for Remind<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Remind {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "RemindError"]
    pub enum ErrorKind {
        /// Invalid command error
        #[fail(display = "Incorrect Command. Send \"currency help\" for help.")]
        InvalidCommand,

        /// Missing message error
        #[fail(display = "Reminder needs to have a description")]
        MissingMessage,

        /// Missing receiver error
        #[fail(display = "Specify who to remind")]
        MissingReceiver,

        /// Missing time error
        #[fail(display = "Reminder needs to have a time")]
        MissingTime,

        /// Invalid time error
        #[fail(display = "Could not parse time")]
        InvalidTime,

        /// Invalid date error
        #[fail(display = "Could not parse date")]
        InvalidDate,

        /// Parse error
        #[fail(display = "Could not parse integers")]
        Parsing,

        /// Ambigous time error
        #[fail(display = "Time specified is ambiguous")]
        AmbiguousTime,

        /// Time too short error
        #[fail(display = "Reminder needs to be in over 2 minutes")]
        TimeShort,

        /// Repeat time too short error
        #[fail(display = "Repeat time needs to be over 5 minutes")]
        RepeatTimeShort,

        /// Duplicate error
        #[fail(display = "Entry already exists")]
        Duplicate,

        /// Not found error
        #[fail(display = "No events found")]
        NotFound,
    }
}
<commit_msg>Remind: Change syntax and update help<commit_after>use antidote::RwLock;
use irc::client::prelude::*;
use std::thread::{sleep, spawn};
use std::{fmt, sync::Arc, time::Duration};

use chrono::{self, NaiveDateTime};
use time;

use plugin::*;
pub mod database;
mod parser;
use self::database::Database;
use self::parser::CommandParser;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::ResultExt;

fn get_time() -> NaiveDateTime {
    let tm = time::now().to_timespec();
    NaiveDateTime::from_timestamp(tm.sec, 0u32)
}

fn get_events<T: Database>(db: &RwLock<T>, in_next: chrono::Duration) -> Vec<database::Event> {
    loop {
        let before = get_time() + in_next;
        match db.read().get_events_before(&before) {
            Ok(events) => return events,
            Err(e) => {
                if e.kind() != ErrorKind::NotFound {
                    error!("Failed to get events: {}", e);
                }
            }
        }

        sleep(in_next.to_std().expect("Failed to convert look ahead time"));
    }
}

fn run<T: Database>(client: &IrcClient, db: Arc<RwLock<T>>) {
    let look_ahead = chrono::Duration::minutes(2);

    let mut events = get_events(&db, look_ahead);

    let mut sleep_time = look_ahead
        .to_std()
        .expect("Failed to convert look ahead time");

    loop {
        let now = get_time();
        for event in events {
            if event.time <= now {
                let msg = format!("Reminder from {}: {}", event.author, event.content);
                if let Err(e) = client.send_notice(&event.receiver, &msg) {
                    error!("Failed to send reminder: {}", e);
                } else {
                    debug!("Sent reminder {:?}", event);

                    if let Some(repeat) = event.repeat {
                        let next_time = event.time + chrono::Duration::seconds(repeat as i64);

                        if let Err(e) = db.write().update_event_time(event.id, &next_time) {
                            error!("Failed to update reminder: {}", e);
                        } else {
                            debug!("Updated time on: {:?}", event);
                        }
                    } else if let Err(e) = db.write().delete_event(event.id) {
                        error!("Failed to delete reminder: {}", e);
                    }
                }
            } else {
                let until_event = (event.time - now)
                    .to_std()
                    .expect("Failed to convert until event time");

                if until_event < sleep_time {
                    sleep_time = until_event + Duration::from_secs(1);
                }
            }
        }

        sleep(sleep_time);
        sleep_time = Duration::from_secs(120);

        events = get_events(&db, look_ahead);
    }
}

#[derive(PluginName)]
pub struct Remind<T: 'static + Database> {
    events: Arc<RwLock<T>>,
    has_reminder: RwLock<bool>,
}

impl<T: 'static + Database> Remind<T> {
    pub fn new(db: T) -> Self {
        let events = Arc::new(RwLock::new(db));

        Remind {
            events: events,
            has_reminder: RwLock::new(false),
        }
    }

    fn set(&self, command: PluginCommand) -> Result<&str, RemindError> {
        let parser = CommandParser::try_from_tokens(command.tokens)?;
        debug!("parser: {:?}", parser);

        let mut target = parser.get_target();
        if target == "me" {
            target = &command.source;
        }

        let event = database::NewEvent {
            receiver: target,
            content: &parser.get_message(),
            author: &command.source,
            time: &parser.get_time(Duration::from_secs(120))?,
            repeat: parser
                .get_repeat(Duration::from_secs(300))?
                .map(|d| d.as_secs()),
        };

        debug!("New event: {:?}", event);

        Ok(self.events.write().insert_event(&event).map(|()| "Got it")?)
    }

    fn list(&self, user: &str) -> Result<String, RemindError> {
        let mut events = self.events.read().get_user_events(user)?;

        let mut list = events.remove(0).to_string();
        for ev in events {
            list.push_str("\r\n");
            list.push_str(&ev.to_string());
        }

        Ok(list)
    }

    fn delete(&self, mut command: PluginCommand) -> Result<&str, RemindError> {
        let id = command
            .tokens
            .remove(0)
            .parse::<i64>()
            .context(ErrorKind::Parsing)?;
        let event = self.events.read().get_event(id)?;

        if event.receiver.eq_ignore_ascii_case(&command.source)
            || event.author.eq_ignore_ascii_case(&command.source)
        {
            self.events
                .write()
                .delete_event(id)
                .map(|()| "Successfully deleted")
        } else {
            Ok("Only the author or receiver can delete a reminder")
        }
    }

    fn help(&self) -> &str {
        "usage: remind <subcommand>\r\n\
         subcommands: user, list, delete, help\r\n\
         examples\r\n\
         remind user foo to sleep in 1 hour\r\n\
         remind user bar to leave early on 1.1 at 16:00 every week"
    }
}

impl<T: Database> Plugin for Remind<T> {
    fn execute(&self, client: &IrcClient, _: &Message) -> ExecutionStatus {
        let mut has_reminder = self.has_reminder.write();
        if !*has_reminder {
            let events = Arc::clone(&self.events);
            let client = client.clone();

            spawn(move || run(&client, events));

            *has_reminder = true;
        }

        ExecutionStatus::Done
    }

    fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
        panic!("Remind should not use frippy's threading")
    }

    fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            return Ok(client
                .send_notice(&command.source, &ErrorKind::InvalidCommand.to_string())
                .context(FrippyErrorKind::Connection)?);
        }

        let source = command.source.clone();

        let sub_command = command.tokens.remove(0);
        let response = match sub_command.as_ref() {
            "user" => self.set(command).map(|s| s.to_owned()),
            "delete" => self.delete(command).map(|s| s.to_owned()),
            "list" => self.list(&source),
            "help" => Ok(self.help().to_owned()),
            _ => Err(ErrorKind::InvalidCommand.into()),
        };

        let result = match response {
            Ok(msg) => client
                .send_notice(&source, &msg)
                .context(FrippyErrorKind::Connection)?,
            Err(e) => {
                let message = e.to_string();

                client
                    .send_notice(&source, &message)
                    .context(FrippyErrorKind::Connection)?;

                Err(e).context(FrippyErrorKind::Remind)?
            }
        };

        Ok(result)
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from(
            "Evaluation of commands is not implemented for remind at this time",
        ))
    }
}

impl<T: Database> fmt::Debug for Remind<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Remind {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "RemindError"]
    pub enum ErrorKind {
        /// Invalid command error
        #[fail(display = "Incorrect Command. Send \"currency help\" for help.")]
        InvalidCommand,

        /// Missing message error
        #[fail(display = "Reminder needs to have a description")]
        MissingMessage,

        /// Missing receiver error
        #[fail(display = "Specify who to remind")]
        MissingReceiver,

        /// Missing time error
        #[fail(display = "Reminder needs to have a time")]
        MissingTime,

        /// Invalid time error
        #[fail(display = "Could not parse time")]
        InvalidTime,

        /// Invalid date error
        #[fail(display = "Could not parse date")]
        InvalidDate,

        /// Parse error
        #[fail(display = "Could not parse integers")]
        Parsing,

        /// Ambigous time error
        #[fail(display = "Time specified is ambiguous")]
        AmbiguousTime,

        /// Time too short error
        #[fail(display = "Reminder needs to be in over 2 minutes")]
        TimeShort,

        /// Repeat time too short error
        #[fail(display = "Repeat time needs to be over 5 minutes")]
        RepeatTimeShort,

        /// Duplicate error
        #[fail(display = "Entry already exists")]
        Duplicate,

        /// Not found error
        #[fail(display = "No events found")]
        NotFound,
    }
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! generate_error_types {
    (
        $name: ident,
        $kindname: ident,
        $($kind:ident => $string:expr),*
    ) => {
        #[derive(Clone, Copy, Debug, PartialEq)]
        pub enum $kindname {
            $( $kind ),*
        }

        impl Display for $kindname {

            fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
                let s = match *self {
                    $( $kindname::$kind => $string ),*
                };
                try!(write!(fmt, "{}", s));
                Ok(())
            }

        }

        #[derive(Debug)]
        pub struct $name {
            err_type: $kindname,
            cause: Option<Box<Error>>,
        }

        impl $name {

            pub fn new(errtype: $kindname, cause: Option<Box<Error>>) -> $name {
                $name {
                    err_type: errtype,
                    cause: cause,
                }
            }

            pub fn err_type(&self) -> $kindname {
                self.err_type
            }

        }

        impl Display for $name {

            fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
                try!(write!(fmt, "[{}]", self.err_type));
                Ok(())
            }

        }

        impl Error for $name {

            fn description(&self) -> &str {
                match self.err_type {
                    $( $kindname::$kind => $string ),*
                }
            }

            fn cause(&self) -> Option<&Error> {
                self.cause.as_ref().map(|e| &**e)
            }

        }

    }
}

#[cfg(test)]
mod test {
    use std::error::Error;
    use std::fmt::Error as FmtError;
    use std::fmt::{Display, Formatter};

    generate_error_types!(TestError, TestErrorKind,
        TestErrorKindA => "testerrorkind a",
        TestErrorKindB => "testerrorkind B");

    #[test]
    fn test_a() {
        let kind = TestErrorKind::TestErrorKindA;
        assert_eq!(String::from("testerrorkind a"), format!("{}", kind));

        let e = TestError::new(kind, None);
        assert_eq!(String::from("[testerrorkind a]"), format!("{}", e));
    }

    #[test]
    fn test_b() {
        let kind = TestErrorKind::TestErrorKindB;
        assert_eq!(String::from("testerrorkind B"), format!("{}", kind));

        let e = TestError::new(kind, None);
        assert_eq!(String::from("[testerrorkind B]"), format!("{}", e));

    }

    #[test]
    fn test_ab() {
        let kinda = TestErrorKind::TestErrorKindA;
        let kindb = TestErrorKind::TestErrorKindB;
        assert_eq!(String::from("testerrorkind a"), format!("{}", kinda));
        assert_eq!(String::from("testerrorkind B"), format!("{}", kindb));

        let e = TestError::new(kinda, Some(Box::new(TestError::new(kindb, None))));
        assert_eq!(String::from("[testerrorkind a]"), format!("{}", e));
        assert_eq!(TestErrorKind::TestErrorKindA, e.err_type());
        assert_eq!(String::from("[testerrorkind B]"), format!("{}", e.cause().unwrap()));
    }
}
<commit_msg>Expand error generating to be able to generate modules<commit_after>#[macro_export]
macro_rules! generate_error_imports {
    () => {
        use std::error::Error;
        use std::fmt::Error as FmtError;
        use std::fmt::{Display, Formatter};
    }
}

#[macro_export]
macro_rules! generate_error_module {
    ( $exprs:item ) => {
        pub mod error {
            generate_error_imports!();
            $exprs
        }
    }
}

#[macro_export]
macro_rules! generate_error_types {
    (
        $name: ident,
        $kindname: ident,
        $($kind:ident => $string:expr),*
    ) => {
        #[derive(Clone, Copy, Debug, PartialEq)]
        pub enum $kindname {
            $( $kind ),*
        }

        impl Display for $kindname {

            fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
                let s = match *self {
                    $( $kindname::$kind => $string ),*
                };
                try!(write!(fmt, "{}", s));
                Ok(())
            }

        }

        #[derive(Debug)]
        pub struct $name {
            err_type: $kindname,
            cause: Option<Box<Error>>,
        }

        impl $name {

            pub fn new(errtype: $kindname, cause: Option<Box<Error>>) -> $name {
                $name {
                    err_type: errtype,
                    cause: cause,
                }
            }

            pub fn err_type(&self) -> $kindname {
                self.err_type
            }

        }

        impl Display for $name {

            fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
                try!(write!(fmt, "[{}]", self.err_type));
                Ok(())
            }

        }

        impl Error for $name {

            fn description(&self) -> &str {
                match self.err_type {
                    $( $kindname::$kind => $string ),*
                }
            }

            fn cause(&self) -> Option<&Error> {
                self.cause.as_ref().map(|e| &**e)
            }

        }

    }
}

#[cfg(test)]
mod test {

    generate_error_module!(
        generate_error_types!(TestError, TestErrorKind,
            TestErrorKindA => "testerrorkind a",
            TestErrorKindB => "testerrorkind B");
    );

    #[test]
    fn test_a() {
        use self::error::{TestError, TestErrorKind};

        let kind = TestErrorKind::TestErrorKindA;
        assert_eq!(String::from("testerrorkind a"), format!("{}", kind));

        let e = TestError::new(kind, None);
        assert_eq!(String::from("[testerrorkind a]"), format!("{}", e));
    }

    #[test]
    fn test_b() {
        use self::error::{TestError, TestErrorKind};

        let kind = TestErrorKind::TestErrorKindB;
        assert_eq!(String::from("testerrorkind B"), format!("{}", kind));

        let e = TestError::new(kind, None);
        assert_eq!(String::from("[testerrorkind B]"), format!("{}", e));

    }

    #[test]
    fn test_ab() {
        use std::error::Error;
        use self::error::{TestError, TestErrorKind};

        let kinda = TestErrorKind::TestErrorKindA;
        let kindb = TestErrorKind::TestErrorKindB;
        assert_eq!(String::from("testerrorkind a"), format!("{}", kinda));
        assert_eq!(String::from("testerrorkind B"), format!("{}", kindb));

        let e = TestError::new(kinda, Some(Box::new(TestError::new(kindb, None))));
        assert_eq!(String::from("[testerrorkind a]"), format!("{}", e));
        assert_eq!(TestErrorKind::TestErrorKindA, e.err_type());
        assert_eq!(String::from("[testerrorkind B]"), format!("{}", e.cause().unwrap()));
    }

    pub mod anothererrormod {
        generate_error_imports!();
        generate_error_types!(TestError, TestErrorKind,
            TestErrorKindA => "testerrorkind a",
            TestErrorKindB => "testerrorkind B");
    }

    #[test]
    fn test_other_a() {
        use self::anothererrormod::{TestError, TestErrorKind};

        let kind = TestErrorKind::TestErrorKindA;
        assert_eq!(String::from("testerrorkind a"), format!("{}", kind));

        let e = TestError::new(kind, None);
        assert_eq!(String::from("[testerrorkind a]"), format!("{}", e));
    }

    #[test]
    fn test_other_b() {
        use self::anothererrormod::{TestError, TestErrorKind};

        let kind = TestErrorKind::TestErrorKindB;
        assert_eq!(String::from("testerrorkind B"), format!("{}", kind));

        let e = TestError::new(kind, None);
        assert_eq!(String::from("[testerrorkind B]"), format!("{}", e));

    }

    #[test]
    fn test_other_ab() {
        use std::error::Error;
        use self::anothererrormod::{TestError, TestErrorKind};

        let kinda = TestErrorKind::TestErrorKindA;
        let kindb = TestErrorKind::TestErrorKindB;
        assert_eq!(String::from("testerrorkind a"), format!("{}", kinda));
        assert_eq!(String::from("testerrorkind B"), format!("{}", kindb));

        let e = TestError::new(kinda, Some(Box::new(TestError::new(kindb, None))));
        assert_eq!(String::from("[testerrorkind a]"), format!("{}", e));
        assert_eq!(TestErrorKind::TestErrorKindA, e.err_type());
        assert_eq!(String::from("[testerrorkind B]"), format!("{}", e.cause().unwrap()));
    }
}
<|endoftext|>


<commit_before>#![feature(std_misc, fs, io, path, core)]

extern crate log;
extern crate time;
extern crate "toml" as toml_parser;

use std::borrow::ToOwned;
use std::cmp;
use std::collections::HashMap;
use std::sync::{Mutex, Arc};
use log::{LogLevel, LogRecord, LogLevelFilter, SetLoggerError};

pub mod toml;
pub mod config;
pub mod appender;
pub mod pattern;

pub trait Append: Send + 'static{
    fn append(&mut self, record: &LogRecord);
}

struct ConfiguredLogger {
    level: LogLevelFilter,
    appenders: Vec<usize>,
    children: Vec<(String, Box<ConfiguredLogger>)>,
}

impl ConfiguredLogger {
    fn add(&mut self, path: &str, mut appenders: Vec<usize>, additive: bool, level: LogLevelFilter) {
        let (part, rest) = match path.find("::") {
            Some(idx) => (&path[..idx], &path[idx+2..]),
            None => (path, ""),
        };

        for &mut (ref child_part, ref mut child) in &mut self.children {
            if &child_part[..] == part {
                child.add(rest, appenders, additive, level);
                return;
            }
        }

        let child = if rest.is_empty() {
            if additive {
                appenders.extend(self.appenders.iter().cloned());
            }

            ConfiguredLogger {
                level: level,
                appenders: appenders,
                children: vec![],
            }
        } else {
            let mut child = ConfiguredLogger {
                level: self.level,
                appenders: self.appenders.clone(),
                children: vec![],
            };
            child.add(rest, appenders, additive, level);
            child
        };

        self.children.push((part.to_owned(), Box::new(child)));
    }

    fn max_log_level(&self) -> LogLevelFilter {
        let mut max = self.level;
        for &(_, ref child) in &self.children {
            max = cmp::max(max, child.max_log_level());
        }
        max
    }

    fn find(&self, path: &str) -> &ConfiguredLogger {
        let mut node = self;

        'parts: for part in path.split("::") {
            for &(ref child_part, ref child) in &node.children {
                if &child_part[..] == part {
                    node = child;
                    continue 'parts;
                }
            }

            break;
        }

        node
    }

    fn enabled(&self, level: LogLevel) -> bool {
        self.level >= level
    }

    fn log(&self, record: &log::LogRecord, appenders: &mut [Box<Append>]) {
        if self.enabled(record.level()) {
            for &idx in &self.appenders {
                appenders[idx].append(record)
            }
        }
    }
}

struct SharedLogger {
    root: ConfiguredLogger,
    appenders: Vec<Box<Append>>,
}

impl SharedLogger {
    fn new(config: config::Config) -> SharedLogger {
        let config::Config { appenders, root, loggers, .. } = config;

        let root = {
            let appender_map = appenders
                .iter()
                .enumerate()
                .map(|(i, appender)| {
                    (&appender.name, i)
                })
                .collect::<HashMap<_, _>>();

            let config::Root { level, appenders, .. } = root;
            let mut root = ConfiguredLogger {
                level: level,
                appenders: appenders
                    .into_iter()
                    .map(|appender| appender_map[appender].clone())
                    .collect(),
                children: vec![],
            };

            for logger in loggers {
                let appenders = logger.appenders
                    .into_iter()
                    .map(|appender| appender_map[appender])
                    .collect();
                root.add(&logger.name, appenders, logger.additive, logger.level);
            }

            root
        };

        let appenders = appenders.into_iter().map(|appender| appender.appender).collect();

        SharedLogger {
            root: root,
            appenders: appenders,
        }
    }
}

struct Logger {
    inner: Arc<Mutex<SharedLogger>>,
}

impl Logger {
    fn new(config: config::Config) -> Logger {
        Logger {
            inner: Arc::new(Mutex::new(SharedLogger::new(config)))
        }
    }

    fn max_log_level(&self) -> LogLevelFilter {
        self.inner.lock().unwrap().root.max_log_level()
    }
}

impl log::Log for Logger {
    fn enabled(&self, level: LogLevel, module: &str) -> bool {
        self.inner.lock().unwrap().root.find(module).enabled(level)
    }

    fn log(&self, record: &log::LogRecord) {
        let shared = &mut *self.inner.lock().unwrap();
        shared.root.find(record.location().module_path).log(record, &mut shared.appenders);
    }
}

pub fn init_config(config: config::Config) -> Result<(), SetLoggerError> {
    log::set_logger(|max_log_level| {
        let logger = Logger::new(config);
        max_log_level.set(logger.max_log_level());
        Box::new(logger)
    })
}

#[cfg(test)]
mod test {
    use log::{LogLevel, LogLevelFilter, Log};

    use super::*;

    #[test]
    fn enabled() {
        let appenders = vec![];
        let root = config::Root::new(LogLevelFilter::Debug);
        let loggers = vec![
            config::Logger::new("foo::bar".to_string(), LogLevelFilter::Trace),
            config::Logger::new("foo::bar::baz".to_string(), LogLevelFilter::Off),
            config::Logger::new("foo::baz::buz".to_string(), LogLevelFilter::Error),
        ];
        let config = config::Config::new(appenders, root, loggers).unwrap();

        let logger = super::Logger::new(config);

        assert!(logger.enabled(LogLevel::Warn, "bar"));
        assert!(!logger.enabled(LogLevel::Trace, "bar"));
        assert!(logger.enabled(LogLevel::Debug, "foo"));
        assert!(logger.enabled(LogLevel::Trace, "foo::bar"));
        assert!(!logger.enabled(LogLevel::Error, "foo::bar::baz"));
        assert!(!logger.enabled(LogLevel::Error, "foo::bar::baz::buz"));
        assert!(!logger.enabled(LogLevel::Warn, "foo::baz::buz"));
        assert!(!logger.enabled(LogLevel::Warn, "foo::baz::buz::bar"));
    }
}
<commit_msg>Slightly better tests<commit_after>#![feature(std_misc, fs, io, path, core)]

extern crate log;
extern crate time;
extern crate "toml" as toml_parser;

use std::borrow::ToOwned;
use std::cmp;
use std::collections::HashMap;
use std::sync::{Mutex, Arc};
use log::{LogLevel, LogRecord, LogLevelFilter, SetLoggerError};

pub mod toml;
pub mod config;
pub mod appender;
pub mod pattern;

pub trait Append: Send + 'static{
    fn append(&mut self, record: &LogRecord);
}

struct ConfiguredLogger {
    level: LogLevelFilter,
    appenders: Vec<usize>,
    children: Vec<(String, Box<ConfiguredLogger>)>,
}

impl ConfiguredLogger {
    fn add(&mut self, path: &str, mut appenders: Vec<usize>, additive: bool, level: LogLevelFilter) {
        let (part, rest) = match path.find("::") {
            Some(idx) => (&path[..idx], &path[idx+2..]),
            None => (path, ""),
        };

        for &mut (ref child_part, ref mut child) in &mut self.children {
            if &child_part[..] == part {
                child.add(rest, appenders, additive, level);
                return;
            }
        }

        let child = if rest.is_empty() {
            if additive {
                appenders.extend(self.appenders.iter().cloned());
            }

            ConfiguredLogger {
                level: level,
                appenders: appenders,
                children: vec![],
            }
        } else {
            let mut child = ConfiguredLogger {
                level: self.level,
                appenders: self.appenders.clone(),
                children: vec![],
            };
            child.add(rest, appenders, additive, level);
            child
        };

        self.children.push((part.to_owned(), Box::new(child)));
    }

    fn max_log_level(&self) -> LogLevelFilter {
        let mut max = self.level;
        for &(_, ref child) in &self.children {
            max = cmp::max(max, child.max_log_level());
        }
        max
    }

    fn find(&self, path: &str) -> &ConfiguredLogger {
        let mut node = self;

        'parts: for part in path.split("::") {
            for &(ref child_part, ref child) in &node.children {
                if &child_part[..] == part {
                    node = child;
                    continue 'parts;
                }
            }

            break;
        }

        node
    }

    fn enabled(&self, level: LogLevel) -> bool {
        self.level >= level
    }

    fn log(&self, record: &log::LogRecord, appenders: &mut [Box<Append>]) {
        if self.enabled(record.level()) {
            for &idx in &self.appenders {
                appenders[idx].append(record)
            }
        }
    }
}

struct SharedLogger {
    root: ConfiguredLogger,
    appenders: Vec<Box<Append>>,
}

impl SharedLogger {
    fn new(config: config::Config) -> SharedLogger {
        let config::Config { appenders, root, loggers, .. } = config;

        let root = {
            let appender_map = appenders
                .iter()
                .enumerate()
                .map(|(i, appender)| {
                    (&appender.name, i)
                })
                .collect::<HashMap<_, _>>();

            let config::Root { level, appenders, .. } = root;
            let mut root = ConfiguredLogger {
                level: level,
                appenders: appenders
                    .into_iter()
                    .map(|appender| appender_map[appender].clone())
                    .collect(),
                children: vec![],
            };

            for logger in loggers {
                let appenders = logger.appenders
                    .into_iter()
                    .map(|appender| appender_map[appender])
                    .collect();
                root.add(&logger.name, appenders, logger.additive, logger.level);
            }

            root
        };

        let appenders = appenders.into_iter().map(|appender| appender.appender).collect();

        SharedLogger {
            root: root,
            appenders: appenders,
        }
    }
}

struct Logger {
    inner: Arc<Mutex<SharedLogger>>,
}

impl Logger {
    fn new(config: config::Config) -> Logger {
        Logger {
            inner: Arc::new(Mutex::new(SharedLogger::new(config)))
        }
    }

    fn max_log_level(&self) -> LogLevelFilter {
        self.inner.lock().unwrap().root.max_log_level()
    }
}

impl log::Log for Logger {
    fn enabled(&self, level: LogLevel, module: &str) -> bool {
        self.inner.lock().unwrap().root.find(module).enabled(level)
    }

    fn log(&self, record: &log::LogRecord) {
        let shared = &mut *self.inner.lock().unwrap();
        shared.root.find(record.location().module_path).log(record, &mut shared.appenders);
    }
}

pub fn init_config(config: config::Config) -> Result<(), SetLoggerError> {
    log::set_logger(|max_log_level| {
        let logger = Logger::new(config);
        max_log_level.set(logger.max_log_level());
        Box::new(logger)
    })
}

#[cfg(test)]
mod test {
    use log::{LogLevel, LogLevelFilter, Log};

    use super::*;

    #[test]
    fn enabled() {
        let appenders = vec![];
        let root = config::Root::new(LogLevelFilter::Debug);
        let loggers = vec![
            config::Logger::new("foo::bar".to_string(), LogLevelFilter::Trace),
            config::Logger::new("foo::bar::baz".to_string(), LogLevelFilter::Off),
            config::Logger::new("foo::baz::buz".to_string(), LogLevelFilter::Error),
        ];
        let config = config::Config::new(appenders, root, loggers).unwrap();

        let logger = super::Logger::new(config);

        assert!(logger.enabled(LogLevel::Warn, "bar"));
        assert!(!logger.enabled(LogLevel::Trace, "bar"));
        assert!(logger.enabled(LogLevel::Debug, "foo"));
        assert!(logger.enabled(LogLevel::Trace, "foo::bar"));
        assert!(!logger.enabled(LogLevel::Error, "foo::bar::baz"));
        assert!(logger.enabled(LogLevel::Debug, "foo::bar::bazbuz"));
        assert!(!logger.enabled(LogLevel::Error, "foo::bar::baz::buz"));
        assert!(!logger.enabled(LogLevel::Warn, "foo::baz::buz"));
        assert!(!logger.enabled(LogLevel::Warn, "foo::baz::buz::bar"));
        assert!(logger.enabled(LogLevel::Error, "foo::baz::buz::bar"));
    }
}
<|endoftext|>