<commit_before>use crate::{
    db::Database,
    model::{
        AnnoLichess, GameInfo, GameInfoPlayer, Mode, PersonalEntry, PersonalKeyBuilder,
        PersonalStatus, UserId, UserName,
    },
    util::NevermindExt as _,
};
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Color, Outcome,
    Position,
};
use std::{
    sync::Arc,
    time::{Duration, SystemTime},
};
use tokio::{
    sync::{
        mpsc::{self, error::SendTimeoutError},
        oneshot,
    },
    task::JoinHandle,
};

mod lila;

use lila::{Game, Lila};

#[derive(Clap)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
}

#[derive(Clone)]
pub struct IndexerStub {
    tx: mpsc::Sender<IndexerMessage>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, JoinHandle<()>) {
        let (tx, rx) = mpsc::channel(1000);
        (
            IndexerStub { tx },
            tokio::spawn(
                IndexerActor {
                    rx,
                    db,
                    lila: Lila::new(opt),
                }
                .run(),
            ),
        )
    }

    pub async fn index_player(&self, player: UserName) -> oneshot::Receiver<()> {
        let (req, res) = oneshot::channel();

        match self
            .tx
            .send_timeout(
                IndexerMessage::IndexPlayer {
                    player,
                    callback: req,
                },
                Duration::from_secs(10),
            )
            .await
        {
            Ok(_) => (),
            Err(SendTimeoutError::Timeout(_)) => {
                log::error!("indexer queue full for more than 10 seconds")
            }
            Err(SendTimeoutError::Closed(_)) => panic!("indexer died"),
        };

        res
    }
}

struct IndexerActor {
    rx: mpsc::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(mut self) {
        while let Some(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer { callback, player } => {
                    self.index_player(player).await;
                    callback.send(()).nevermind("requester gone away");
                }
            }
        }
    }

    async fn index_player(&self, player: UserName) {
        let player_id = UserId::from(player.clone());
        let mut status = self
            .db
            .queryable()
            .get_player_status(&player_id)
            .expect("get player status")
            .unwrap_or_default();

        let since_created_at = match status
            .maybe_revisit_ongoing()
            .or_else(|| status.maybe_index())
        {
            Some(since) => since,
            None => {
                log::debug!("not reindexing {} so soon", player);
                return;
            }
        };

        log::info!(
            "starting to index {} (created_at >= {})",
            player,
            since_created_at
        );
        let mut games = match self.lila.user_games(&player, since_created_at).await {
            Ok(games) => games,
            /* TODO: Err(err) if err.status() == Some(StatusCode::NOT_FOUND) => {
                return;
            }, */
            Err(err) => {
                log::error!("indexer request failed: {}", err);
                return;
            }
        };

        let hash = ByColor::new_with(|color| PersonalKeyBuilder::with_user_pov(&player_id, color));
        let mut num_games = 0;
        while let Some(game) = games.next().await {
            let game = match game {
                Ok(game) => game,
                Err(err) => {
                    log::error!("{}", err);
                    continue;
                }
            };

            self.index_game(&player, &hash, game, &mut status);

            num_games += 1;
            if num_games % 1024 == 0 {
                log::info!("indexed {} games for {}", num_games, player);
            }
        }

        status.indexed_at = SystemTime::now();
        self.db
            .queryable()
            .put_player_status(&player_id, status)
            .expect("put player status");
        log::info!("finished indexing {} games for {}", num_games, player);
    }

    fn index_game(
        &self,
        player: &UserName,
        hash: &ByColor<PersonalKeyBuilder>,
        game: Game,
        status: &mut PersonalStatus,
    ) {
        status.latest_created_at = game.created_at;

        if game.status.is_ongoing() {
            if status.revisit_ongoing_created_at.is_none() {
                log::debug!("will revisit ongoing game {} eventually", game.id);
                status.revisit_ongoing_created_at = Some(game.created_at);
            }
            return;
        }

        if game.status.is_unindexable() {
            return;
        }

        let color = if Some(player) == game.user_name(Color::White) {
            Color::White
        } else if Some(player) == game.user_name(Color::Black) {
            Color::Black
        } else {
            return;
        };
        let year = AnnoLichess::from_time(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);

        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "{}/{} already indexed",
                game.id,
                color.fold("white", "black")
            );
            return;
        }
        queryable
            .merge_game_info(
                game.id,
                GameInfo {
                    winner: outcome.winner(),
                    speed: game.speed,
                    rated: game.rated,
                    year: year.year(),
                    players: game.players.map(|p| GameInfoPlayer {
                        name: p.user.map(|p| p.name.to_string()),
                        rating: p.rating,
                    }),
                    indexed: ByColor::new_with(|c| color == c),
                },
            )
            .expect("put game info");

        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("not indexing {}: {}", game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::warn!("cutting off {} at ply {}: {}: {}", game.id, ply, err, san);
                    break;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        for (zobrist, uci) in table {
            queryable
                .merge_personal(
                    hash.by_color(color)
                        .with_zobrist(variant, zobrist)
                        .with_year(year),
                    PersonalEntry::new_single(
                        uci.clone(),
                        game.speed,
                        Mode::from_rated(game.rated),
                        game.id,
                        outcome,
                    ),
                )
                .expect("merge personal");
        }
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserName,
        callback: oneshot::Sender<()>,
    },
}

pub enum IndexerStatus {
    Ongoing,
    Completed,
    Failed,
}
<commit_msg>downgrade 404 to warning<commit_after>use crate::{
    db::Database,
    model::{
        AnnoLichess, GameInfo, GameInfoPlayer, Mode, PersonalEntry, PersonalKeyBuilder,
        PersonalStatus, UserId, UserName,
    },
    util::NevermindExt as _,
};
use axum::http::StatusCode;
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Color, Outcome,
    Position,
};
use std::{
    sync::Arc,
    time::{Duration, SystemTime},
};
use tokio::{
    sync::{
        mpsc::{self, error::SendTimeoutError},
        oneshot,
    },
    task::JoinHandle,
};

mod lila;

use lila::{Game, Lila};

#[derive(Clap)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
}

#[derive(Clone)]
pub struct IndexerStub {
    tx: mpsc::Sender<IndexerMessage>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, JoinHandle<()>) {
        let (tx, rx) = mpsc::channel(1000);
        (
            IndexerStub { tx },
            tokio::spawn(
                IndexerActor {
                    rx,
                    db,
                    lila: Lila::new(opt),
                }
                .run(),
            ),
        )
    }

    pub async fn index_player(&self, player: UserName) -> oneshot::Receiver<()> {
        let (req, res) = oneshot::channel();

        match self
            .tx
            .send_timeout(
                IndexerMessage::IndexPlayer {
                    player,
                    callback: req,
                },
                Duration::from_secs(10),
            )
            .await
        {
            Ok(_) => (),
            Err(SendTimeoutError::Timeout(_)) => {
                log::error!("indexer queue full for more than 10 seconds")
            }
            Err(SendTimeoutError::Closed(_)) => panic!("indexer died"),
        };

        res
    }
}

struct IndexerActor {
    rx: mpsc::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(mut self) {
        while let Some(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer { callback, player } => {
                    self.index_player(player).await;
                    callback.send(()).nevermind("requester gone away");
                }
            }
        }
    }

    async fn index_player(&self, player: UserName) {
        let player_id = UserId::from(player.clone());
        let mut status = self
            .db
            .queryable()
            .get_player_status(&player_id)
            .expect("get player status")
            .unwrap_or_default();

        let since_created_at = match status
            .maybe_revisit_ongoing()
            .or_else(|| status.maybe_index())
        {
            Some(since) => since,
            None => {
                log::debug!("not reindexing {} so soon", player);
                return;
            }
        };

        log::info!(
            "starting to index {} (created_at >= {})",
            player,
            since_created_at
        );
        let mut games = match self.lila.user_games(&player, since_created_at).await {
            Ok(games) => games,
            Err(err) if err.status() == Some(StatusCode::NOT_FOUND) => {
                log::warn!("indexer did not find player {}", player);
                return;
            }
            Err(err) => {
                log::error!("indexer request failed: {}", err);
                return;
            }
        };

        let hash = ByColor::new_with(|color| PersonalKeyBuilder::with_user_pov(&player_id, color));
        let mut num_games = 0;
        while let Some(game) = games.next().await {
            let game = match game {
                Ok(game) => game,
                Err(err) => {
                    log::error!("{}", err);
                    continue;
                }
            };

            self.index_game(&player, &hash, game, &mut status);

            num_games += 1;
            if num_games % 1024 == 0 {
                log::info!("indexed {} games for {}", num_games, player);
            }
        }

        status.indexed_at = SystemTime::now();
        self.db
            .queryable()
            .put_player_status(&player_id, status)
            .expect("put player status");
        log::info!("finished indexing {} games for {}", num_games, player);
    }

    fn index_game(
        &self,
        player: &UserName,
        hash: &ByColor<PersonalKeyBuilder>,
        game: Game,
        status: &mut PersonalStatus,
    ) {
        status.latest_created_at = game.created_at;

        if game.status.is_ongoing() {
            if status.revisit_ongoing_created_at.is_none() {
                log::debug!("will revisit ongoing game {} eventually", game.id);
                status.revisit_ongoing_created_at = Some(game.created_at);
            }
            return;
        }

        if game.status.is_unindexable() {
            return;
        }

        let color = if Some(player) == game.user_name(Color::White) {
            Color::White
        } else if Some(player) == game.user_name(Color::Black) {
            Color::Black
        } else {
            return;
        };
        let year = AnnoLichess::from_time(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);

        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "{}/{} already indexed",
                game.id,
                color.fold("white", "black")
            );
            return;
        }
        queryable
            .merge_game_info(
                game.id,
                GameInfo {
                    winner: outcome.winner(),
                    speed: game.speed,
                    rated: game.rated,
                    year: year.year(),
                    players: game.players.map(|p| GameInfoPlayer {
                        name: p.user.map(|p| p.name.to_string()),
                        rating: p.rating,
                    }),
                    indexed: ByColor::new_with(|c| color == c),
                },
            )
            .expect("put game info");

        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("not indexing {}: {}", game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::warn!("cutting off {} at ply {}: {}: {}", game.id, ply, err, san);
                    break;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        for (zobrist, uci) in table {
            queryable
                .merge_personal(
                    hash.by_color(color)
                        .with_zobrist(variant, zobrist)
                        .with_year(year),
                    PersonalEntry::new_single(
                        uci.clone(),
                        game.speed,
                        Mode::from_rated(game.rated),
                        game.id,
                        outcome,
                    ),
                )
                .expect("merge personal");
        }
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserName,
        callback: oneshot::Sender<()>,
    },
}

