<commit_before>use std::{io::Cursor, path::Path};

use rocksdb::{
    merge_operator::MergeFn, BlockBasedIndexType, BlockBasedOptions, ColumnFamily,
    ColumnFamilyDescriptor, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform,
    WriteBatch, DB,
};

use crate::model::{
    GameId, Key, KeyPrefix, LichessEntry, LichessGame, MastersEntry, MastersGame, Month,
    PlayerEntry, PlayerStatus, UserId, Year,
};

#[derive(Debug)]
pub struct Database {
    pub inner: DB,
}

fn column_family(
    name: &str,
    merge: Option<&str>,
    merge_fn: impl MergeFn + Clone,
    prefix: Option<usize>,
    block_size: usize,
    bloom_filter: i32,
) -> ColumnFamilyDescriptor {
    let mut opts = Options::default();
    if let Some(merge) = merge {
        opts.set_merge_operator_associative(merge, merge_fn);
    }
    opts.set_prefix_extractor(match prefix {
        Some(prefix) => SliceTransform::create_fixed_prefix(prefix),
        None => SliceTransform::create_noop(),
    });
    let mut block_opts = BlockBasedOptions::default();
    block_opts.set_index_type(BlockBasedIndexType::HashSearch);
    block_opts.set_block_size(block_size);
    if bloom_filter > 0 {
        block_opts.set_bloom_filter(bloom_filter, true);
    }
    opts.set_block_based_table_factory(&block_opts);
    ColumnFamilyDescriptor::new(name, opts)
}

impl Database {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Database, rocksdb::Error> {
        let mut db_opts = Options::default();
        db_opts.create_if_missing(true);
        db_opts.create_missing_column_families(true);

        let inner = DB::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                // Masters database
                column_family(
                    "masters",
                    Some("masters_merge"),
                    masters_merge,
                    Some(KeyPrefix::SIZE),
                    8 * 1024,
                    3,
                ),
                column_family("masters_game", None, void_merge, None, 4 * 1024, 0),
                // Lichess database
                column_family(
                    "lichess",
                    Some("lichess_merge"),
                    lichess_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family(
                    "lichess_game",
                    Some("lichess_game_merge"),
                    lichess_game_merge,
                    None,
                    4 * 1024,
                    0,
                ),
                // Player database (also shares lichess_game)
                column_family(
                    "player",
                    Some("player_merge"),
                    player_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family("player_status", None, void_merge, None, 4 * 1024, 0),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn masters(&self) -> MastersDatabase<'_> {
        MastersDatabase {
            inner: &self.inner,
            cf_masters: self.inner.cf_handle("masters").expect("cf masters"),
            cf_masters_game: self
                .inner
                .cf_handle("masters_game")
                .expect("cf masters_game"),
        }
    }

    pub fn lichess(&self) -> LichessDatabase<'_> {
        LichessDatabase {
            inner: &self.inner,
            cf_lichess: self.inner.cf_handle("lichess").expect("cf lichess"),
            cf_lichess_game: self
                .inner
                .cf_handle("lichess_game")
                .expect("cf lichess_game"),

            cf_player: self.inner.cf_handle("player").expect("cf player"),
            cf_player_status: self
                .inner
                .cf_handle("player_status")
                .expect("cf player_status"),
        }
    }
}

pub struct MastersDatabase<'a> {
    inner: &'a DB,
    cf_masters: &'a ColumnFamily,
    cf_masters_game: &'a ColumnFamily,
}

impl MastersDatabase<'_> {
    pub fn has_game(&self, id: GameId) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters_game, id.to_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn game(&self, id: GameId) -> Result<Option<MastersGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_masters_game, id.to_bytes())?
            .map(|buf| serde_json::from_slice(&buf).expect("deserialize masters game")))
    }

    pub fn has(&self, key: Key) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters, key.into_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn read(
        &self,
        key: KeyPrefix,
        since: Year,
        until: Year,
    ) -> Result<MastersEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_year(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_year(until.add_years_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_masters, opt, IteratorMode::Start);

        let mut entry = MastersEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize masters entry");
        }

        Ok(entry)
    }

    pub fn batch(&self) -> MastersBatch<'_> {
        MastersBatch {
            db: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct MastersBatch<'a> {
    db: &'a MastersDatabase<'a>,
    batch: WriteBatch,
}

impl MastersBatch<'_> {
    pub fn merge(&mut self, key: Key, entry: MastersEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(MastersEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize masters entry");
        self.batch
            .merge_cf(self.db.cf_masters, key.into_bytes(), cursor.into_inner());
    }

    pub fn put_game(&mut self, id: GameId, game: &MastersGame) {
        self.batch.put_cf(
            self.db.cf_masters_game,
            id.to_bytes(),
            serde_json::to_vec(game).expect("serialize masters game"),
        );
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        self.db.inner.write(self.batch)
    }
}

pub struct LichessDatabase<'a> {
    inner: &'a DB,
    cf_lichess: &'a ColumnFamily,
    cf_lichess_game: &'a ColumnFamily,

    cf_player: &'a ColumnFamily,
    cf_player_status: &'a ColumnFamily,
}

impl LichessDatabase<'_> {
    pub fn game(&self, id: GameId) -> Result<Option<LichessGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_lichess_game, id.to_bytes())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                LichessGame::read(&mut cursor).expect("deserialize game info")
            }))
    }

    pub fn read_lichess(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<LichessEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_month(until.add_months_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_lichess, opt, IteratorMode::Start);

        let mut entry = LichessEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize lichess entry");
        }

        Ok(entry)
    }

    pub fn read_player(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<PlayerEntry, rocksdb::Error> {
        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
        opt.set_iterate_upper_bound(key.with_month(until.add_months_saturating(1)).into_bytes());

        let iterator = self
            .inner
            .iterator_cf_opt(self.cf_player, opt, IteratorMode::Start);

        let mut entry = PlayerEntry::default();
        for (_key, value) in iterator {
            let mut cursor = Cursor::new(value);
            entry
                .extend_from_reader(&mut cursor)
                .expect("deserialize player entry");
        }

        Ok(entry)
    }

    pub fn player_status(&self, id: &UserId) -> Result<Option<PlayerStatus>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_player_status, id.as_lowercase_str())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                PlayerStatus::read(&mut cursor).expect("deserialize status")
            }))
    }

    pub fn put_player_status(
        &self,
        id: &UserId,
        status: &PlayerStatus,
    ) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerStatus::SIZE_HINT));
        status.write(&mut cursor).expect("serialize status");
        self.inner.put_cf(
            self.cf_player_status,
            id.as_lowercase_str(),
            cursor.into_inner(),
        )
    }

    pub fn batch(&self) -> LichessBatch<'_> {
        LichessBatch {
            inner: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct LichessBatch<'a> {
    inner: &'a LichessDatabase<'a>,
    batch: WriteBatch,
}

impl LichessBatch<'_> {
    pub fn merge_lichess(&mut self, key: Key, entry: LichessEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize lichess entry");
        self.batch
            .merge_cf(self.inner.cf_lichess, key.into_bytes(), cursor.into_inner());
    }

    pub fn merge_game(&mut self, id: GameId, info: LichessGame) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessGame::SIZE_HINT));
        info.write(&mut cursor).expect("serialize game info");
        self.batch.merge_cf(
            self.inner.cf_lichess_game,
            id.to_bytes(),
            cursor.into_inner(),
        );
    }

    pub fn merge_player(&mut self, key: Key, entry: PlayerEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize player entry");
        self.batch
            .merge_cf(self.inner.cf_player, key.into_bytes(), cursor.into_inner());
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        self.inner.inner.write(self.batch)
    }
}

fn lichess_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = LichessEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for lichess merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write lichess entry");
    Some(cursor.into_inner())
}

fn lichess_game_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    // Take latest game info, but merge index status.
    let mut info: Option<LichessGame> = None;
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = LichessGame::read(&mut cursor).expect("read for lichess game merge");
        if let Some(old_info) = info {
            new_info.indexed_player.white |= old_info.indexed_player.white;
            new_info.indexed_player.black |= old_info.indexed_player.black;
            new_info.indexed_lichess |= old_info.indexed_lichess;
        }
        info = Some(new_info);
        size_hint = op.len();
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
        info.write(&mut cursor).expect("write lichess game");
        cursor.into_inner()
    })
}

fn player_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PlayerEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for player merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write player entry");
    Some(cursor.into_inner())
}

fn masters_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = MastersEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for masters merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write masters entry");
    Some(cursor.into_inner())
}

fn void_merge(
    _key: &[u8],
    _existing: Option<&[u8]>,
    _operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    unreachable!("void merge")
}
<commit_msg>experiment with marking blocking io<commit_after>use std::{io::Cursor, path::Path};

use rocksdb::{
    merge_operator::MergeFn, BlockBasedIndexType, BlockBasedOptions, ColumnFamily,
    ColumnFamilyDescriptor, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform,
    WriteBatch, DB,
};

use crate::model::{
    GameId, Key, KeyPrefix, LichessEntry, LichessGame, MastersEntry, MastersGame, Month,
    PlayerEntry, PlayerStatus, UserId, Year,
};

#[derive(Debug)]
pub struct Database {
    pub inner: DB,
}

fn column_family(
    name: &str,
    merge: Option<&str>,
    merge_fn: impl MergeFn + Clone,
    prefix: Option<usize>,
    block_size: usize,
    bloom_filter: i32,
) -> ColumnFamilyDescriptor {
    let mut opts = Options::default();
    if let Some(merge) = merge {
        opts.set_merge_operator_associative(merge, merge_fn);
    }
    opts.set_prefix_extractor(match prefix {
        Some(prefix) => SliceTransform::create_fixed_prefix(prefix),
        None => SliceTransform::create_noop(),
    });
    let mut block_opts = BlockBasedOptions::default();
    block_opts.set_index_type(BlockBasedIndexType::HashSearch);
    block_opts.set_block_size(block_size);
    if bloom_filter > 0 {
        block_opts.set_bloom_filter(bloom_filter, true);
    }
    opts.set_block_based_table_factory(&block_opts);
    ColumnFamilyDescriptor::new(name, opts)
}

impl Database {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Database, rocksdb::Error> {
        let mut db_opts = Options::default();
        db_opts.create_if_missing(true);
        db_opts.create_missing_column_families(true);

        let inner = DB::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                // Masters database
                column_family(
                    "masters",
                    Some("masters_merge"),
                    masters_merge,
                    Some(KeyPrefix::SIZE),
                    8 * 1024,
                    3,
                ),
                column_family("masters_game", None, void_merge, None, 4 * 1024, 0),
                // Lichess database
                column_family(
                    "lichess",
                    Some("lichess_merge"),
                    lichess_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family(
                    "lichess_game",
                    Some("lichess_game_merge"),
                    lichess_game_merge,
                    None,
                    4 * 1024,
                    0,
                ),
                // Player database (also shares lichess_game)
                column_family(
                    "player",
                    Some("player_merge"),
                    player_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family("player_status", None, void_merge, None, 4 * 1024, 0),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn masters(&self) -> MastersDatabase<'_> {
        MastersDatabase {
            inner: &self.inner,
            cf_masters: self.inner.cf_handle("masters").expect("cf masters"),
            cf_masters_game: self
                .inner
                .cf_handle("masters_game")
                .expect("cf masters_game"),
        }
    }

    pub fn lichess(&self) -> LichessDatabase<'_> {
        LichessDatabase {
            inner: &self.inner,
            cf_lichess: self.inner.cf_handle("lichess").expect("cf lichess"),
            cf_lichess_game: self
                .inner
                .cf_handle("lichess_game")
                .expect("cf lichess_game"),

            cf_player: self.inner.cf_handle("player").expect("cf player"),
            cf_player_status: self
                .inner
                .cf_handle("player_status")
                .expect("cf player_status"),
        }
    }
}

pub struct MastersDatabase<'a> {
    inner: &'a DB,
    cf_masters: &'a ColumnFamily,
    cf_masters_game: &'a ColumnFamily,
}

impl MastersDatabase<'_> {
    pub fn has_game(&self, id: GameId) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters_game, id.to_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn game(&self, id: GameId) -> Result<Option<MastersGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_masters_game, id.to_bytes())?
            .map(|buf| serde_json::from_slice(&buf).expect("deserialize masters game")))
    }

    pub fn has(&self, key: Key) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters, key.into_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn read(
        &self,
        key: KeyPrefix,
        since: Year,
        until: Year,
    ) -> Result<MastersEntry, rocksdb::Error> {
        tokio::task::block_in_place(move || {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_year(since).into_bytes());
            opt.set_iterate_upper_bound(key.with_year(until.add_years_saturating(1)).into_bytes());

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_masters, opt, IteratorMode::Start);

            let mut entry = MastersEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize masters entry");
            }

            Ok(entry)
        })
    }

    pub fn batch(&self) -> MastersBatch<'_> {
        MastersBatch {
            db: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct MastersBatch<'a> {
    db: &'a MastersDatabase<'a>,
    batch: WriteBatch,
}

impl MastersBatch<'_> {
    pub fn merge(&mut self, key: Key, entry: MastersEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(MastersEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize masters entry");
        self.batch
            .merge_cf(self.db.cf_masters, key.into_bytes(), cursor.into_inner());
    }

    pub fn put_game(&mut self, id: GameId, game: &MastersGame) {
        self.batch.put_cf(
            self.db.cf_masters_game,
            id.to_bytes(),
            serde_json::to_vec(game).expect("serialize masters game"),
        );
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        tokio::task::block_in_place(move || self.db.inner.write(self.batch))
    }
}

pub struct LichessDatabase<'a> {
    inner: &'a DB,
    cf_lichess: &'a ColumnFamily,
    cf_lichess_game: &'a ColumnFamily,

    cf_player: &'a ColumnFamily,
    cf_player_status: &'a ColumnFamily,
}

impl LichessDatabase<'_> {
    pub fn game(&self, id: GameId) -> Result<Option<LichessGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_lichess_game, id.to_bytes())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                LichessGame::read(&mut cursor).expect("deserialize game info")
            }))
    }

    pub fn read_lichess(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<LichessEntry, rocksdb::Error> {
        tokio::task::block_in_place(move || {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
            opt.set_iterate_upper_bound(
                key.with_month(until.add_months_saturating(1)).into_bytes(),
            );

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_lichess, opt, IteratorMode::Start);

            let mut entry = LichessEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize lichess entry");
            }

            Ok(entry)
        })
    }

    pub fn read_player(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<PlayerEntry, rocksdb::Error> {
        tokio::task::block_in_place(move || {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
            opt.set_iterate_upper_bound(
                key.with_month(until.add_months_saturating(1)).into_bytes(),
            );

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_player, opt, IteratorMode::Start);

            let mut entry = PlayerEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize player entry");
            }

            Ok(entry)
        })
    }

    pub fn player_status(&self, id: &UserId) -> Result<Option<PlayerStatus>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_player_status, id.as_lowercase_str())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                PlayerStatus::read(&mut cursor).expect("deserialize status")
            }))
    }

    pub fn put_player_status(
        &self,
        id: &UserId,
        status: &PlayerStatus,
    ) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerStatus::SIZE_HINT));
        status.write(&mut cursor).expect("serialize status");
        self.inner.put_cf(
            self.cf_player_status,
            id.as_lowercase_str(),
            cursor.into_inner(),
        )
    }

    pub fn batch(&self) -> LichessBatch<'_> {
        LichessBatch {
            inner: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct LichessBatch<'a> {
    inner: &'a LichessDatabase<'a>,
    batch: WriteBatch,
}

impl LichessBatch<'_> {
    pub fn merge_lichess(&mut self, key: Key, entry: LichessEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize lichess entry");
        self.batch
            .merge_cf(self.inner.cf_lichess, key.into_bytes(), cursor.into_inner());
    }

    pub fn merge_game(&mut self, id: GameId, info: LichessGame) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessGame::SIZE_HINT));
        info.write(&mut cursor).expect("serialize game info");
        self.batch.merge_cf(
            self.inner.cf_lichess_game,
            id.to_bytes(),
            cursor.into_inner(),
        );
    }

    pub fn merge_player(&mut self, key: Key, entry: PlayerEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize player entry");
        self.batch
            .merge_cf(self.inner.cf_player, key.into_bytes(), cursor.into_inner());
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        tokio::task::block_in_place(|| self.inner.inner.write(self.batch))
    }
}

fn lichess_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = LichessEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for lichess merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write lichess entry");
    Some(cursor.into_inner())
}

fn lichess_game_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    // Take latest game info, but merge index status.
    let mut info: Option<LichessGame> = None;
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = LichessGame::read(&mut cursor).expect("read for lichess game merge");
        if let Some(old_info) = info {
            new_info.indexed_player.white |= old_info.indexed_player.white;
            new_info.indexed_player.black |= old_info.indexed_player.black;
            new_info.indexed_lichess |= old_info.indexed_lichess;
        }
        info = Some(new_info);
        size_hint = op.len();
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
        info.write(&mut cursor).expect("write lichess game");
        cursor.into_inner()
    })
}

fn player_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PlayerEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for player merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write player entry");
    Some(cursor.into_inner())
}

fn masters_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = MastersEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for masters merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write masters entry");
    Some(cursor.into_inner())
}

fn void_merge(
    _key: &[u8],
    _existing: Option<&[u8]>,
    _operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    unreachable!("void merge")
}
<|endoftext|>


<commit_before>//! This crate defines a
//! [Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
//! pretty-printing API.
//!
//! See `Doc` and
extern crate typed_arena;

use doc::Doc::{Append, Group, Nest, Newline, Nil, Space, Text};
use std::borrow::Cow;
use std::fmt;
use std::ops::Deref;

mod doc;

#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct BoxDoc<'a>(Box<doc::Doc<'a, BoxDoc<'a>>>);

impl<'a> fmt::Debug for BoxDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> BoxDoc<'a> {
    fn new(doc: doc::Doc<'a, BoxDoc<'a>>) -> BoxDoc<'a> {
        BoxDoc(Box::new(doc))
    }
}

impl<'a> Deref for BoxDoc<'a> {
    type Target = doc::Doc<'a, BoxDoc<'a>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// The `DocBuilder` type allows for convenient appending of documents even for arena allocated
/// documents by storing the arena inline.
pub struct DocBuilder<'a, A: ?Sized>(pub &'a A, pub doc::Doc<'a, A::Doc>)
    where A: DocAllocator<'a> + 'a;

impl<'a, A: ?Sized> Into<doc::Doc<'a, A::Doc>> for DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    fn into(self) -> doc::Doc<'a, A::Doc> {
        self.1
    }
}

/// The `DocAllocator` trait abstracts over a type which can allocate (pointers to) `Doc`.
pub trait DocAllocator<'a> {
    type Doc: Deref<Target = doc::Doc<'a, Self::Doc>> + Clone;
    fn alloc(&'a self, doc::Doc<'a, Self::Doc>) -> Self::Doc;

    #[inline]
    fn nil(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Nil)
    }

    #[inline]
    fn newline(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Newline)
    }

    #[inline]
    fn space(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Space)
    }

    #[inline]
    fn as_string<T: ToString>(&'a self, t: T) -> DocBuilder<'a, Self> {
        self.text(t.to_string())
    }

    #[inline]
    fn text<T: Into<Cow<'a, str>>>(&'a self, data: T) -> DocBuilder<'a, Self> {
        DocBuilder(self, Text(data.into()))
    }

    #[inline]
    fn concat<I>(&'a self, docs: I) -> DocBuilder<'a, Self>
        where I: IntoIterator,
              I::Item: Into<doc::Doc<'a, Self::Doc>>
    {
        docs.into_iter().fold(self.nil(), |a, b| a.append(b))
    }
}