pub enum IndexerStatus {
    Ongoing,
    Completed,
    Failed,
}
<|endoftext|>


<commit_before>use std::{
    collections::{hash_map::Entry, HashMap},
    sync::Arc,
    time::{Duration, Instant, SystemTime},
};

use async_channel::TrySendError;
use axum::http::StatusCode;
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Outcome, Position,
};
use tokio::{
    sync::{watch, RwLock},
    task::JoinHandle,
    time::{sleep, timeout},
};

use crate::{
    db::Database,
    model::{
        GameInfo, GameInfoPlayer, Mode, Month, PersonalEntry, PersonalKeyBuilder, PersonalStatus,
        UserId,
    },
};

mod lila;

use lila::{Game, Lila};

const MAX_PLIES: usize = 50;

#[derive(Clap, Clone)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
    #[clap(long = "bearer")]
    bearer: Option<String>,
    #[clap(long = "indexers", default_value = "16")]
    indexers: usize,
}

#[derive(Clone)]
pub struct IndexerStub {
    db: Arc<Database>,
    indexing: Arc<RwLock<HashMap<UserId, watch::Sender<()>>>>,
    tx: async_channel::Sender<IndexerMessage>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, Vec<JoinHandle<()>>) {
        let indexing = Arc::new(RwLock::new(HashMap::new()));

        let (tx, rx) = async_channel::bounded(1000);
        let mut join_handles = Vec::with_capacity(opt.indexers);
        for idx in 0..opt.indexers {
            join_handles.push(tokio::spawn(
                IndexerActor {
                    idx,
                    rx: rx.clone(),
                    indexing: Arc::clone(&indexing),
                    db: Arc::clone(&db),
                    lila: Lila::new(opt.clone()),
                }
                .run(),
            ));
        }

        (IndexerStub { db, indexing, tx }, join_handles)
    }

    pub async fn num_indexing(&self) -> usize {
        let guard = self.indexing.read().await;
        guard.len()
    }

    pub async fn index_player(&self, player: &UserId) -> Option<watch::Receiver<()>> {
        // Optimization: First try subscribing to an existing indexing run,
        // without acquiring a write lock.
        {
            let guard = self.indexing.read().await;
            if let Some(sender) = guard.get(player) {
                return Some(sender.subscribe());
            }
        }

        // Check player indexing status.
        let mut status = self
            .db
            .queryable()
            .get_player_status(player)
            .expect("get player status")
            .unwrap_or_default();

        let since_created_at = match status
            .maybe_revisit_ongoing()
            .or_else(|| status.maybe_index())
        {
            Some(since) => since,
            None => return None, // Do not reindex so soon!
        };

        // Queue indexing request.
        let mut guard = self.indexing.write().await;
        let entry = match guard.entry(player.to_owned()) {
            Entry::Occupied(entry) => return Some(entry.get().subscribe()),
            Entry::Vacant(entry) => entry,
        };

        match self.tx.try_send(IndexerMessage::IndexPlayer {
            player: player.to_owned(),
            status,
            since_created_at,
        }) {
            Ok(_) => {
                let (sender, receiver) = watch::channel(());
                entry.insert(sender);
                Some(receiver)
            }
            Err(TrySendError::Full(_)) => {
                log::error!(
                    "not queuing {} because indexer queue is full",
                    player.as_str()
                );
                None
            }
            Err(TrySendError::Closed(_)) => panic!("all indexers died"),
        }
    }
}

struct IndexerActor {
    idx: usize,
    indexing: Arc<RwLock<HashMap<UserId, watch::Sender<()>>>>,
    rx: async_channel::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(self) {
        while let Ok(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer {
                    player,
                    status,
                    since_created_at,
                } => {
                    self.index_player(&player, status, since_created_at).await;

                    let mut guard = self.indexing.write().await;
                    guard.remove(&player);
                }
            }
        }
    }

    async fn index_player(
        &self,
        player: &UserId,
        mut status: PersonalStatus,
        since_created_at: u64,
    ) {
        let started_at = Instant::now();
        log::info!(
            "indexer {:02}: starting {} (created_at >= {})",
            self.idx,
            player.as_str(),
            since_created_at
        );

        let mut games = match timeout(
            Duration::from_secs(60),
            self.lila.user_games(player, since_created_at),
        )
        .await
        {
            Ok(Ok(games)) => games,
            Ok(Err(err)) if err.status() == Some(StatusCode::NOT_FOUND) => {
                log::warn!(
                    "indexer {:02}: did not find player {}",
                    self.idx,
                    player.as_str()
                );
                return;
            }
            Ok(Err(err)) => {
                log::error!("indexer {:02}: request failed: {}", self.idx, err);
                sleep(Duration::from_secs(5)).await;
                return;
            }
            Err(timed_out) => {
                log::error!("indexer {:02}: request to lila: {}", self.idx, timed_out);
                return;
            }
        };

        let hash = ByColor::new_with(|color| PersonalKeyBuilder::with_user_pov(player, color));

        let mut num_games = 0;
        loop {
            let game = match timeout(Duration::from_secs(60), games.next()).await {
                Ok(Some(Ok(game))) => game,
                Ok(Some(Err(err))) => {
                    log::error!("indexer {:02}: {}", self.idx, err);
                    continue;
                }
                Ok(None) => break,
                Err(timed_out) => {
                    log::error!("indexer {:02}: stream from lila: {}", self.idx, timed_out);
                    return;
                }
            };

            self.index_game(player, &hash, game, &mut status);

            num_games += 1;
            if num_games % 1024 == 0 {
                self.db
                    .queryable()
                    .put_player_status(player, &status)
                    .expect("put player status");

                log::info!(
                    "indexer {:02}: indexed {} games for {} ...",
                    self.idx,
                    num_games,
                    player.as_str()
                );
            }
        }

        status.indexed_at = SystemTime::now();
        self.db
            .queryable()
            .put_player_status(player, &status)
            .expect("put player status");

        let elapsed = started_at.elapsed();
        log::info!(
            "indexer {:02}: finished {} games for {} in {:.3?} ({:.3?}/game, {:.1} games/s)",
            self.idx,
            num_games,
            player.as_str(),
            elapsed,
            (elapsed.as_nanos() as u64)
                .checked_div(num_games)
                .map(Duration::from_nanos)
                .unwrap_or_default(),
            (num_games as f64) / elapsed.as_secs_f64()
        );
    }

    fn index_game(
        &self,
        player: &UserId,
        hash: &ByColor<PersonalKeyBuilder>,
        game: Game,
        status: &mut PersonalStatus,
    ) {
        status.latest_created_at = game.created_at;

        if game.status.is_ongoing() {
            if status.revisit_ongoing_created_at.is_none() {
                log::debug!(
                    "indexer {:02}: will revisit ongoing game {} eventually",
                    self.idx,
                    game.id
                );
                status.revisit_ongoing_created_at = Some(game.created_at);
            }
            return;
        }

        if game.status.is_unindexable() {
            return;
        }

        if game.players.any(|p| p.user.is_none()) {
            return;
        }

        let color = match game
            .players
            .find(|p| p.user.as_ref().map_or(false, |user| user.name == *player))
        {
            Some(color) => color,
            None => {
                log::error!(
                    "indexer {:02}: {} did not play in {}",
                    self.idx,
                    player.as_str(),
                    game.id
                );
                return;
            }
        };

        // Skip game if already indexed from this side. This cannot race with
        // writes, because all writes for the same player are sequenced by
        // this actor. So making a transaction is not required.
        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "indexer {:02}: {}/{} already indexed",
                self.idx,
                game.id,
                color
            );
            return;
        }

        // Prepare basic information and setup initial position.
        let month = Month::from_time_saturating(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);
        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };
        let opponent_rating = match game.players.by_color(!color).rating {
            Some(rating) => rating,
            None => {
                log::warn!(
                    "indexer {:02}: skipping {} without opponent rating",
                    self.idx,
                    game.id
                );
                return;
            }
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("indexer {:02}: not indexing {}: {}", self.idx, game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            if ply >= MAX_PLIES {
                break;
            }

            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::warn!(
                        "indexer {:02}: cutting off {} at ply {}: {}: {}",
                        self.idx,
                        game.id,
                        ply,
                        err,
                        san
                    );
                    break;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        // Write to database. All writes regarding this game are batched and
        // atomically committed, so the database will always be in a consistent
        // state.
        let mut batch = queryable.batch();

        batch.merge_game_info(
            game.id,
            GameInfo {
                winner: outcome.winner(),
                speed: game.speed,
                mode: Mode::from_rated(game.rated),
                month,
                players: game.players.map(|p| GameInfoPlayer {
                    name: p.user.map(|p| p.name.to_string()),
                    rating: p.rating,
                }),
                indexed: ByColor::new_with(|c| color == c),
            },
        );

        for (zobrist, uci) in table {
            batch.merge_personal(
                hash.by_color(color)
                    .with_zobrist(variant, zobrist)
                    .with_month(month),
                PersonalEntry::new_single(
                    uci.clone(),
                    game.speed,
                    Mode::from_rated(game.rated),
                    game.id,
                    outcome,
                    opponent_rating,
                ),
            );
        }

        batch.write().expect("atomically commit game and moves");
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserId,
        status: PersonalStatus,
        since_created_at: u64,
    },
}
<commit_msg>lower maximum queue size<commit_after>use std::{
    collections::{hash_map::Entry, HashMap},
    sync::Arc,
    time::{Duration, Instant, SystemTime},
};

use async_channel::TrySendError;
use axum::http::StatusCode;
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Outcome, Position,
};
use tokio::{
    sync::{watch, RwLock},
    task::JoinHandle,
    time::{sleep, timeout},
};

use crate::{
    db::Database,
    model::{
        GameInfo, GameInfoPlayer, Mode, Month, PersonalEntry, PersonalKeyBuilder, PersonalStatus,
        UserId,
    },
};

mod lila;

use lila::{Game, Lila};

const MAX_PLIES: usize = 50;

#[derive(Clap, Clone)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
    #[clap(long = "bearer")]
    bearer: Option<String>,
    #[clap(long = "indexers", default_value = "16")]
    indexers: usize,
}

#[derive(Clone)]
pub struct IndexerStub {
    db: Arc<Database>,
    indexing: Arc<RwLock<HashMap<UserId, watch::Sender<()>>>>,
    tx: async_channel::Sender<IndexerMessage>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, Vec<JoinHandle<()>>) {
        let indexing = Arc::new(RwLock::new(HashMap::new()));

        let (tx, rx) = async_channel::bounded(opt.indexers * 10);
        let mut join_handles = Vec::with_capacity(opt.indexers);
        for idx in 0..opt.indexers {
            join_handles.push(tokio::spawn(
                IndexerActor {
                    idx,
                    rx: rx.clone(),
                    indexing: Arc::clone(&indexing),
                    db: Arc::clone(&db),
                    lila: Lila::new(opt.clone()),
                }
                .run(),
            ));
        }

        (IndexerStub { db, indexing, tx }, join_handles)
    }

    pub async fn num_indexing(&self) -> usize {
        let guard = self.indexing.read().await;
        guard.len()
    }

    pub async fn index_player(&self, player: &UserId) -> Option<watch::Receiver<()>> {
        // Optimization: First try subscribing to an existing indexing run,
        // without acquiring a write lock.
        {
            let guard = self.indexing.read().await;
            if let Some(sender) = guard.get(player) {
                return Some(sender.subscribe());
            }
        }

        // Check player indexing status.
        let mut status = self
            .db
            .queryable()
            .get_player_status(player)
            .expect("get player status")
            .unwrap_or_default();

        let since_created_at = match status
            .maybe_revisit_ongoing()
            .or_else(|| status.maybe_index())
        {
            Some(since) => since,
            None => return None, // Do not reindex so soon!
        };

        // Queue indexing request.
        let mut guard = self.indexing.write().await;
        let entry = match guard.entry(player.to_owned()) {
            Entry::Occupied(entry) => return Some(entry.get().subscribe()),
            Entry::Vacant(entry) => entry,
        };

        match self.tx.try_send(IndexerMessage::IndexPlayer {
            player: player.to_owned(),
            status,
            since_created_at,
        }) {
            Ok(_) => {
                let (sender, receiver) = watch::channel(());
                entry.insert(sender);
                Some(receiver)
            }
            Err(TrySendError::Full(_)) => {
                log::error!(
                    "not queuing {} because indexer queue is full",
                    player.as_str()
                );
                None
            }
            Err(TrySendError::Closed(_)) => panic!("all indexers died"),
        }
    }
}

struct IndexerActor {
    idx: usize,
    indexing: Arc<RwLock<HashMap<UserId, watch::Sender<()>>>>,
    rx: async_channel::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(self) {
        while let Ok(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer {
                    player,
                    status,
                    since_created_at,
                } => {
                    self.index_player(&player, status, since_created_at).await;

                    let mut guard = self.indexing.write().await;
                    guard.remove(&player);
                }
            }
        }
    }

    async fn index_player(
        &self,
        player: &UserId,
        mut status: PersonalStatus,
        since_created_at: u64,
    ) {
        let started_at = Instant::now();
        log::info!(
            "indexer {:02}: starting {} (created_at >= {})",
            self.idx,
            player.as_str(),
            since_created_at
        );

        let mut games = match timeout(
            Duration::from_secs(60),
            self.lila.user_games(player, since_created_at),
        )
        .await
        {
            Ok(Ok(games)) => games,
            Ok(Err(err)) if err.status() == Some(StatusCode::NOT_FOUND) => {
                log::warn!(
                    "indexer {:02}: did not find player {}",
                    self.idx,
                    player.as_str()
                );
                return;
            }
            Ok(Err(err)) => {
                log::error!("indexer {:02}: request failed: {}", self.idx, err);
                sleep(Duration::from_secs(5)).await;
                return;
            }
            Err(timed_out) => {
                log::error!("indexer {:02}: request to lila: {}", self.idx, timed_out);
                return;
            }
        };

        let hash = ByColor::new_with(|color| PersonalKeyBuilder::with_user_pov(player, color));

        let mut num_games = 0;
        loop {
            let game = match timeout(Duration::from_secs(60), games.next()).await {
                Ok(Some(Ok(game))) => game,
                Ok(Some(Err(err))) => {
                    log::error!("indexer {:02}: {}", self.idx, err);
                    continue;
                }
                Ok(None) => break,
                Err(timed_out) => {
                    log::error!("indexer {:02}: stream from lila: {}", self.idx, timed_out);
                    return;
                }
            };

            self.index_game(player, &hash, game, &mut status);

            num_games += 1;
            if num_games % 1024 == 0 {
                self.db
                    .queryable()
                    .put_player_status(player, &status)
                    .expect("put player status");

                log::info!(
                    "indexer {:02}: indexed {} games for {} ...",
                    self.idx,
                    num_games,
                    player.as_str()
                );
            }
        }

        status.indexed_at = SystemTime::now();
        self.db
            .queryable()
            .put_player_status(player, &status)
            .expect("put player status");

        let elapsed = started_at.elapsed();
        log::info!(
            "indexer {:02}: finished {} games for {} in {:.3?} ({:.3?}/game, {:.1} games/s)",
            self.idx,
            num_games,
            player.as_str(),
            elapsed,
            (elapsed.as_nanos() as u64)
                .checked_div(num_games)
                .map(Duration::from_nanos)
                .unwrap_or_default(),
            (num_games as f64) / elapsed.as_secs_f64()
        );
    }

    fn index_game(
        &self,
        player: &UserId,
        hash: &ByColor<PersonalKeyBuilder>,
        game: Game,
        status: &mut PersonalStatus,
    ) {
        status.latest_created_at = game.created_at;

        if game.status.is_ongoing() {
            if status.revisit_ongoing_created_at.is_none() {
                log::debug!(
                    "indexer {:02}: will revisit ongoing game {} eventually",
                    self.idx,
                    game.id
                );
                status.revisit_ongoing_created_at = Some(game.created_at);
            }
            return;
        }

        if game.status.is_unindexable() {
            return;
        }

        if game.players.any(|p| p.user.is_none()) {
            return;
        }

        let color = match game
            .players
            .find(|p| p.user.as_ref().map_or(false, |user| user.name == *player))
        {
            Some(color) => color,
            None => {
                log::error!(
                    "indexer {:02}: {} did not play in {}",
                    self.idx,
                    player.as_str(),
                    game.id
                );
                return;
            }
        };

        // Skip game if already indexed from this side. This cannot race with
        // writes, because all writes for the same player are sequenced by
        // this actor. So making a transaction is not required.
        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "indexer {:02}: {}/{} already indexed",
                self.idx,
                game.id,
                color
            );
            return;
        }

        // Prepare basic information and setup initial position.
        let month = Month::from_time_saturating(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);
        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };
        let opponent_rating = match game.players.by_color(!color).rating {
            Some(rating) => rating,
            None => {
                log::warn!(
                    "indexer {:02}: skipping {} without opponent rating",
                    self.idx,
                    game.id
                );
                return;
            }
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("indexer {:02}: not indexing {}: {}", self.idx, game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            if ply >= MAX_PLIES {
                break;
            }

            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::warn!(
                        "indexer {:02}: cutting off {} at ply {}: {}: {}",
                        self.idx,
                        game.id,
                        ply,
                        err,
                        san
                    );
                    break;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        // Write to database. All writes regarding this game are batched and
        // atomically committed, so the database will always be in a consistent
        // state.
        let mut batch = queryable.batch();

        batch.merge_game_info(
            game.id,
            GameInfo {
                winner: outcome.winner(),
                speed: game.speed,
                mode: Mode::from_rated(game.rated),
                month,
                players: game.players.map(|p| GameInfoPlayer {
                    name: p.user.map(|p| p.name.to_string()),
                    rating: p.rating,
                }),
                indexed: ByColor::new_with(|c| color == c),
            },
        );

        for (zobrist, uci) in table {
            batch.merge_personal(
                hash.by_color(color)
                    .with_zobrist(variant, zobrist)
                    .with_month(month),
                PersonalEntry::new_single(
                    uci.clone(),
                    game.speed,
                    Mode::from_rated(game.rated),
                    game.id,
                    outcome,
                    opponent_rating,
                ),
            );
        }

        batch.write().expect("atomically commit game and moves");
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserId,
        status: PersonalStatus,
        since_created_at: u64,
    },
}
<|endoftext|>