impl<'a, 's, A: ?Sized> DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    #[inline]
    pub fn append<B>(self, that: B) -> DocBuilder<'a, A>
        where B: Into<doc::Doc<'a, A::Doc>>
    {
        let DocBuilder(allocator, this) = self;
        let that = that.into();
        let doc = match this {
            Nil => that,
            _ => {
                match that {
                    Nil => this,
                    _ => Append(allocator.alloc(this), allocator.alloc(that)),
                }
            }
        };
        DocBuilder(allocator, doc)
    }

    #[inline]
    pub fn group(self) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Group(allocator.alloc(this)))
    }

    #[inline]
    pub fn nest(self, offset: usize) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Nest(offset, allocator.alloc(this)))
    }
}

/// Newtype wrapper for `&doc::Doc`
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct RefDoc<'a>(&'a doc::Doc<'a, RefDoc<'a>>);

impl<'a> fmt::Debug for RefDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> Deref for RefDoc<'a> {
    type Target = doc::Doc<'a, RefDoc<'a>>;

    fn deref(&self) -> &doc::Doc<'a, RefDoc<'a>> {
        &self.0
    }
}

/// An arena which can be used to allocate `Doc` values.
pub type Arena<'a> = typed_arena::Arena<doc::Doc<'a, RefDoc<'a>>>;

impl<'a> DocAllocator<'a> for Arena<'a> {
    type Doc = RefDoc<'a>;

    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        RefDoc(Arena::alloc(self, doc))
    }
}

pub struct BoxAllocator;

static BOX_ALLOCATOR: BoxAllocator = BoxAllocator;

impl<'a> DocAllocator<'a> for BoxAllocator {
    type Doc = BoxDoc<'a>;

    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        BoxDoc::new(doc)
    }
}

pub use doc::Doc;

impl<'a, B> Doc<'a, B> {
    #[inline]
    pub fn nil() -> Doc<'a, B> {
        Nil
    }

    #[inline]
    pub fn as_string<T: ToString>(t: T) -> Doc<'a, B> {
        Doc::text(t.to_string())
    }

    #[inline]
    pub fn newline() -> Doc<'a, B> {
        Newline
    }

    #[inline]
    pub fn text<T: Into<Cow<'a, str>>>(data: T) -> Doc<'a, B> {
        Text(data.into())
    }

    #[inline]
    pub fn space() -> Doc<'a, B> {
        Space
    }
}

impl<'a> Doc<'a, BoxDoc<'a>> {
    #[inline]
    pub fn append(self, that: Doc<'a, BoxDoc<'a>>) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).append(that).into()
    }

    #[inline]
    pub fn concat<I>(&'a self, docs: I) -> Doc<'a, BoxDoc<'a>>
        where I: IntoIterator<Item = Doc<'a, BoxDoc<'a>>>
    {
        docs.into_iter().fold(Doc::nil(), |a, b| a.append(b))
    }

    #[inline]
    pub fn group(self) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).group().into()
    }

    #[inline]
    pub fn nest(self, offset: usize) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).nest(offset).into()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::from_utf8;

    macro_rules! test {
        ($size: expr, $actual: expr, $expected: expr) => {
            let mut vec = Vec::new();
            $actual.render($size, &mut vec).unwrap();
            assert_eq!(from_utf8(&vec).unwrap(), $expected);
        };
        ($actual: expr, $expected: expr) => {
            test!(70, $actual, $expected)
        }
    }

    #[test]
    fn box_doc_inference() {
        let doc = Doc::group(Doc::text("test").append(Doc::space()).append(Doc::text("test")));
        test!(doc, "test test");
    }

    #[test]
    fn forced_newline() {
        let doc = Doc::group(Doc::text("test").append(Doc::newline()).append(Doc::text("test")));
        test!(doc, "test\ntest");
    }

    #[test]
    fn block() {
        let doc = Doc::group(Doc::text("{")
            .append(Doc::space()
                .append(Doc::text("test"))
                .append(Doc::space())
                .append(Doc::text("test"))
                .nest(2))
            .append(Doc::space())
            .append(Doc::text("}")));
        test!(5, doc, "{\n  test\n  test\n}");
    }
}
<commit_msg>fix: Add missing derive to DocBuilder<commit_after>//! This crate defines a
//! [Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
//! pretty-printing API.
//!
//! See `Doc` and
extern crate typed_arena;

use doc::Doc::{Append, Group, Nest, Newline, Nil, Space, Text};
use std::borrow::Cow;
use std::fmt;
use std::ops::Deref;

mod doc;

#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct BoxDoc<'a>(Box<doc::Doc<'a, BoxDoc<'a>>>);

impl<'a> fmt::Debug for BoxDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> BoxDoc<'a> {
    fn new(doc: doc::Doc<'a, BoxDoc<'a>>) -> BoxDoc<'a> {
        BoxDoc(Box::new(doc))
    }
}

impl<'a> Deref for BoxDoc<'a> {
    type Target = doc::Doc<'a, BoxDoc<'a>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// The `DocBuilder` type allows for convenient appending of documents even for arena allocated
/// documents by storing the arena inline.
#[derive(Eq, Ord, PartialEq, PartialOrd)]
pub struct DocBuilder<'a, A: ?Sized>(pub &'a A, pub doc::Doc<'a, A::Doc>)
    where A: DocAllocator<'a> + 'a;

impl<'a, A: DocAllocator<'a> + 'a> Clone for DocBuilder<'a, A> {
    fn clone(&self) -> Self {
        DocBuilder(self.0, self.1.clone())
    }
}

impl<'a, A: ?Sized> Into<doc::Doc<'a, A::Doc>> for DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    fn into(self) -> doc::Doc<'a, A::Doc> {
        self.1
    }
}

/// The `DocAllocator` trait abstracts over a type which can allocate (pointers to) `Doc`.
pub trait DocAllocator<'a> {
    type Doc: Deref<Target = doc::Doc<'a, Self::Doc>> + Clone;
    fn alloc(&'a self, doc::Doc<'a, Self::Doc>) -> Self::Doc;

    #[inline]
    fn nil(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Nil)
    }

    #[inline]
    fn newline(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Newline)
    }

    #[inline]
    fn space(&'a self) -> DocBuilder<'a, Self> {
        DocBuilder(self, Space)
    }

    #[inline]
    fn as_string<T: ToString>(&'a self, t: T) -> DocBuilder<'a, Self> {
        self.text(t.to_string())
    }

    #[inline]
    fn text<T: Into<Cow<'a, str>>>(&'a self, data: T) -> DocBuilder<'a, Self> {
        DocBuilder(self, Text(data.into()))
    }

    #[inline]
    fn concat<I>(&'a self, docs: I) -> DocBuilder<'a, Self>
        where I: IntoIterator,
              I::Item: Into<doc::Doc<'a, Self::Doc>>
    {
        docs.into_iter().fold(self.nil(), |a, b| a.append(b))
    }
}

impl<'a, 's, A: ?Sized> DocBuilder<'a, A>
    where A: DocAllocator<'a>
{
    #[inline]
    pub fn append<B>(self, that: B) -> DocBuilder<'a, A>
        where B: Into<doc::Doc<'a, A::Doc>>
    {
        let DocBuilder(allocator, this) = self;
        let that = that.into();
        let doc = match this {
            Nil => that,
            _ => {
                match that {
                    Nil => this,
                    _ => Append(allocator.alloc(this), allocator.alloc(that)),
                }
            }
        };
        DocBuilder(allocator, doc)
    }

    #[inline]
    pub fn group(self) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Group(allocator.alloc(this)))
    }

    #[inline]
    pub fn nest(self, offset: usize) -> DocBuilder<'a, A> {
        let DocBuilder(allocator, this) = self;
        DocBuilder(allocator, Nest(offset, allocator.alloc(this)))
    }
}

/// Newtype wrapper for `&doc::Doc`
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct RefDoc<'a>(&'a doc::Doc<'a, RefDoc<'a>>);

impl<'a> fmt::Debug for RefDoc<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> Deref for RefDoc<'a> {
    type Target = doc::Doc<'a, RefDoc<'a>>;

    fn deref(&self) -> &doc::Doc<'a, RefDoc<'a>> {
        &self.0
    }
}

/// An arena which can be used to allocate `Doc` values.
pub type Arena<'a> = typed_arena::Arena<doc::Doc<'a, RefDoc<'a>>>;

impl<'a> DocAllocator<'a> for Arena<'a> {
    type Doc = RefDoc<'a>;

    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        RefDoc(Arena::alloc(self, doc))
    }
}

pub struct BoxAllocator;

static BOX_ALLOCATOR: BoxAllocator = BoxAllocator;

impl<'a> DocAllocator<'a> for BoxAllocator {
    type Doc = BoxDoc<'a>;

    fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc>) -> Self::Doc {
        BoxDoc::new(doc)
    }
}

pub use doc::Doc;

impl<'a, B> Doc<'a, B> {
    #[inline]
    pub fn nil() -> Doc<'a, B> {
        Nil
    }

    #[inline]
    pub fn as_string<T: ToString>(t: T) -> Doc<'a, B> {
        Doc::text(t.to_string())
    }

    #[inline]
    pub fn newline() -> Doc<'a, B> {
        Newline
    }

    #[inline]
    pub fn text<T: Into<Cow<'a, str>>>(data: T) -> Doc<'a, B> {
        Text(data.into())
    }

    #[inline]
    pub fn space() -> Doc<'a, B> {
        Space
    }
}

impl<'a> Doc<'a, BoxDoc<'a>> {
    #[inline]
    pub fn append(self, that: Doc<'a, BoxDoc<'a>>) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).append(that).into()
    }

    #[inline]
    pub fn concat<I>(&'a self, docs: I) -> Doc<'a, BoxDoc<'a>>
        where I: IntoIterator<Item = Doc<'a, BoxDoc<'a>>>
    {
        docs.into_iter().fold(Doc::nil(), |a, b| a.append(b))
    }

    #[inline]
    pub fn group(self) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).group().into()
    }

    #[inline]
    pub fn nest(self, offset: usize) -> Doc<'a, BoxDoc<'a>> {
        DocBuilder(&BOX_ALLOCATOR, self).nest(offset).into()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::from_utf8;

    macro_rules! test {
        ($size: expr, $actual: expr, $expected: expr) => {
            let mut vec = Vec::new();
            $actual.render($size, &mut vec).unwrap();
            assert_eq!(from_utf8(&vec).unwrap(), $expected);
        };
        ($actual: expr, $expected: expr) => {
            test!(70, $actual, $expected)
        }
    }

    #[test]
    fn box_doc_inference() {
        let doc = Doc::group(Doc::text("test").append(Doc::space()).append(Doc::text("test")));
        test!(doc, "test test");
    }

    #[test]
    fn forced_newline() {
        let doc = Doc::group(Doc::text("test").append(Doc::newline()).append(Doc::text("test")));
        test!(doc, "test\ntest");
    }

    #[test]
    fn block() {
        let doc = Doc::group(Doc::text("{")
            .append(Doc::space()
                .append(Doc::text("test"))
                .append(Doc::space())
                .append(Doc::text("test"))
                .nest(2))
            .append(Doc::space())
            .append(Doc::text("}")));
        test!(5, doc, "{\n  test\n  test\n}");
    }
}
<|endoftext|>


<commit_before>// Small functions of utility

use racer::SearchType::{self, ExactMatch, StartsWith};

use std::cmp;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

pub fn getline(filepath: &Path, linenum: usize) -> String {
    let mut i = 0;
    let file = BufReader::new(File::open(filepath).unwrap());
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.unwrap().to_string();
        }
    }
    "not found".to_string()
}

pub fn is_pattern_char(c: char) -> bool {
    c.is_alphanumeric() || c.is_whitespace() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_search_expr_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_ident_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_')
}

pub fn txt_matches(stype: SearchType, needle: &str, haystack: &str) -> bool {
    return match stype {
        ExactMatch => {
            let nlen = needle.len();
            let hlen = haystack.len();

            if nlen == 0 {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if (n == 0  || !is_ident_char(char_at(haystack, n-1))) &&
                    (n+nlen == hlen || !is_ident_char(char_at(haystack, n+nlen))) {
                    return true;
                }
                n += 1;
            }
            return false;
        },
        StartsWith => {
            if needle.is_empty() {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if n == 0  || !is_ident_char(char_at(haystack, n-1)) {
                    return true;
                }
                n += 1;
            }
            return false;
        }
    }
}

pub fn symbol_matches(stype: SearchType, searchstr: &str, candidate: &str) -> bool {
   match stype {
        ExactMatch => searchstr == candidate,
        StartsWith => candidate.starts_with(searchstr)
    }
}

// pub fn get_backtrace() -> String {
//     let mut m = std::old_io::MemWriter::new();
//     let s = std::rt::backtrace::write(&mut m)
//         .ok().map_or("NO backtrace".to_string(),
//                      |_| String::from_utf8_lossy(m.get_ref()).to_string());
//     return s;
// }

pub fn is_double_dot(msrc: &str, i: usize) -> bool {
    (i > 1) && &msrc[i-1..i+1] == ".."
}

#[test]
fn txt_matches_matches_stuff() {
    assert_eq!(true, txt_matches(ExactMatch, "Vec","Vec"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","Vector"));
    assert_eq!(false, txt_matches(ExactMatch, "Vec","use Vector"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","use Vector"));
    assert_eq!(false, txt_matches(StartsWith, "Vec","use aVector"));
    assert_eq!(true, txt_matches(ExactMatch, "Vec","use Vec"));
}

pub fn expand_ident(s: &str, pos: usize) -> (usize, usize) {
    // TODO: Would this better be an assertion ? Why are out-of-bound values getting here ?
    // They are coming from the command-line, question is, if they should be handled beforehand
    // clamp pos into allowed range
    let pos = cmp::min(s.len(), pos);
    let sb = &s[..pos];
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices().rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    (start, pos)
}

pub fn find_ident_end(s: &str, pos: usize) -> usize {
    // find end of word
    let sa = &s[pos..];
    for (i, c) in sa.char_indices() {
        if !is_ident_char(c) {
            return pos + i;
        }
    }
    s.len()
}

#[test]
fn find_ident_end_ascii() {
    assert_eq!(5, find_ident_end("ident", 0));
    assert_eq!(6, find_ident_end("(ident)", 1));
    assert_eq!(17, find_ident_end("let an_identifier = 100;", 4));
}

#[test]
fn find_ident_end_unicode() {
    assert_eq!(7, find_ident_end("num_µs", 0));
    assert_eq!(10, find_ident_end("ends_in_µ", 0));
}

pub fn to_refs<'a>(v: &'a Vec<String>) -> Vec<&'a str> {
    let mut out = Vec::new();
    for item in v.iter() {
        out.push(&item[..]);
    }
    out
}

pub fn find_last_str(needle: &str, mut haystack: &str) -> Option<usize> {
    let mut res = None;
    while let Some(n) = haystack.find(needle) {
        res = Some(n);
        haystack = &haystack[n+1..];
    }
    res
}

// PD: short term replacement for .char_at() function. Should be replaced once
// that stabilizes
pub fn char_at(src: &str, i: usize) -> char {
    src[i..].chars().next().unwrap()
}

// PD: short term replacement for path.exists() (PathExt trait). Replace once
// that stabilizes
pub fn path_exists<P: AsRef<Path>>(path: P) -> bool {
    is_dir(path.as_ref()) || File::open(path).is_ok()
}

// PD: short term replacement for path.is_dir() (PathExt trait). Replace once
// that stabilizes
pub fn is_dir<P: AsRef<Path>>(path: P) -> bool {
    ::std::fs::read_dir(path.as_ref()).is_ok()
}
<commit_msg>Use std::fs::metadata to test if a path is a directory.<commit_after>// Small functions of utility

use racer::SearchType::{self, ExactMatch, StartsWith};

use std;
use std::cmp;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

pub fn getline(filepath: &Path, linenum: usize) -> String {
    let mut i = 0;
    let file = BufReader::new(File::open(filepath).unwrap());
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.unwrap().to_string();
        }
    }
    "not found".to_string()
}

pub fn is_pattern_char(c: char) -> bool {
    c.is_alphanumeric() || c.is_whitespace() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_search_expr_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == ':') || (c == '.')
}

pub fn is_ident_char(c: char) -> bool {
    c.is_alphanumeric() || (c == '_')
}

pub fn txt_matches(stype: SearchType, needle: &str, haystack: &str) -> bool {
    return match stype {
        ExactMatch => {
            let nlen = needle.len();
            let hlen = haystack.len();

            if nlen == 0 {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if (n == 0  || !is_ident_char(char_at(haystack, n-1))) &&
                    (n+nlen == hlen || !is_ident_char(char_at(haystack, n+nlen))) {
                    return true;
                }
                n += 1;
            }
            return false;
        },
        StartsWith => {
            if needle.is_empty() {
                return true;
            }

            // PD: switch to use .match_indices() when that stabilizes
            let mut n=0;
            while let Some(n1) = haystack[n..].find(needle) {
                n += n1;
                if n == 0  || !is_ident_char(char_at(haystack, n-1)) {
                    return true;
                }
                n += 1;
            }
            return false;
        }
    }
}

pub fn symbol_matches(stype: SearchType, searchstr: &str, candidate: &str) -> bool {
   match stype {
        ExactMatch => searchstr == candidate,
        StartsWith => candidate.starts_with(searchstr)
    }
}

// pub fn get_backtrace() -> String {
//     let mut m = std::old_io::MemWriter::new();
//     let s = std::rt::backtrace::write(&mut m)
//         .ok().map_or("NO backtrace".to_string(),
//                      |_| String::from_utf8_lossy(m.get_ref()).to_string());
//     return s;
// }

pub fn is_double_dot(msrc: &str, i: usize) -> bool {
    (i > 1) && &msrc[i-1..i+1] == ".."
}

#[test]
fn txt_matches_matches_stuff() {
    assert_eq!(true, txt_matches(ExactMatch, "Vec","Vec"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","Vector"));
    assert_eq!(false, txt_matches(ExactMatch, "Vec","use Vector"));
    assert_eq!(true, txt_matches(StartsWith, "Vec","use Vector"));
    assert_eq!(false, txt_matches(StartsWith, "Vec","use aVector"));
    assert_eq!(true, txt_matches(ExactMatch, "Vec","use Vec"));
}

pub fn expand_ident(s: &str, pos: usize) -> (usize, usize) {
    // TODO: Would this better be an assertion ? Why are out-of-bound values getting here ?
    // They are coming from the command-line, question is, if they should be handled beforehand
    // clamp pos into allowed range
    let pos = cmp::min(s.len(), pos);
    let sb = &s[..pos];
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices().rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    (start, pos)
}

pub fn find_ident_end(s: &str, pos: usize) -> usize {
    // find end of word
    let sa = &s[pos..];
    for (i, c) in sa.char_indices() {
        if !is_ident_char(c) {
            return pos + i;
        }
    }
    s.len()
}

#[test]
fn find_ident_end_ascii() {
    assert_eq!(5, find_ident_end("ident", 0));
    assert_eq!(6, find_ident_end("(ident)", 1));
    assert_eq!(17, find_ident_end("let an_identifier = 100;", 4));
}

#[test]
fn find_ident_end_unicode() {
    assert_eq!(7, find_ident_end("num_µs", 0));
    assert_eq!(10, find_ident_end("ends_in_µ", 0));
}

pub fn to_refs<'a>(v: &'a Vec<String>) -> Vec<&'a str> {
    let mut out = Vec::new();
    for item in v.iter() {
        out.push(&item[..]);
    }
    out
}