<commit_before>extern crate rustbox;

use std::comm::{Receiver, Sender};
use std::char;
use std::io::{fs, File, FileMode, FileAccess, TempDir};
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicBool};

use super::Response;
use input::Input;
use cursor::Direction;
use keyboard::Key;
use view::View;

enum EventStatus {
    Handled(Response),
    NotHandled,
}

pub struct Editor<'e> {
    pub running: Arc<AtomicBool>,
    pub sender: Sender<rustbox::Event>,

    events: Receiver<rustbox::Event>,
    view: View<'e>,
}

impl<'e> Editor<'e> {
    pub fn new(source: Input) -> Editor<'e> {
        let view = View::new(source);

        let (send, recv) = channel();
        Editor {
            sender: send,
            events: recv,
            view: view,
            running: Arc::new(AtomicBool::new(false)),
        }
    }

    pub fn handle_key_event(&mut self, key: u16, ch: u32) -> Response {
        let key = match key {
            0 => char::from_u32(ch).map(|c| Key::Char(c)),
            a => Key::from_special_code(a),
        };

        match self.handle_system_event(key) {
            EventStatus::Handled(response) => { response }
            EventStatus::NotHandled        => { Response::Continue }
        }
    }

    pub fn save_active_buffer(&mut self) {
        let lines = &self.view.buffer.lines;
        let path = self.view.buffer.file_path.as_ref().unwrap();

        let tmpdir = match TempDir::new_in(&Path::new("."), "iota") {
            Ok(d) => d,
            Err(e) => panic!("file error: {}", e)
        };

        let tmppath = tmpdir.path().join(Path::new("tmpfile"));

        let mut file = match File::open_mode(&tmppath, FileMode::Open, FileAccess::Write) {
            Ok(f) => f,
            Err(e) => panic!("file error: {}", e)
        };

        for line in lines.iter() {
            let mut data = line.data.clone();
            data.push('\n');
            let result = file.write(data.as_bytes());

            if result.is_err() {
                // TODO(greg): figure out what to do here.
                panic!("Something went wrong while writing the file");
            }
        }

        if let Err(e) = fs::rename(&tmppath, path) {
            panic!("file error: {}", e);
        }
    }

    pub fn draw(&mut self) {
        self.view.draw();
        self.view.draw_status();
        self.view.draw_cursor();
    }

    pub fn start(&mut self) {
        // Synchronizes with transfer across thread boundary
        self.running.store(true, Ordering::Relaxed);
        self.event_loop();
        self.main_loop();
    }

    fn main_loop(&mut self) {
        while self.running.load(Ordering::Relaxed) {
            self.view.clear();
            self.draw();
            rustbox::present();
            if let rustbox::Event::KeyEvent(_, key, ch) = self.events.recv() {
                if let Response::Quit = self.handle_key_event(key, ch) {
                    // Okay if it doesn't quit immediately.
                    self.running.store(false, Ordering::Relaxed);
                }
            }
        }
    }

    fn event_loop(&self) {
        // clone the sender so that we can use it in the proc
        let sender = self.sender.clone();
        let running = self.running.clone();

        spawn(move || {
            while running.load(Ordering::Relaxed) {
                if sender.send_opt(rustbox::peek_event(1000)).is_err() {
                    running.store(false, Ordering::Relaxed);
                }
            }
        });
    }

    fn handle_system_event(&mut self, k: Option<Key>) -> EventStatus {
        let key = match k {
            Some(k) => k,
            None => return EventStatus::NotHandled
        };

        match key {
            Key::Up        => { self.view.move_cursor(Direction::Up); }
            Key::Down      => { self.view.move_cursor(Direction::Down); }
            Key::Left      => { self.view.move_cursor(Direction::Left); }
            Key::Right     => { self.view.move_cursor(Direction::Right); }
            Key::Enter     => { self.view.insert_line(); }

            // Tab inserts 4 spaces, rather than a \t
            Key::Tab       => { self.view.insert_tab(); }

            Key::Backspace => { self.view.delete_char(Direction::Left); }
            Key::Delete    => { self.view.delete_char(Direction::Right); }
            Key::CtrlS     => { self.save_active_buffer(); }
            Key::CtrlQ     => { return EventStatus::Handled(Response::Quit) }
            Key::CtrlR     => { self.view.resize(); }

            Key::Char(c)   => { self.view.insert_char(c) }

            // default
            _              => { return EventStatus::NotHandled }
        }
        // event is handled and we want to keep the editor running
        EventStatus::Handled(Response::Continue)
    }

}
<commit_msg>Don't panic when saving buffer with no file_path<commit_after>extern crate rustbox;

use std::comm::{Receiver, Sender};
use std::char;
use std::io::{fs, File, FileMode, FileAccess, TempDir};
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicBool};

use super::Response;
use input::Input;
use cursor::Direction;
use keyboard::Key;
use view::View;

enum EventStatus {
    Handled(Response),
    NotHandled,
}

pub struct Editor<'e> {
    pub running: Arc<AtomicBool>,
    pub sender: Sender<rustbox::Event>,

    events: Receiver<rustbox::Event>,
    view: View<'e>,
}

impl<'e> Editor<'e> {
    pub fn new(source: Input) -> Editor<'e> {
        let view = View::new(source);

        let (send, recv) = channel();
        Editor {
            sender: send,
            events: recv,
            view: view,
            running: Arc::new(AtomicBool::new(false)),
        }
    }

    pub fn handle_key_event(&mut self, key: u16, ch: u32) -> Response {
        let key = match key {
            0 => char::from_u32(ch).map(|c| Key::Char(c)),
            a => Key::from_special_code(a),
        };

        match self.handle_system_event(key) {
            EventStatus::Handled(response) => { response }
            EventStatus::NotHandled        => { Response::Continue }
        }
    }

    pub fn save_active_buffer(&mut self) {
        let lines = &self.view.buffer.lines;
        let path = match self.view.buffer.file_path {
            Some(ref p) => p.clone(),
            None => {
                // TODO: prompt user for file name here
                Path::new("untitled")
            },
        };

        let tmpdir = match TempDir::new_in(&Path::new("."), "iota") {
            Ok(d) => d,
            Err(e) => panic!("file error: {}", e)
        };

        let tmppath = tmpdir.path().join(Path::new("tmpfile"));

        let mut file = match File::open_mode(&tmppath, FileMode::Open, FileAccess::Write) {
            Ok(f) => f,
            Err(e) => panic!("file error: {}", e)
        };

        for line in lines.iter() {
            let mut data = line.data.clone();
            data.push('\n');
            let result = file.write(data.as_bytes());

            if result.is_err() {
                // TODO(greg): figure out what to do here.
                panic!("Something went wrong while writing the file");
            }
        }

        if let Err(e) = fs::rename(&tmppath, &path) {
            panic!("file error: {}", e);
        }
    }

    pub fn draw(&mut self) {
        self.view.draw();
        self.view.draw_status();
        self.view.draw_cursor();
    }

    pub fn start(&mut self) {
        // Synchronizes with transfer across thread boundary
        self.running.store(true, Ordering::Relaxed);
        self.event_loop();
        self.main_loop();
    }

    fn main_loop(&mut self) {
        while self.running.load(Ordering::Relaxed) {
            self.view.clear();
            self.draw();
            rustbox::present();
            if let rustbox::Event::KeyEvent(_, key, ch) = self.events.recv() {
                if let Response::Quit = self.handle_key_event(key, ch) {
                    // Okay if it doesn't quit immediately.
                    self.running.store(false, Ordering::Relaxed);
                }
            }
        }
    }

    fn event_loop(&self) {
        // clone the sender so that we can use it in the proc
        let sender = self.sender.clone();
        let running = self.running.clone();

        spawn(move || {
            while running.load(Ordering::Relaxed) {
                if sender.send_opt(rustbox::peek_event(1000)).is_err() {
                    running.store(false, Ordering::Relaxed);
                }
            }
        });
    }

    fn handle_system_event(&mut self, k: Option<Key>) -> EventStatus {
        let key = match k {
            Some(k) => k,
            None => return EventStatus::NotHandled
        };

        match key {
            Key::Up        => { self.view.move_cursor(Direction::Up); }
            Key::Down      => { self.view.move_cursor(Direction::Down); }
            Key::Left      => { self.view.move_cursor(Direction::Left); }
            Key::Right     => { self.view.move_cursor(Direction::Right); }
            Key::Enter     => { self.view.insert_line(); }

            // Tab inserts 4 spaces, rather than a \t
            Key::Tab       => { self.view.insert_tab(); }

            Key::Backspace => { self.view.delete_char(Direction::Left); }
            Key::Delete    => { self.view.delete_char(Direction::Right); }
            Key::CtrlS     => { self.save_active_buffer(); }
            Key::CtrlQ     => { return EventStatus::Handled(Response::Quit) }
            Key::CtrlR     => { self.view.resize(); }

            Key::Char(c)   => { self.view.insert_char(c) }

            // default
            _              => { return EventStatus::NotHandled }
        }
        // event is handled and we want to keep the editor running
        EventStatus::Handled(Response::Continue)
    }

}
<|endoftext|>


<commit_before>use std::fmt::{Write, Error as FmtError};

/// Simple pretty printer hand tailored for the output generated by the `quote`
/// crate for schema inference.
///
/// # Rules
///
/// 1. Seeing `{` increases indentation level
/// 2. Seeing `}` decreases indentation level
/// 3. Insert newline after `{`, `}`, `,`, and `;`
/// 4. Don't put spaces:
///   - between ident and `!`,
///   - between path segments and `::`
///   - after `(`, '<' and before `)`, `>`
///   - before `,`
pub fn format_schema(schema: &str) -> Result<String, FmtError> {
    let mut out = String::with_capacity(schema.len());
    let mut indent = String::new();
    let mut skip_space = false;
    let mut last_char = ' ';
    let mut inside_parenthesis = false;

    for c in schema.chars() {
        // The `quote!` macro inserts whitespaces at some strange location,
        // let's remove them!
        match c {
            '!' | ',' | '<' | ')' | '>' if last_char.is_whitespace() => {
                out.pop();
            }
            ':' if last_char.is_whitespace() => {
                // Unless we are at the beginning of a fully qualified path,
                // remove the whitespace.
                let char_before_whitespace = {
                    let mut chars = out.chars();
                    chars.next_back();
                    chars.next_back()
                };

                if char_before_whitespace != Some('>') {
                    out.pop();
                }
            }
            _ => {}
        }

        if skip_space && c.is_whitespace() && last_char != '>' {
            continue;
        }

        last_char = c;
        skip_space = false;

        // At this point, there is an empty line before `}`. We need to remove
        // the already inserted indent, because the new indent is smaller than
        // the old one.
        if c == '}' {
            while let Some(c) = out.pop() {
                if c == '\n' {
                    break;
                }
            }

            indent.pop();
            write!(out, "\n{}", indent)?;
        }

        // Keep track of our parenthesis level
        match c {
            '(' => inside_parenthesis = true,
            ')' => inside_parenthesis = false,
             _ => {}
        }

        write!(out, "{}", c)?;

        // We need to insert newlines in some places and adjust the indent.
        // Also, we need to remember if we could skip the next whitespace.
        match c {
            ',' => {
                if !inside_parenthesis {
                    skip_space = true;
                    write!(out, "\n{}", indent)?;
                }
            },
            '}' => {
                skip_space = true;
                write!(out, "\n{}", indent)?;
            }
            '{' => {
                skip_space = true;
                indent += "\t";
                write!(out, "\n{}", indent)?;
            }
            ':' | '(' | '<' => skip_space = true,
            _ => {}
        }
    }

    Ok(out.replace("\t", "    ").replace("table!", "\ntable!").trim().to_string())
}

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

    macro_rules! test_pretty_printing {
        ($($name:ident: $input:expr => $expected:expr);*) => {
            $(
                #[test]
                fn $name() {
                    let actual = format_schema($input).unwrap();
                    assert_eq!($expected, actual);
                }
            )*
        }
    }

    test_pretty_printing! {
        test_increase_indent:
            "{,}" =>
            "{\n    ,\n}";

        test_decrease_indent:
            "{abc,}" =>
            "{\n    abc,\n}";

        test_newline_after_comma:
            ",," =>
            ",\n,";

        test_remove_whitespace_macro_call:
            "table ! { }" =>
            "table! {\n}";

        test_remove_whitespace_path_segments:
            ":: diesel :: types :: Text" =>
            "::diesel::types::Text";

        test_remove_whitespace_parenthesis:
            "foo ( 42 )" =>
            "foo (42)";

        test_remove_whitespace_angular_brackets:
            "Option < i32 >" =>
            "Option<i32>";

        test_remove_whitespace_before_comma:
            "id -> Int4 , username -> Varchar" =>
            "id -> Int4,\nusername -> Varchar";

        test_format_nullable:
            "Nullable < :: diesel :: types :: Text >" =>
            "Nullable<::diesel::types::Text>";

        test_format_nullable_multispace:
            "Nullable <  Integer >" =>
            "Nullable<Integer>";

        test_format_arrow:
            "created_at -> :: diesel :: types :: Timestamp" =>
            "created_at -> ::diesel::types::Timestamp";

        test_format_full_line:
            "created_at -> :: diesel :: types :: Timestamp ,," =>
            "created_at -> ::diesel::types::Timestamp,\n,";

        test_format_generated_mod:
            "table ! { users ( id ) { id -> :: diesel :: types :: Int4 , \
            username -> :: diesel :: types :: Varchar , password -> :: diesel :: types :: Varchar \
            , } }" =>
r"table! {
    users (id) {
        id -> ::diesel::types::Int4,
        username -> ::diesel::types::Varchar,
        password -> ::diesel::types::Varchar,
    }
}";
        test_no_newline_after_comma_inside_parenthetis:
            "(a, b)" =>
            "(a, b)"
    }
}
<commit_msg>Remove unused pretty printing code<commit_after><|endoftext|>


<commit_before>use super::{Lexer, LexerResult, LexerError};