pub fn find_last_str(needle: &str, mut haystack: &str) -> Option<usize> {
    let mut res = None;
    while let Some(n) = haystack.find(needle) {
        res = Some(n);
        haystack = &haystack[n+1..];
    }
    res
}

// PD: short term replacement for .char_at() function. Should be replaced once
// that stabilizes
pub fn char_at(src: &str, i: usize) -> char {
    src[i..].chars().next().unwrap()
}

// PD: short term replacement for path.exists() (PathExt trait). Replace once
// that stabilizes
pub fn path_exists<P: AsRef<Path>>(path: P) -> bool {
    is_dir(path.as_ref()) || File::open(path).is_ok()
}

// PD: short term replacement for path.is_dir() (PathExt trait). Replace once
// that stabilizes
pub fn is_dir<P: AsRef<Path>>(path: P) -> bool {
    match std::fs::metadata(path) {
        Ok(file_info) => file_info.is_dir(),
        _ => false
    }
}
<|endoftext|>


<commit_before>use num;
use num::traits::NumCast;
use std::str::FromStr;
use std::error::Error;
use std::fmt;

/// A data table consisting of varying column types and headers.
pub struct DataTable {
    pub data_cols: Vec<DataColumn>,
}

impl DataTable {
    pub fn empty() -> DataTable {
        DataTable { data_cols: Vec::new() }
    }

    pub fn cols(&self) -> usize {
        self.data_cols.len()
    }

    pub fn rows(&self) -> usize {
        if self.data_cols.len() > 0 {
            return self.data_cols[0].len();
        }

        0usize
    }

    pub fn shrink_to_fit(&mut self) {
        for col in self.data_cols.iter_mut() {
            col.shrink_to_fit();
        }

        self.data_cols.shrink_to_fit();
    }
}

/// A data column with a consistent data type.
pub struct DataColumn {
    pub data: Vec<String>,
}

#[derive(Debug)]
pub struct DataCastError;

impl fmt::Display for DataCastError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "DataCastError")
    }
}

impl Error for DataCastError {
    fn description(&self) -> &str {
        "Failed to cast data."
    }
}

impl DataColumn {
    pub fn empty() -> DataColumn {
        DataColumn {
            data: Vec::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

    pub fn push(&mut self, val: &str) {
        self.data.push(val.to_owned());
    }

    pub fn into_vec<T: FromStr>(self) -> Result<Vec<T>, DataCastError> {
        let mut casted_data = Vec::<T>::with_capacity(self.data.len());

        for d in self.data.into_iter() {
            match T::from_str(&d[..]) {
                Ok(x) => casted_data.push(x),
                Err(_) => return Err(DataCastError),
            }
        }

        Ok(casted_data)
    }

    pub fn cast<T: FromStr>(&self) -> Option<Vec<T>> {
        let mut casted_data = Vec::<T>::with_capacity(self.data.len());

        for d in self.data.iter() {
            match T::from_str(&d[..]) {
                Ok(x) => casted_data.push(x),
                Err(_) => return None,
            }
        }

        Some(casted_data)
    }

    pub fn shrink_to_fit(&mut self) {
        self.data.shrink_to_fit();
    }
}
<commit_msg>Adding first pass at function for getting to data array.<commit_after>use std::str::FromStr;
use std::error::Error;
use std::fmt;

/// A data table consisting of varying column types and headers.
pub struct DataTable {
    pub data_cols: Vec<DataColumn>,
}

impl DataTable {
    pub fn empty() -> DataTable {
        DataTable { data_cols: Vec::new() }
    }

    pub fn cols(&self) -> usize {
        self.data_cols.len()
    }

    pub fn rows(&self) -> usize {
        if self.data_cols.len() > 0 {
            return self.data_cols[0].len();
        }

        0usize
    }

    /// Shrinks the table and it's underlying
    pub fn shrink_to_fit(&mut self) {
        for col in self.data_cols.iter_mut() {
            col.shrink_to_fit();
        }

        self.data_cols.shrink_to_fit();
    }

    /// Attempt to convert the data table into a single Vec.
    ///
    /// Uses column major ordering.
    pub fn into_consistent_data<T: FromStr>(self) -> Result<Vec<T>, DataCastError> {
        let mut table_data = Vec::with_capacity(self.cols() * self.rows());
        for d in self.data_cols.into_iter() {
            match d.into_vec() {
                Ok(x) => table_data.extend(x),
                Err(e) => return Err(e),
            }
        }

        Ok(table_data)
    }
}

/// An error for attempting an into conversion.
#[derive(Debug)]
pub struct DataCastError;

impl fmt::Display for DataCastError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "DataCastError")
    }
}

impl Error for DataCastError {
    fn description(&self) -> &str {
        "Failed to cast data."
    }
}

/// A data column with a consistent data type.
pub struct DataColumn {
    data: Vec<String>,
}