#[test]
fn test_read_nil() {
    let sym_name = "nil";
    let t_sym = t_symbol!(sym_name, span!(1, 1, 1, 4));
    let mut lexer = Lexer::new(sym_name.chars());
    assert_eq!(Some(Ok(t_sym)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_integer_as_float() {
    let mut lexer = Lexer::new("64".chars());
    let t_num = t_number!(64_f64, span!(1, 1, 1, 3));
    assert_eq!(Some(Ok(t_num)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_float() {
    let mut lexer = Lexer::new("64.5".chars());
    let t_num = t_number!(64.5, span!(1, 1, 1, 5));
    assert_eq!(Some(Ok(t_num)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_string() {
    let s = "rust is beautiful";
    let actual_input = format!(r#""{}""#, s);
    let mut lexer = Lexer::new(actual_input.chars());
    let t_str = t_string!(s, span!(1, 1, 1, 20));
    assert_eq!(Some(Ok(t_str)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_symbol() {
    let sym_name = "my-symbol";
    let mut lexer = Lexer::new(sym_name.chars());
    let t_sym = t_symbol!(sym_name, span!(1, 1, 1, 10));
    assert_eq!(Some(Ok(t_sym)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_incorrect_symbol_starting_with_digit() {
    let sym_name = "6-my-incorrect-symbol";
    let mut lexer = Lexer::new(sym_name.chars());
    let expected_result = Some(Err(LexerError::new(1, 2)));
    assert_eq!(expected_result, lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_explicitly_positive_number() {
    let mut lexer = Lexer::new("+1".chars());
    let t_num = t_number!(1_f64, span!(1, 1, 1, 3));
    let expected_result = Some(Ok(t_num));
    assert_eq!(expected_result, lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_explicitly_negative_number() {
    let mut lexer = Lexer::new("-1".chars());
    let t_num = t_number!(-1_f64, span!(1, 1, 1, 3));
    let expected_result = Some(Ok(t_num));
    assert_eq!(expected_result, lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_dense_expression() {
    let lexer = Lexer::new("(def a 1)".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("def", span!(1, 2, 1, 5))),
                               Ok(t_symbol!("a", span!(1, 6, 1, 7))),
                               Ok(t_number!(1_f64, span!(1, 8, 1, 9))),
                               Ok(t_list_end!(span!(1, 9, 1, 10)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_sparse_expression() {
    let lexer = Lexer::new(" ( \n def a\n1)   \n".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 2, 1, 3))),
                               Ok(t_symbol!("def", span!(2, 3, 2, 6))),
                               Ok(t_symbol!("a", span!(2, 7, 3, 1))),
                               Ok(t_number!(1_f64, span!(3, 2, 3, 3))),
                               Ok(t_list_end!(span!(3, 3, 3, 4)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_nested_list_expressions() {
    let lexer = Lexer::new("(def a (+ 1 1))".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("def", span!(1, 2, 1, 5))),
                               Ok(t_symbol!("a", span!(1, 6, 1, 7))),
                               Ok(t_list_start!(span!(1, 8, 1, 9))),
                               Ok(t_symbol!("+", span!(1, 9, 1, 10))),
                               Ok(t_number!(1_f64, span!(1, 11, 1, 12))),
                               Ok(t_number!(1_f64, span!(1, 13, 1, 14))),
                               Ok(t_list_end!(span!(1, 14, 1, 15))),
                               Ok(t_list_end!(span!(1, 15, 1, 16)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_vec_of_expressions() {
    let lexer = Lexer::new("[a 1 (1 [3]) +]".chars());
    let expected_result = vec![Ok(t_vec_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("a", span!(1, 2, 1,3))),
                               Ok(t_number!(1., span!(1, 4, 1, 5))),
                               Ok(t_list_start!(span!(1, 6, 1, 7))),
                               Ok(t_number!(1., span!(1, 7, 1, 8))),
                               Ok(t_vec_start!(span!(1, 9, 1, 10))),
                               Ok(t_number!(3., span!(1, 10, 1, 11))),
                               Ok(t_vec_end!(span!(1, 11, 1, 12))),
                               Ok(t_list_end!(span!(1, 12, 1, 13))),
                               Ok(t_symbol!("+", span!(1, 14, 1, 15))),
                               Ok(t_vec_end!(span!(1, 15, 1, 16)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_list_of_symbols() {
    let lexer = Lexer::new("(+ - / * % > < = a b c z A X Y Z)".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("+", span!(1, 2, 1, 3))),
                               Ok(t_symbol!("-", span!(1, 4, 1, 5))),
                               Ok(t_symbol!("/", span!(1, 6, 1, 7))),
                               Ok(t_symbol!("*", span!(1, 8, 1, 9))),
                               Ok(t_symbol!("%", span!(1, 10, 1, 11))),
                               Ok(t_symbol!(">", span!(1, 12, 1, 13))),
                               Ok(t_symbol!("<", span!(1, 14, 1, 15))),
                               Ok(t_symbol!("=", span!(1, 16, 1, 17))),
                               Ok(t_symbol!("a", span!(1, 18, 1, 19))),
                               Ok(t_symbol!("b", span!(1, 20, 1, 21))),
                               Ok(t_symbol!("c", span!(1, 22, 1, 23))),
                               Ok(t_symbol!("z", span!(1, 24, 1, 25))),
                               Ok(t_symbol!("A", span!(1, 26, 1, 27))),
                               Ok(t_symbol!("X", span!(1, 28, 1, 29))),
                               Ok(t_symbol!("Y", span!(1, 30, 1, 31))),
                               Ok(t_symbol!("Z", span!(1, 32, 1, 33))),
                               Ok(t_list_end!(span!(1, 33, 1, 34)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_symbol() {
    let lexer = Lexer::new("'a".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_symbol!["a", span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_number() {
    let lexer = Lexer::new("'1".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_number![1., span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_list() {
    let lexer = Lexer::new("'()".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_list_start![span![1, 2, 1, 3]]),
                               Ok(t_list_end![span![1, 3, 1, 4]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_symbol_inside_list() {
    let lexer = Lexer::new("(+ 1 'a)".chars());
    let expected_result = vec![Ok(t_list_start![span![1, 1, 1, 2]]),
                               Ok(t_symbol!["+", span![1, 2, 1, 3]]),
                               Ok(t_number![1., span![1, 4, 1, 5]]),
                               Ok(t_quote![span![1, 6, 1, 7]]),
                               Ok(t_symbol!["a", span![1, 7, 1, 8]]),
                               Ok(t_list_end![span![1, 8, 1, 9]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_symbol() {
    let lexer = Lexer::new("~a".chars());
    let expected_result = vec![Ok(t_unquote![span![1, 1, 1, 2]]),
                               Ok(t_symbol!["a", span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_number() {
    let lexer = Lexer::new("~1".chars());
    let expected_result = vec![Ok(t_unquote![span![1, 1, 1, 2]]),
                               Ok(t_number![1., span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_list() {
    let lexer = Lexer::new("~()".chars());
    let expected_result = vec![Ok(t_unquote![span![1, 1, 1, 2]]),
                               Ok(t_list_start![span![1, 2, 1, 3]]),
                               Ok(t_list_end![span![1, 3, 1, 4]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_symbol_inside_list() {
    let lexer = Lexer::new("'(+ 1 ~a)".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_list_start![span![1, 2, 1, 3]]),
                               Ok(t_symbol!["+", span![1, 3, 1, 4]]),
                               Ok(t_number![1., span![1, 5, 1, 6]]),
                               Ok(t_unquote![span![1, 7, 1, 8]]),
                               Ok(t_symbol!["a", span![1, 8, 1, 9]]),
                               Ok(t_list_end![span![1, 9, 1, 10]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_splicing_list() {
    let lexer = Lexer::new("~@()".chars());
    let expected_result = vec![Ok(t_unquote_splicing![span![1, 1, 1, 3]]),
                               Ok(t_list_start![span![1, 3, 1, 4]]),
                               Ok(t_list_end![span![1, 4, 1, 5]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}
<commit_msg>Extend test case for whitespaces and comments<commit_after>use super::{Lexer, LexerResult, LexerError};

#[test]
fn test_read_nil() {
    let sym_name = "nil";
    let t_sym = t_symbol!(sym_name, span!(1, 1, 1, 4));
    let mut lexer = Lexer::new(sym_name.chars());
    assert_eq!(Some(Ok(t_sym)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_integer_as_float() {
    let mut lexer = Lexer::new("64".chars());
    let t_num = t_number!(64_f64, span!(1, 1, 1, 3));
    assert_eq!(Some(Ok(t_num)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_float() {
    let mut lexer = Lexer::new("64.5".chars());
    let t_num = t_number!(64.5, span!(1, 1, 1, 5));
    assert_eq!(Some(Ok(t_num)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_string() {
    let s = "rust is beautiful";
    let actual_input = format!(r#""{}""#, s);
    let mut lexer = Lexer::new(actual_input.chars());
    let t_str = t_string!(s, span!(1, 1, 1, 20));
    assert_eq!(Some(Ok(t_str)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_symbol() {
    let sym_name = "my-symbol";
    let mut lexer = Lexer::new(sym_name.chars());
    let t_sym = t_symbol!(sym_name, span!(1, 1, 1, 10));
    assert_eq!(Some(Ok(t_sym)), lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_incorrect_symbol_starting_with_digit() {
    let sym_name = "6-my-incorrect-symbol";
    let mut lexer = Lexer::new(sym_name.chars());
    let expected_result = Some(Err(LexerError::new(1, 2)));
    assert_eq!(expected_result, lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_explicitly_positive_number() {
    let mut lexer = Lexer::new("+1".chars());
    let t_num = t_number!(1_f64, span!(1, 1, 1, 3));
    let expected_result = Some(Ok(t_num));
    assert_eq!(expected_result, lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_explicitly_negative_number() {
    let mut lexer = Lexer::new("-1".chars());
    let t_num = t_number!(-1_f64, span!(1, 1, 1, 3));
    let expected_result = Some(Ok(t_num));
    assert_eq!(expected_result, lexer.next());
    assert_eq!(None, lexer.next());
}

#[test]
fn test_read_dense_expression() {
    let lexer = Lexer::new("(def a 1)".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("def", span!(1, 2, 1, 5))),
                               Ok(t_symbol!("a", span!(1, 6, 1, 7))),
                               Ok(t_number!(1_f64, span!(1, 8, 1, 9))),
                               Ok(t_list_end!(span!(1, 9, 1, 10)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_sparse_expression_with_comments() {
    let lexer = Lexer::new(" ( ;; comments\n \
                            def a ;; comments \n\
                            1) ;; comments \n\
                            ;; comments   \n".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 2, 1, 3))),
                               Ok(t_symbol!("def", span!(2, 3, 2, 6))),
                               Ok(t_symbol!("a", span!(2, 7, 2, 8))),
                               Ok(t_number!(1_f64, span!(3, 2, 3, 3))),
                               Ok(t_list_end!(span!(3, 3, 3, 4)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_nested_list_expressions() {
    let lexer = Lexer::new("(def a (+ 1 1))".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("def", span!(1, 2, 1, 5))),
                               Ok(t_symbol!("a", span!(1, 6, 1, 7))),
                               Ok(t_list_start!(span!(1, 8, 1, 9))),
                               Ok(t_symbol!("+", span!(1, 9, 1, 10))),
                               Ok(t_number!(1_f64, span!(1, 11, 1, 12))),
                               Ok(t_number!(1_f64, span!(1, 13, 1, 14))),
                               Ok(t_list_end!(span!(1, 14, 1, 15))),
                               Ok(t_list_end!(span!(1, 15, 1, 16)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_vec_of_expressions() {
    let lexer = Lexer::new("[a 1 (1 [3]) +]".chars());
    let expected_result = vec![Ok(t_vec_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("a", span!(1, 2, 1,3))),
                               Ok(t_number!(1., span!(1, 4, 1, 5))),
                               Ok(t_list_start!(span!(1, 6, 1, 7))),
                               Ok(t_number!(1., span!(1, 7, 1, 8))),
                               Ok(t_vec_start!(span!(1, 9, 1, 10))),
                               Ok(t_number!(3., span!(1, 10, 1, 11))),
                               Ok(t_vec_end!(span!(1, 11, 1, 12))),
                               Ok(t_list_end!(span!(1, 12, 1, 13))),
                               Ok(t_symbol!("+", span!(1, 14, 1, 15))),
                               Ok(t_vec_end!(span!(1, 15, 1, 16)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_list_of_symbols() {
    let lexer = Lexer::new("(+ - / * % > < = a b c z A X Y Z)".chars());
    let expected_result = vec![Ok(t_list_start!(span!(1, 1, 1, 2))),
                               Ok(t_symbol!("+", span!(1, 2, 1, 3))),
                               Ok(t_symbol!("-", span!(1, 4, 1, 5))),
                               Ok(t_symbol!("/", span!(1, 6, 1, 7))),
                               Ok(t_symbol!("*", span!(1, 8, 1, 9))),
                               Ok(t_symbol!("%", span!(1, 10, 1, 11))),
                               Ok(t_symbol!(">", span!(1, 12, 1, 13))),
                               Ok(t_symbol!("<", span!(1, 14, 1, 15))),
                               Ok(t_symbol!("=", span!(1, 16, 1, 17))),
                               Ok(t_symbol!("a", span!(1, 18, 1, 19))),
                               Ok(t_symbol!("b", span!(1, 20, 1, 21))),
                               Ok(t_symbol!("c", span!(1, 22, 1, 23))),
                               Ok(t_symbol!("z", span!(1, 24, 1, 25))),
                               Ok(t_symbol!("A", span!(1, 26, 1, 27))),
                               Ok(t_symbol!("X", span!(1, 28, 1, 29))),
                               Ok(t_symbol!("Y", span!(1, 30, 1, 31))),
                               Ok(t_symbol!("Z", span!(1, 32, 1, 33))),
                               Ok(t_list_end!(span!(1, 33, 1, 34)))];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_symbol() {
    let lexer = Lexer::new("'a".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_symbol!["a", span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_number() {
    let lexer = Lexer::new("'1".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_number![1., span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_list() {
    let lexer = Lexer::new("'()".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_list_start![span![1, 2, 1, 3]]),
                               Ok(t_list_end![span![1, 3, 1, 4]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_quoted_symbol_inside_list() {
    let lexer = Lexer::new("(+ 1 'a)".chars());
    let expected_result = vec![Ok(t_list_start![span![1, 1, 1, 2]]),
                               Ok(t_symbol!["+", span![1, 2, 1, 3]]),
                               Ok(t_number![1., span![1, 4, 1, 5]]),
                               Ok(t_quote![span![1, 6, 1, 7]]),
                               Ok(t_symbol!["a", span![1, 7, 1, 8]]),
                               Ok(t_list_end![span![1, 8, 1, 9]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_symbol() {
    let lexer = Lexer::new("~a".chars());
    let expected_result = vec![Ok(t_unquote![span![1, 1, 1, 2]]),
                               Ok(t_symbol!["a", span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_number() {
    let lexer = Lexer::new("~1".chars());
    let expected_result = vec![Ok(t_unquote![span![1, 1, 1, 2]]),
                               Ok(t_number![1., span![1, 2, 1, 3]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_list() {
    let lexer = Lexer::new("~()".chars());
    let expected_result = vec![Ok(t_unquote![span![1, 1, 1, 2]]),
                               Ok(t_list_start![span![1, 2, 1, 3]]),
                               Ok(t_list_end![span![1, 3, 1, 4]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_symbol_inside_list() {
    let lexer = Lexer::new("'(+ 1 ~a)".chars());
    let expected_result = vec![Ok(t_quote![span![1, 1, 1, 2]]),
                               Ok(t_list_start![span![1, 2, 1, 3]]),
                               Ok(t_symbol!["+", span![1, 3, 1, 4]]),
                               Ok(t_number![1., span![1, 5, 1, 6]]),
                               Ok(t_unquote![span![1, 7, 1, 8]]),
                               Ok(t_symbol!["a", span![1, 8, 1, 9]]),
                               Ok(t_list_end![span![1, 9, 1, 10]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}

#[test]
fn test_read_unquoted_splicing_list() {
    let lexer = Lexer::new("~@()".chars());
    let expected_result = vec![Ok(t_unquote_splicing![span![1, 1, 1, 3]]),
                               Ok(t_list_start![span![1, 3, 1, 4]]),
                               Ok(t_list_end![span![1, 4, 1, 5]])];
    assert_eq!(expected_result, lexer.collect::<Vec<LexerResult>>());
}
<|endoftext|>


<commit_before>use {Field, Borrowed, Value, Matrix2d, RoundRectangle};
use vecmath::{margin_round_rectangle,
relative_round_rectangle, translate, rotate_radians, scale, shear, multiply};
use {Transform2d};
use {RelativeRectangle};

/// A round rectangle context.
pub struct RoundRectangleContext<'a> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current round rectangle.
    pub round_rect: Field<'a, RoundRectangle>,
}

impl<'a> Transform2d<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn trans(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(&trans, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn trans_local(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(self.transform.get(), &trans))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(&rot, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad_local(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(self.transform.get(), &rot))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(&scale, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(self.transform.get(), &scale))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(&shear, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(self.transform.get(), &shear))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

impl<'a> RelativeRectangle<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn margin(&'a self, m: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(margin_round_rectangle(self.round_rect.get(), m)),
        }
    }

    #[inline(always)]
    fn rel(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(relative_round_rectangle(self.round_rect.get(), x, y)),
        }
    }
}

<commit_msg>Implemented `AddColor` for `RoundRectangleContext`<commit_after>use {Field, Borrowed, Value, Matrix2d, RoundRectangle};
use vecmath::{margin_round_rectangle,
relative_round_rectangle, translate, rotate_radians, scale, shear, multiply};
use {Transform2d};
use {RelativeRectangle};
use {RoundRectangleColorContext};
use {AddColor};

/// A round rectangle context.
pub struct RoundRectangleContext<'a> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current round rectangle.
    pub round_rect: Field<'a, RoundRectangle>,
}

impl<'a> Transform2d<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn trans(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(&trans, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn trans_local(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let trans = translate(x, y);
                Value(multiply(self.transform.get(), &trans))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(&rot, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn rot_rad_local(&'a self, angle: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let rot = rotate_radians(angle);
                Value(multiply(self.transform.get(), &rot))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(&scale, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn scale_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let scale = scale(sx, sy);
                Value(multiply(self.transform.get(), &scale))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(&shear, self.transform.get()))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }

    #[inline(always)]
    fn shear_local(&'a self, sx: f64, sy: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: {
                let shear = shear(sx, sy);
                Value(multiply(self.transform.get(), &shear))
            },
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

impl<'a> RelativeRectangle<'a> for RoundRectangleContext<'a> {
    #[inline(always)]
    fn margin(&'a self, m: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(margin_round_rectangle(self.round_rect.get(), m)),
        }
    }

    #[inline(always)]
    fn rel(&'a self, x: f64, y: f64) -> RoundRectangleContext<'a> {
        RoundRectangleContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            round_rect: Value(relative_round_rectangle(self.round_rect.get(), x, y)),
        }
    }
}

impl<'a> AddColor<'a, RoundRectangleColorContext<'a>> for RoundRectangleContext<'a> {
    /// Creates a RectangleColorContext.
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> RoundRectangleColorContext<'a> {
        RoundRectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value([r, g, b, a]),
            round_rect: Borrowed(self.round_rect.get()),
        }
    }
}

<|endoftext|>


<commit_before>use hlua::any::AnyLuaValue;
use hlua::any::AnyLuaValue::*;

use std::ops::Deref;
use std::iter::Iterator;

pub trait ToTable {
    fn to_table(&self) -> AnyLuaValue;
}

pub trait FromTable {
    fn from_table(decoder: &mut LuaDecoder) -> Self;
}

macro_rules! numeric_impl {
    ($($typ:ty), +) => {
        $(impl ToTable for $typ {
            fn to_table(&self) -> AnyLuaValue {
                LuaNumber(*self as f64)
            }
        })+
    }
}

numeric_impl!(usize, isize);
numeric_impl!(i8, i16, i32);
numeric_impl!(u8, u16, u32);
numeric_impl!(f32, f64);

impl ToTable for bool {
    fn to_table(&self) -> AnyLuaValue {
        LuaBoolean(*self)
    }
}

impl ToTable for String {
    fn to_table(&self) -> AnyLuaValue {
        LuaString((*self).clone())
    }
}

impl<T: ToTable> ToTable for Option<T> {
    fn to_table(&self) -> AnyLuaValue {
        match self {
            &Some(ref val) => val.to_table(),
            &None => LuaNil
        }
    }
}

impl ToTable for () {
    fn to_table(&self) -> AnyLuaValue {
        LuaNil
    }
}

/// Errors a converter can run into
enum ConverterError {
    /// The type of value present did not match the one expected
    UnexpectedType(AnyLuaValue),
    /// The table index expected did not exist
    MissingTableIndex(String),
    /// The table index present was not valid
    InvalidTableIndex(AnyLuaValue)
}

pub type ConvertResult<T> = Result<T, ConverterError>;

/// Can decode values with a FromTable
pub struct LuaDecoder {
    val: AnyLuaValue
}

impl LuaDecoder {
    pub fn get_u32(&self) -> ConvertResult<u32> {
        unimplemented!()
    }

    pub fn get_i32(&self) -> ConvertResult<i32> {
        unimplemented!()
    }

    pub fn get_f64(&self) -> ConvertResult<i32> {
        unimplemented!()
    }

    pub fn get_string(&self) -> ConvertResult<String> {
        unimplemented!()
    }

    pub fn read_field<T, F>(&self, func: F) -> ConvertResult<T> {
        unimplemented!()
    }
}

/*

$name =>

struct $name { ($field : $type, )+ }

( ($meta )+ (struct $name { ($field : $type, )+ }); )+

 */
<commit_msg>Change to_table to require self<commit_after>use hlua::any::AnyLuaValue;
use hlua::any::AnyLuaValue::*;

use std::ops::Deref;
use std::iter::Iterator;

/// Represents types which can be serialized into a Lua table (AnyLuaValue).
///
/// If you wish to continue using the object after serialization, consider
/// using `impl<a> ToTable for &'a T`, which will take an `&T` as its `self`.
pub trait ToTable {
    /// Write this value into an AnyLuaValue
    fn to_table(self) -> AnyLuaValue;
}

/// Represents types which can be serialized from a Lua table (AnyLuaValue).
///
/// For convenience, this method takes in a `LuaDecoder` (obtained from
/// `LuaDecoder::new(AnyLuaValue)`). See methods on `LuaDecoder`.
pub trait FromTable {
    /// Attempt to parse this value from the decoder.
    fn from_table(decoder: &mut LuaDecoder) -> ConvertResult<Self>
        where Self: Sized;
}

// Implementation for standard numeric types
macro_rules! numeric_impl {
    ($($typ:ty), +) => {
        $(impl ToTable for $typ {
            fn to_table(self) -> AnyLuaValue {
                LuaNumber(self as f64)
            }
        })+
    }
}

numeric_impl!(usize, isize);
numeric_impl!(i8, i16, i32);
numeric_impl!(u8, u16, u32);
numeric_impl!(f32, f64);

// Implementation for &Ts which use Copy syntax
impl<'a, T> ToTable for &'a T where T: Copy {
    fn to_table(self) -> AnyLuaValue {
        self.clone().to_table()
    }
}

impl ToTable for bool {
    fn to_table(self) -> AnyLuaValue {
        LuaBoolean(self)
    }
}

impl ToTable for String {
    fn to_table(self) -> AnyLuaValue {
        LuaString(self)
    }
}

impl<T: ToTable> ToTable for Option<T> {
    fn to_table(self) -> AnyLuaValue {
        match self {
            Some(val) => val.to_table(),
            None => LuaNil
        }
    }
}

impl ToTable for () {
    fn to_table(self) -> AnyLuaValue {
        LuaNil
    }
}

/// Errors a converter can run into
pub enum ConverterError {
    /// The type of value present did not match the one expected
    UnexpectedType(AnyLuaValue),
    /// The table index expected did not exist
    MissingTableIndex(String),
    /// The table index present was not valid
    InvalidTableIndex(AnyLuaValue)
}

pub type ConvertResult<T> = Result<T, ConverterError>;

/// Can decode values with a FromTable
pub struct LuaDecoder {
    val: AnyLuaValue
}

impl LuaDecoder {
    pub fn new(val: AnyLuaValue) -> LuaDecoder {
        LuaDecoder { val: val }
    }

    pub fn get_u32(&self) -> ConvertResult<u32> {
        unimplemented!()
    }

    pub fn get_i32(&self) -> ConvertResult<i32> {
        unimplemented!()
    }

    pub fn get_f64(&self) -> ConvertResult<i32> {
        unimplemented!()
    }

    pub fn get_string(&self) -> ConvertResult<String> {
        unimplemented!()
    }

    pub fn read_field<T, F>(&self, func: F) -> ConvertResult<T> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_nproc"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate num_cpus;

#[cfg(not(windows))]
extern crate libc;

#[macro_use]
extern crate uucore;

use std::io::Write;
use std::env;

#[cfg(target_os = "linux")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 83;
#[cfg(target_os = "macos")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = libc::_SC_NPROCESSORS_CONF;
#[cfg(target_os = "freebsd")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 57;
#[cfg(target_os = "netbsd")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 1001;

static NAME: &'static str = "nproc";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optflag("", "all", "print the number of cores available to the system");
    opts.optopt("", "ignore", "ignore up to N cores", "N");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(err) => {
            show_error!("{}", err);
            return 1;
        }
    };

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
  {0} [OPTIONS]...

Print the number of cores available to the current process.", NAME, VERSION);

        print!("{}", opts.usage(&msg));
        return 0;
    }

    let mut ignore = match matches.opt_str("ignore") {
        Some(numstr) => match numstr.parse() {
            Ok(num) => num,
            Err(e) => {
                show_error!("\"{}\" is not a valid number: {}", numstr, e);
                return 1;
            }
        },
        None => 0
    };

    if !matches.opt_present("all") {
        ignore += match env::var("OMP_NUM_THREADS") {
            Ok(threadstr) => match threadstr.parse() {
                Ok(num) => num,
                Err(_)=> 0
            },
            Err(_) => 0
        };
    }

    let mut cores = if matches.opt_present("all") {
        if cfg!(target_os = "linux") || cfg!(target_os = "macos") || cfg!(target_os = "freebsd") || cfg!(target_os = "netbsd") {
            let nprocs = unsafe { libc::sysconf(_SC_NPROCESSORS_CONF) };
            if nprocs == 1 {
                // In some situation, /proc and /sys are not mounted, and sysconf returns 1.
                // However, we want to guarantee that `nproc --all` >= `nproc`.
                num_cpus::get()
            } else {
                if nprocs > 0 { nprocs as usize } else { 1 }
            }
        } else {
            num_cpus::get()
        }
    } else {
        // On windows, num_cpus::get() directly.
        num_cpus::get()
    };

    if cores <= ignore {
        cores = 1;
    } else {
        cores -= ignore;
    }
    println!("{}", cores);
    0
}
<commit_msg>nproc: fix comment position<commit_after>#![crate_name = "uu_nproc"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate num_cpus;

#[cfg(not(windows))]
extern crate libc;

#[macro_use]
extern crate uucore;

use std::io::Write;
use std::env;

#[cfg(target_os = "linux")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 83;
#[cfg(target_os = "macos")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = libc::_SC_NPROCESSORS_CONF;
#[cfg(target_os = "freebsd")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 57;
#[cfg(target_os = "netbsd")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 1001;

static NAME: &'static str = "nproc";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optflag("", "all", "print the number of cores available to the system");
    opts.optopt("", "ignore", "ignore up to N cores", "N");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(err) => {
            show_error!("{}", err);
            return 1;
        }
    };

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
  {0} [OPTIONS]...

Print the number of cores available to the current process.", NAME, VERSION);

        print!("{}", opts.usage(&msg));
        return 0;
    }

    let mut ignore = match matches.opt_str("ignore") {
        Some(numstr) => match numstr.parse() {
            Ok(num) => num,
            Err(e) => {
                show_error!("\"{}\" is not a valid number: {}", numstr, e);
                return 1;
            }
        },
        None => 0
    };

    if !matches.opt_present("all") {
        ignore += match env::var("OMP_NUM_THREADS") {
            Ok(threadstr) => match threadstr.parse() {
                Ok(num) => num,
                Err(_)=> 0
            },
            Err(_) => 0
        };
    }

    let mut cores = if matches.opt_present("all") {
        if cfg!(target_os = "linux") || cfg!(target_os = "macos") || cfg!(target_os = "freebsd") || cfg!(target_os = "netbsd") {
            let nprocs = unsafe { libc::sysconf(_SC_NPROCESSORS_CONF) };
            if nprocs == 1 {
                // In some situation, /proc and /sys are not mounted, and sysconf returns 1.
                // However, we want to guarantee that `nproc --all` >= `nproc`.
                num_cpus::get()
            } else {
                if nprocs > 0 { nprocs as usize } else { 1 }
            }
        } else {
            // Other platform(e.g., windows), num_cpus::get() directly.
            num_cpus::get()
        }
    } else {
        num_cpus::get()
    };

    if cores <= ignore {
        cores = 1;
    } else {
        cores -= ignore;
    }
    println!("{}", cores);
    0
}
<|endoftext|>