impl DataColumn {
    pub fn empty() -> DataColumn {
        DataColumn {
            data: Vec::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

    pub fn data(&self) -> &Vec<String> {
        &self.data
    }

    pub fn push(&mut self, val: &str) {
        self.data.push(val.to_owned());
    }

    pub fn into_vec<T: FromStr>(self) -> Result<Vec<T>, DataCastError> {
        let mut casted_data = Vec::<T>::with_capacity(self.data.len());

        for d in self.data.into_iter() {
            match T::from_str(&d[..]) {
                Ok(x) => casted_data.push(x),
                Err(_) => return Err(DataCastError),
            }
        }

        Ok(casted_data)
    }

    pub fn cast<T: FromStr>(&self) -> Option<Vec<T>> {
        let mut casted_data = Vec::<T>::with_capacity(self.data.len());

        for d in self.data.iter() {
            match T::from_str(&d[..]) {
                Ok(x) => casted_data.push(x),
                Err(_) => return None,
            }
        }

        Some(casted_data)
    }

    pub fn shrink_to_fit(&mut self) {
        self.data.shrink_to_fit();
    }
}<|endoftext|>


<commit_before>use std::f32::consts::PI;
use sfml::graphics::*;
use resize_handler::ResizeHandler;

const DAY_LENGTH: f32 = 20.;
const MAX_DARK: f32 = 128.;

pub struct DayNight {
    time: f32,
    fader: RectangleShape<'static>,
}

impl DayNight {
    pub fn new() -> DayNight {
        DayNight {
            time: 0.,
            fader: RectangleShape::new(),
        }
    }

    pub fn update(&mut self, delta: f32) {
        self.time += delta * (1. / DAY_LENGTH);

        if self.time > 1. {
            self.time = self.time - 1.;
        }

        let light = self.daylight();
        self.fader.set_fill_color(&light);
    }

    pub fn time(&self) -> f32 {
        self.time
    }

    fn daylight(&self) -> Color {
        let value = if self.time < 0.5 {
            self.time
        } else {
            1.0 - self.time
        }
            * 2.;

        //println!("{}        {}", self.time, (value * 2. * PI));
        //println!("value: {}", value);
        //println!("light: {}", (value * PI * 0.5).sin());
        //println!("time: {}", self.time);

        //Color::rgba(0, 0, 0, (((value * PI * 2.).sin() * 255.) / 2.) as u8)
        Color::rgba(0, 0, 0, ((value * PI * 0.5).sin() * MAX_DARK) as u8)
    }
}

impl Drawable for DayNight {
    fn draw<'se, 'tex, 'sh, 'shte>(
                                   &'se self,
                                   target: &mut RenderTarget,
                                   states: RenderStates<'tex, 'sh, 'shte>)
        where 'se: 'sh
    {
        let old_view = target.view().to_owned();

        let size = target.size();
        let mut view = target.view().to_owned();
        view.set_center2f(size.x as f32 / 2., size.y as f32 / 2.);
        target.set_view(&view);

        target.draw_rectangle_shape(&self.fader, states);

        target.set_view(&old_view);
    }
}

impl ResizeHandler for DayNight {
    fn on_resize(&mut self, width: u32, height: u32) {
        let width = width as f32;
        let height = height as f32;

        self.fader.set_size2f(width, height);
    }
}
<commit_msg>Make day/night cycle smoother<commit_after>use std::f32::consts::PI;
use sfml::graphics::*;
use resize_handler::ResizeHandler;

const DAY_LENGTH: f32 = 20.;
const MAX_DARK: f32 = 128.;

pub struct DayNight {
    time: f32,
    fader: RectangleShape<'static>,
}

impl DayNight {
    pub fn new() -> DayNight {
        DayNight {
            time: 0.,
            fader: RectangleShape::new(),
        }
    }

    pub fn update(&mut self, delta: f32) {
        self.time += delta * (1. / DAY_LENGTH);

        if self.time > 1. {
            self.time = self.time - 1.;
        }

        let light = self.daylight();
        self.fader.set_fill_color(&light);
    }

    pub fn time(&self) -> f32 {
        self.time
    }

    fn daylight(&self) -> Color {
        let mut value = if self.time < 0.5 {
            self.time
        } else {
            1.0 - self.time
        }
            * 2.;

        //println!("{}        {}", self.time, (value * 2. * PI));
        //println!("value: {}", value);
        //println!("light: {}", (value * PI * 0.5).sin());
        //println!("time: {}", self.time);

        //Color::rgba(0, 0, 0, (((value * PI * 2.).sin() * 255.) / 2.) as u8)
        value = (value * PI * 0.5).sin() * MAX_DARK;
        if value < MAX_DARK / 2. {
            value -= ((value - (MAX_DARK / 2.)).abs().sqrt() * 15.).min(value);
        }

        Color::rgba(0, 0, 0, value as u8)
    }
}

impl Drawable for DayNight {
    fn draw<'se, 'tex, 'sh, 'shte>(
                                   &'se self,
                                   target: &mut RenderTarget,
                                   states: RenderStates<'tex, 'sh, 'shte>)
        where 'se: 'sh
    {
        let old_view = target.view().to_owned();

        let size = target.size();
        let mut view = target.view().to_owned();
        view.set_center2f(size.x as f32 / 2., size.y as f32 / 2.);
        target.set_view(&view);

        target.draw_rectangle_shape(&self.fader, states);

        target.set_view(&old_view);
    }
}

impl ResizeHandler for DayNight {
    fn on_resize(&mut self, width: u32, height: u32) {
        let width = width as f32;
        let height = height as f32;

        self.fader.set_size2f(width, height);
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::rc::Rc;
use std::collections::HashMap;
use std::path::Path;
use log;
use serde_json;
use rand;
use rand::Rng;
use regex::{Captures, Regex};
use directives;
use highlighter::{self, SyntaxHighlighter};
use markdown;
use page::{Page, Slug};
use parse::{Node, NodeValue, Parser};
use toctree::TocTree;

pub enum PlaceholderAction {
    Path,
    Title,
}

#[derive(Debug)]
pub struct RefDef {
    pub title: String,
    pub slug: Slug,
}

impl RefDef {
    pub fn new(title: &str, slug: &Slug) -> Self {
        RefDef {
            title: title.to_owned(),
            slug: slug.to_owned(),
        }
    }
}

pub enum StoredValue {
    Directive(Box<directives::DirectiveHandler>),
    Node(Node),
}

pub struct Evaluator {
    current_slug: Option<Slug>,

    pub parser: Parser,
    pub markdown: markdown::MarkdownRenderer,
    pub highlighter: SyntaxHighlighter,

    prelude_ctx: HashMap<String, Rc<StoredValue>>,
    pub ctx: HashMap<String, Rc<StoredValue>>,
    pub refdefs: HashMap<String, RefDef>,
    pub theme_config: serde_json::map::Map<String, serde_json::Value>,
    pub toctree: TocTree,

    placeholder_pattern: Regex,
    placeholder_prefix: String,
    pub pending_links: Vec<(PlaceholderAction, String)>,
}

impl Evaluator {
    #[allow(dead_code)]
    pub fn new() -> Self {
        Self::new_with_options(highlighter::DEFAULT_SYNTAX_THEME)
    }

    pub fn new_with_options(syntax_theme: &str) -> Self {
        let hex_chars = b"0123456789abcdef";
        let mut rnd_buf = [0u8; 16];
        rand::thread_rng().fill_bytes(&mut rnd_buf);
        let mut placeholder_prefix = String::with_capacity(32);
        for c in &rnd_buf {
            placeholder_prefix.push(hex_chars[(c >> 4) as usize] as char);
            placeholder_prefix.push(hex_chars[(c & 15) as usize] as char);
        }

        let pattern_text = format!(r"%{}-(\d+)%", &placeholder_prefix);
        let placeholder_pattern =
            Regex::new(&pattern_text).expect("Failed to compile linker pattern");

        Evaluator {
            current_slug: None,
            parser: Parser::new(),
            markdown: markdown::MarkdownRenderer::new(),
            highlighter: SyntaxHighlighter::new(syntax_theme),
            prelude_ctx: HashMap::new(),
            ctx: HashMap::new(),
            refdefs: HashMap::new(),
            theme_config: serde_json::map::Map::new(),
            toctree: TocTree::new(Slug::new("index".to_owned()), true),

            placeholder_pattern,
            placeholder_prefix,
            pending_links: vec![],
        }
    }

    pub fn register_prelude<S: Into<String>>(
        &mut self,
        name: S,
        handler: Box<directives::DirectiveHandler>,
    ) {
        self.prelude_ctx
            .insert(name.into(), Rc::new(StoredValue::Directive(handler)));
    }

    pub fn register<S: Into<String>>(
        &mut self,
        name: S,
        handler: Box<directives::DirectiveHandler>,
    ) {
        self.ctx
            .insert(name.into(), Rc::new(StoredValue::Directive(handler)));
    }

    pub fn evaluate(&mut self, node: &Node) -> String {
        match node.value {
            NodeValue::Owned(ref s) => s.to_owned(),
            NodeValue::Children(ref children) => if let Some(first_element) = children.get(0) {
                let directive_name = match first_element.value {
                    NodeValue::Owned(ref dname) => Cow::Borrowed(dname),
                    NodeValue::Children(_) => Cow::Owned(self.evaluate(first_element)),
                };

                self.lookup(node, directive_name.as_ref(), &children[1..])
                    .unwrap_or_else(|_| "".to_owned())
            } else {
                "".to_owned()
            },
        }
    }

    pub fn log(&self, node: &Node, message: &str, level: log::LogLevel) {
        let file_path = self.parser.get_node_source_path(node);
        log!(
            level,
            "{}\n  --> {}:?:?",
            message,
            file_path.unwrap_or_else(|| Path::new("")).to_string_lossy()
        );
    }

    #[allow(dead_code)]
    pub fn warn(&self, node: &Node, message: &str) {
        self.log(node, message, log::LogLevel::Warn);
    }

    pub fn error(&self, node: &Node, message: &str) {
        self.log(node, message, log::LogLevel::Error);
    }

    pub fn reset(&mut self) {
        self.ctx.clear();
        self.theme_config.clear();
    }

    pub fn set_slug(&mut self, slug: Slug) {
        self.current_slug = Some(slug);
    }

    pub fn get_slug(&self) -> &Slug {
        self.current_slug
            .as_ref()
            .expect("Requested slug before set")
    }

    pub fn get_placeholder(&mut self, refid: String, action: PlaceholderAction) -> String {
        self.pending_links.push((action, refid));
        format!(
            "%{}-{}%",
            self.placeholder_prefix,
            self.pending_links.len() - 1
        )
    }

    pub fn substitute(&self, page: &Page) -> Result<String, ()> {
        let result = self.placeholder_pattern
            .replace_all(&page.body, |captures: &Captures| {
                let ref_number = str::parse::<u64>(&captures[1]).expect("Failed to parse refid");
                let &(ref action, ref refid) = self.pending_links
                    .get(ref_number as usize)
                    .expect("Missing ref number");
                let refdef = match self.refdefs.get(refid) {
                    Some(r) => r,
                    None => {
                        return "unknown refdef".to_owned();
                    }
                };

                match *action {
                    PlaceholderAction::Path => page.slug.path_to(&refdef.slug, true),
                    PlaceholderAction::Title => refdef.title.to_owned(),
                }
            });

        Ok(result.into_owned())
    }

    pub fn lookup(&mut self, node: &Node, key: &str, args: &[Node]) -> Result<String, ()> {
        let stored = match self.ctx.get(key).or_else(|| self.prelude_ctx.get(key)) {
            Some(val) => Rc::clone(val),
            None => {
                self.error(node, &format!("Unknown name: '{}'", key));
                return Err(());
            }
        };

        match *stored {
            StoredValue::Node(ref stored_node) => Ok(self.evaluate(stored_node)),
            StoredValue::Directive(ref handler) => handler.handle(self, args),
        }
    }
}
<commit_msg>Report evaluation errors rather than silently eating them<commit_after>use std::borrow::Cow;
use std::rc::Rc;
use std::collections::HashMap;
use std::path::Path;
use log;
use serde_json;
use rand;
use rand::Rng;
use regex::{Captures, Regex};
use directives;
use highlighter::{self, SyntaxHighlighter};
use markdown;
use page::{Page, Slug};
use parse::{Node, NodeValue, Parser};
use toctree::TocTree;

pub enum PlaceholderAction {
    Path,
    Title,
}

#[derive(Debug)]
pub struct RefDef {
    pub title: String,
    pub slug: Slug,
}

impl RefDef {
    pub fn new(title: &str, slug: &Slug) -> Self {
        RefDef {
            title: title.to_owned(),
            slug: slug.to_owned(),
        }
    }
}

pub enum StoredValue {
    Directive(Box<directives::DirectiveHandler>),
    Node(Node),
}

pub struct Evaluator {
    current_slug: Option<Slug>,

    pub parser: Parser,
    pub markdown: markdown::MarkdownRenderer,
    pub highlighter: SyntaxHighlighter,

    prelude_ctx: HashMap<String, Rc<StoredValue>>,
    pub ctx: HashMap<String, Rc<StoredValue>>,
    pub refdefs: HashMap<String, RefDef>,
    pub theme_config: serde_json::map::Map<String, serde_json::Value>,
    pub toctree: TocTree,

    placeholder_pattern: Regex,
    placeholder_prefix: String,
    pub pending_links: Vec<(PlaceholderAction, String)>,
}

impl Evaluator {
    #[allow(dead_code)]
    pub fn new() -> Self {
        Self::new_with_options(highlighter::DEFAULT_SYNTAX_THEME)
    }

    pub fn new_with_options(syntax_theme: &str) -> Self {
        let hex_chars = b"0123456789abcdef";
        let mut rnd_buf = [0u8; 16];
        rand::thread_rng().fill_bytes(&mut rnd_buf);
        let mut placeholder_prefix = String::with_capacity(32);
        for c in &rnd_buf {
            placeholder_prefix.push(hex_chars[(c >> 4) as usize] as char);
            placeholder_prefix.push(hex_chars[(c & 15) as usize] as char);
        }

        let pattern_text = format!(r"%{}-(\d+)%", &placeholder_prefix);
        let placeholder_pattern =
            Regex::new(&pattern_text).expect("Failed to compile linker pattern");

        Evaluator {
            current_slug: None,
            parser: Parser::new(),
            markdown: markdown::MarkdownRenderer::new(),
            highlighter: SyntaxHighlighter::new(syntax_theme),
            prelude_ctx: HashMap::new(),
            ctx: HashMap::new(),
            refdefs: HashMap::new(),
            theme_config: serde_json::map::Map::new(),
            toctree: TocTree::new(Slug::new("index".to_owned()), true),

            placeholder_pattern,
            placeholder_prefix,
            pending_links: vec![],
        }
    }

    pub fn register_prelude<S: Into<String>>(
        &mut self,
        name: S,
        handler: Box<directives::DirectiveHandler>,
    ) {
        self.prelude_ctx
            .insert(name.into(), Rc::new(StoredValue::Directive(handler)));
    }

    pub fn register<S: Into<String>>(
        &mut self,
        name: S,
        handler: Box<directives::DirectiveHandler>,
    ) {
        self.ctx
            .insert(name.into(), Rc::new(StoredValue::Directive(handler)));
    }

    pub fn evaluate(&mut self, node: &Node) -> String {
        match node.value {
            NodeValue::Owned(ref s) => s.to_owned(),
            NodeValue::Children(ref children) => if let Some(first_element) = children.get(0) {
                let directive_name = match first_element.value {
                    NodeValue::Owned(ref dname) => Cow::Borrowed(dname),
                    NodeValue::Children(_) => Cow::Owned(self.evaluate(first_element)),
                };

                match self.lookup(node, directive_name.as_ref(), &children[1..]) {
                    Ok(s) => s,
                    Err(_) => {
                        self.error(node, "Error evaluating node");
                        String::new()
                    }
                }
            } else {
                "".to_owned()
            },
        }
    }

    pub fn log(&self, node: &Node, message: &str, level: log::LogLevel) {
        let file_path = self.parser.get_node_source_path(node);
        log!(
            level,
            "{}\n  --> {}:?:?",
            message,
            file_path.unwrap_or_else(|| Path::new("")).to_string_lossy()
        );
    }

    #[allow(dead_code)]
    pub fn warn(&self, node: &Node, message: &str) {
        self.log(node, message, log::LogLevel::Warn);
    }

    pub fn error(&self, node: &Node, message: &str) {
        self.log(node, message, log::LogLevel::Error);
    }

    pub fn reset(&mut self) {
        self.ctx.clear();
        self.theme_config.clear();
    }

    pub fn set_slug(&mut self, slug: Slug) {
        self.current_slug = Some(slug);
    }

    pub fn get_slug(&self) -> &Slug {
        self.current_slug
            .as_ref()
            .expect("Requested slug before set")
    }

    pub fn get_placeholder(&mut self, refid: String, action: PlaceholderAction) -> String {
        self.pending_links.push((action, refid));
        format!(
            "%{}-{}%",
            self.placeholder_prefix,
            self.pending_links.len() - 1
        )
    }

    pub fn substitute(&self, page: &Page) -> Result<String, ()> {
        let result = self.placeholder_pattern
            .replace_all(&page.body, |captures: &Captures| {
                let ref_number = str::parse::<u64>(&captures[1]).expect("Failed to parse refid");
                let &(ref action, ref refid) = self.pending_links
                    .get(ref_number as usize)
                    .expect("Missing ref number");
                let refdef = match self.refdefs.get(refid) {
                    Some(r) => r,
                    None => {
                        return "unknown refdef".to_owned();
                    }
                };

                match *action {
                    PlaceholderAction::Path => page.slug.path_to(&refdef.slug, true),
                    PlaceholderAction::Title => refdef.title.to_owned(),
                }
            });

        Ok(result.into_owned())
    }

    pub fn lookup(&mut self, node: &Node, key: &str, args: &[Node]) -> Result<String, ()> {
        let stored = match self.ctx.get(key).or_else(|| self.prelude_ctx.get(key)) {
            Some(val) => Rc::clone(val),
            None => {
                self.error(node, &format!("Unknown name: '{}'", key));
                return Err(());
            }
        };

        match *stored {
            StoredValue::Node(ref stored_node) => Ok(self.evaluate(stored_node)),
            StoredValue::Directive(ref handler) => handler.handle(self, args),
        }
    }
}
<|endoftext|>


<commit_before>// (c) 2016 Productize SPRL <joost@productize.be>

use std::io;

use Sexp;
use symbolic_expressions::Result;
use symbolic_expressions::Formatter;

// custom symbolic_expressions formatter that aims to be
// kicad compatible

struct Indent2 {
    newline_before:i64,
    closing_on_newline:bool,
    newline_after:i64,
}

impl Indent2 {
    fn before() -> Indent2 {
        Indent2 {
            newline_before:1,
            closing_on_newline:false,
            newline_after:0,
        }
    }
    fn before_after() -> Indent2 {
        Indent2 {
            newline_before:1,
            closing_on_newline:true,
            newline_after:0,
        }
    }
    fn before_double() -> Indent2 {
        Indent2 {
            newline_before:2,
            closing_on_newline:false,
            newline_after:0,
        }
    }
    fn before_double_after() -> Indent2 {
        Indent2 {
            newline_before:2,
            closing_on_newline:true,
            newline_after:0,
        }
    }
    fn before_double_after_double() -> Indent2 {
        Indent2 {
            newline_before:2,
            closing_on_newline:true,
            newline_after:1,
        }
    }
}

pub struct KicadFormatter {
    indent:i64,
    stack:Vec<Option<(String,Option<Indent2>)>>,
    ind:Vec<u8>,
    pts_xy_count:i64,
}

impl KicadFormatter {

    pub fn new(initial_indent_level:i64) -> KicadFormatter {
        KicadFormatter {
            indent:initial_indent_level,
            stack:vec![],
            ind:vec![b' ',b' '], // two spaces
            pts_xy_count:0,
        }
    }

    fn is(&self, what:&'static str) -> bool {
        for x in &self.stack {
            if let Some((ref name,_)) = *x {
                if name == what {
                    return true
                }
            }
        }
        false
    }

    fn parent_is(&self, what:&'static str) -> bool {
        if let Some(s) = self.stack.last() {
            if let Some((ref t,_)) = *s {
                return t == what
            }
        }
        false
    }

    fn indent<W:io::Write>(&self, writer:&mut W, nls:i64) -> Result<()> {
        for _ in 0..nls {
            try!(writer.write_all(b"\n"));
        }
        for _ in 0..self.indent {
            try!(writer.write_all(&self.ind));
        }
        Ok(())
    }

    fn indent_plus<W:io::Write>(&mut self, writer:&mut W, nls:i64) -> Result<()> {
        self.indent+=1;
        let res = self.indent(writer, nls);
        self.indent-=1;
        res
    }

    fn want_indent_module(&self, ele:&str) -> Option<Indent2> {
        //if !self.is("module") {
        //    return None
        //}
        if self.parent_is("module") {
            match ele {
                "at" | "descr" | "fp_line" | "fp_poly" |
                "pad" => return Some(Indent2::before()),
                "model" | "fp_text" => return Some(Indent2::before_after()),
                _ => (),
            }
        }
        if self.parent_is("fp_text") | self.parent_is("gr_text") {
            if let "effects" = ele {
                return Some(Indent2::before())
            }
        }
        if self.parent_is("pts") {
            if let "xy" = ele {
                if self.pts_xy_count > 0 && self.pts_xy_count % 4 == 0 {
                    return Some(Indent2::before())
                }
            }
        }
        if self.parent_is("model") {
            match ele {
                "at" | "scale" | "rotate" => {
                    return Some(Indent2::before())
                },
                _ => (),
            }
        }
        None
    }

    fn want_indent_layout(&self, ele:&str, e:&Sexp) -> Option<Indent2> {
        if !self.is("kicad_pcb") {
            return None
        }
        if self.parent_is("kicad_pcb") {
            match ele {
                "page" |
                "module" |
                "gr_arc"  | "gr_circle"
                    => return Some(Indent2::before_double()),
                "net" | "gr_line" | "segment" | "via"
                    => return Some(Indent2::before()),
                "layers" | "gr_text" | "dimension" | "zone"
                    => return Some(Indent2::before_after()),
                "setup"
                    => return Some(Indent2::before_double_after_double()),
                "general" | "net_class"
                    => return Some(Indent2::before_double_after()),
                _ => (),
            }
        }
        if self.parent_is("general") {
            return Some(Indent2::before())
        }
        if self.parent_is("layers") {
            return Some(Indent2::before())
        }
        if self.parent_is("setup") {
            return Some(Indent2::before())
        }
        if self.parent_is("pcbplotparams") {
            return Some(Indent2::before())
        }
        if self.parent_is("net_class") {
            return Some(Indent2::before())
        }
        if self.parent_is("dimension") {
            match ele {
                "gr_text" | "feature1" |
                "feature2" | "crossbar" |
                "arrow1a" | "arrow1b" |
                "arrow2a" | "arrow2b" => {
                    return Some(Indent2::before())
                },
                _ => (),
            }
        }
        if self.parent_is("zone") {
            match ele {
                "connect_pads" | "min_thickness" | "fill" |
                "polygon" | "filled_polygon"
                    => return Some(Indent2::before()),
                _ => (),
            }
        }
        if self.parent_is("polygon") | self.parent_is("filled_polygon") {
            return Some(Indent2::before())
        }
        None
    }

    fn want_indent(&self, value:&Sexp) -> Option<Indent2> {
        let first = match *value {
            Sexp::List(ref l) => {
                if l.is_empty() {
                    return None
                }
                (&l[0]).clone()
            },
            Sexp::Empty => return None,
            Sexp::String(ref l) => Sexp::String(l.clone()),
        };
        if let Sexp::String(ref ele) = first {
            let i = self.want_indent_module(ele);
            if i.is_some() {
                return i
            }
            let i = self.want_indent_layout(ele, value);
            if i.is_some() {
                return i
            }
        }
        None
    }
}

impl Formatter for KicadFormatter {

    fn open<W>(&mut self, writer: &mut W, value:Option<&Sexp>) -> Result<()>
        where W: io::Write
    {
        let mut ele = String::new();
        // if first element is string
        if let Some(ref sexp) = value {
            if let Sexp::String(ref s) = **sexp {
                ele.push_str(s);
            }
        }
        let exp = Sexp::String(ele.clone());
        let want_indent = self.want_indent(&exp);
        if let Some(ref want_indent) = want_indent {
            self.indent += 1;
            if want_indent.newline_before > 0 {
                try!(self.indent(writer, want_indent.newline_before));
            }
        }

        // special handling for breaking after 4 elements of xy
        if let "pts" = &ele[..] {
            self.pts_xy_count = 0;
        }
        if self.parent_is("pts") {
            if let "xy" = &ele[..] {
                self.pts_xy_count += 1;
                if self.pts_xy_count == 5 {
                    self.pts_xy_count = 1;
                }
            }
        }

        if !ele.is_empty() {
            self.stack.push(Some((ele, want_indent)))
        } else {
            self.stack.push(None)
        }
        writer.write_all(b"(").map_err(From::from)
    }

    fn element<W>(&mut self, writer: &mut W, value:&Sexp) -> Result<()>
        where W: io::Write
    {
        // get rid of the space if we will be putting a newline next
        if self.want_indent(value).is_none() {
            try!(writer.write_all(b" "));
        } else if let Sexp::String(_) = *value {
            try!(writer.write_all(b" "));
        }
        Ok(())

    }

    fn close<W>(&mut self, writer: &mut W) -> Result<()>
        where W: io::Write
    {
        if let Some(Some((s, want_indent))) = self.stack.pop() {
            if let Some(indent) = want_indent {
                self.indent -= 1;
                if indent.closing_on_newline {
                    try!(self.indent_plus(writer, 1));
                }
                // special handling of toplevel module...
                // which doesn't work, because it is not indented
                if &s == "module" && self.stack.is_empty() {
                    try!(writer.write_all(b"\n"));
                }
                try!(writer.write_all(b")"));
                for _ in 0..indent.newline_after {
                    try!(writer.write_all(b"\n"));
                }
                return Ok(())
            } else {
                if self.stack.is_empty() && &s == "module" {
                    try!(writer.write_all(b"\n"));
                }
            }
        }
        try!(writer.write_all(b")"));
        Ok(())
    }
}

<commit_msg>cleanup<commit_after>// (c) 2016 Productize SPRL <joost@productize.be>

use std::io;

use Sexp;
use symbolic_expressions::Result;
use symbolic_expressions::Formatter;

// custom symbolic_expressions formatter that aims to be
// kicad compatible

struct Indent {
    newline_before:i64,
    closing_on_newline:bool,
    newline_after:i64,
}

impl Indent {
    fn before() -> Indent {
        Indent {
            newline_before:1,
            closing_on_newline:false,
            newline_after:0,
        }
    }
    fn before_after() -> Indent {
        Indent {
            newline_before:1,
            closing_on_newline:true,
            newline_after:0,
        }
    }
    fn before_double() -> Indent {
        Indent {
            newline_before:2,
            closing_on_newline:false,
            newline_after:0,
        }
    }
    fn before_double_after() -> Indent {
        Indent {
            newline_before:2,
            closing_on_newline:true,
            newline_after:0,
        }
    }
    fn before_double_after_double() -> Indent {
        Indent {
            newline_before:2,
            closing_on_newline:true,
            newline_after:1,
        }
    }
}

pub struct KicadFormatter {
    indent:i64,
    stack:Vec<Option<(String,Option<Indent>)>>,
    ind:Vec<u8>,
    pts_xy_count:i64,
}

impl KicadFormatter {

    pub fn new(initial_indent_level:i64) -> KicadFormatter {
        KicadFormatter {
            indent:initial_indent_level,
            stack:vec![],
            ind:vec![b' ',b' '], // two spaces
            pts_xy_count:0,
        }
    }

    fn is(&self, what:&'static str) -> bool {
        for x in &self.stack {
            if let Some((ref name,_)) = *x {
                if name == what {
                    return true
                }
            }
        }
        false
    }

    fn parent_is(&self, what:&'static str) -> bool {
        if let Some(s) = self.stack.last() {
            if let Some((ref t,_)) = *s {
                return t == what
            }
        }
        false
    }

    fn indent<W:io::Write>(&self, writer:&mut W, nls:i64) -> Result<()> {
        for _ in 0..nls {
            try!(writer.write_all(b"\n"));
        }
        for _ in 0..self.indent {
            try!(writer.write_all(&self.ind));
        }
        Ok(())
    }

    fn indent_plus<W:io::Write>(&mut self, writer:&mut W, nls:i64) -> Result<()> {
        self.indent+=1;
        let res = self.indent(writer, nls);
        self.indent-=1;
        res
    }

    fn want_indent_module(&self, ele:&str) -> Option<Indent> {
        //if !self.is("module") {
        //    return None
        //}
        if self.parent_is("module") {
            match ele {
                "at" | "descr" | "fp_line" | "fp_poly" |
                "pad" => return Some(Indent::before()),
                "model" | "fp_text" => return Some(Indent::before_after()),
                _ => (),
            }
        }
        if self.parent_is("fp_text") | self.parent_is("gr_text") {
            if let "effects" = ele {
                return Some(Indent::before())
            }
        }
        if self.parent_is("pts") {
            if let "xy" = ele {
                if self.pts_xy_count > 0 && self.pts_xy_count % 4 == 0 {
                    return Some(Indent::before())
                }
            }
        }
        if self.parent_is("model") {
            match ele {
                "at" | "scale" | "rotate" => {
                    return Some(Indent::before())
                },
                _ => (),
            }
        }
        None
    }

    fn want_indent_layout(&self, ele:&str) -> Option<Indent> {
        if !self.is("kicad_pcb") {
            return None
        }
        if self.parent_is("kicad_pcb") {
            match ele {
                "page" |
                "module" |
                "gr_arc"  | "gr_circle"
                    => return Some(Indent::before_double()),
                "net" | "gr_line" | "segment" | "via"
                    => return Some(Indent::before()),
                "layers" | "gr_text" | "dimension" | "zone"
                    => return Some(Indent::before_after()),
                "setup"
                    => return Some(Indent::before_double_after_double()),
                "general" | "net_class"
                    => return Some(Indent::before_double_after()),
                _ => (),
            }
        }
        if self.parent_is("general") {
            return Some(Indent::before())
        }
        if self.parent_is("layers") {
            return Some(Indent::before())
        }
        if self.parent_is("setup") {
            return Some(Indent::before())
        }
        if self.parent_is("pcbplotparams") {
            return Some(Indent::before())
        }
        if self.parent_is("net_class") {
            return Some(Indent::before())
        }
        if self.parent_is("dimension") {
            match ele {
                "gr_text" | "feature1" |
                "feature2" | "crossbar" |
                "arrow1a" | "arrow1b" |
                "arrow2a" | "arrow2b" => {
                    return Some(Indent::before())
                },
                _ => (),
            }
        }
        if self.parent_is("zone") {
            match ele {
                "connect_pads" | "min_thickness" | "fill" |
                "polygon" | "filled_polygon"
                    => return Some(Indent::before()),
                _ => (),
            }
        }
        if self.parent_is("polygon") | self.parent_is("filled_polygon") {
            return Some(Indent::before())
        }
        None
    }

    fn want_indent(&self, value:&Sexp) -> Option<Indent> {
        let first = match *value {
            Sexp::List(ref l) => {
                if l.is_empty() {
                    return None
                }
                (&l[0]).clone()
            },
            Sexp::Empty => return None,
            Sexp::String(ref l) => Sexp::String(l.clone()),
        };
        if let Sexp::String(ref ele) = first {
            let i = self.want_indent_module(ele);
            if i.is_some() {
                return i
            }
            let i = self.want_indent_layout(ele);
            if i.is_some() {
                return i
            }
        }
        None
    }
}

impl Formatter for KicadFormatter {

    fn open<W>(&mut self, writer: &mut W, value:Option<&Sexp>) -> Result<()>
        where W: io::Write
    {
        let mut ele = String::new();
        // if first element is string
        if let Some(ref sexp) = value {
            if let Sexp::String(ref s) = **sexp {
                ele.push_str(s);
            }
        }
        let exp = Sexp::String(ele.clone());
        let want_indent = self.want_indent(&exp);
        if let Some(ref want_indent) = want_indent {
            self.indent += 1;
            if want_indent.newline_before > 0 {
                try!(self.indent(writer, want_indent.newline_before));
            }
        }

        // special handling for breaking after 4 elements of xy
        if let "pts" = &ele[..] {
            self.pts_xy_count = 0;
        }
        if self.parent_is("pts") {
            if let "xy" = &ele[..] {
                self.pts_xy_count += 1;
                if self.pts_xy_count == 5 {
                    self.pts_xy_count = 1;
                }
            }
        }

        if !ele.is_empty() {
            self.stack.push(Some((ele, want_indent)))
        } else {
            self.stack.push(None)
        }
        writer.write_all(b"(").map_err(From::from)
    }

    fn element<W>(&mut self, writer: &mut W, value:&Sexp) -> Result<()>
        where W: io::Write
    {
        // get rid of the space if we will be putting a newline next
        if self.want_indent(value).is_none() {
            try!(writer.write_all(b" "));
        } else if let Sexp::String(_) = *value {
            try!(writer.write_all(b" "));
        }
        Ok(())

    }

    fn close<W>(&mut self, writer: &mut W) -> Result<()>
        where W: io::Write
    {
        if let Some(Some((s, want_indent))) = self.stack.pop() {
            if let Some(indent) = want_indent {
                self.indent -= 1;
                if indent.closing_on_newline {
                    try!(self.indent_plus(writer, 1));
                }
                // special handling of toplevel module...
                // which doesn't work, because it is not indented
                if &s == "module" && self.stack.is_empty() {
                    try!(writer.write_all(b"\n"));
                }
                try!(writer.write_all(b")"));
                for _ in 0..indent.newline_after {
                    try!(writer.write_all(b"\n"));
                }
                return Ok(())
            } else {
                if self.stack.is_empty() && &s == "module" {
                    try!(writer.write_all(b"\n"));
                }
            }
        }
        try!(writer.write_all(b")"));
        Ok(())
    }
}

<|endoftext|>