<commit_before>use crate::model::{AnnoLichess, GameId, GameInfo, PersonalEntry, PersonalKey, PersonalKeyPrefix};
use rocksdb::{
    BlockBasedIndexType, BlockBasedOptions, ColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode,
    Direction, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform, DB,
};
use std::{io::Cursor, path::Path};

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

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 mut personal_opts = Options::default();
        personal_opts.set_merge_operator_associative("personal merge", personal_merge);
        personal_opts.set_prefix_extractor(SliceTransform::create_fixed_prefix(16));

        let mut game_opts = Options::default();
        game_opts.set_merge_operator_associative("game merge", game_merge);
        game_opts.set_prefix_extractor(SliceTransform::create_noop());
        let mut game_block_opts = BlockBasedOptions::default();
        game_block_opts.set_index_type(BlockBasedIndexType::HashSearch);
        game_block_opts.set_block_size(1024);
        game_opts.set_block_based_table_factory(&game_block_opts);

        let inner = DBWithThreadMode::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                ColumnFamilyDescriptor::new("personal", personal_opts),
                ColumnFamilyDescriptor::new("game", game_opts),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn queryable(&self) -> QueryableDatabase<'_> {
        QueryableDatabase {
            db: &self.inner,
            cf_personal: self.inner.cf_handle("personal").expect("cf personal"),
            cf_game: self.inner.cf_handle("game").expect("cf game"),
        }
    }
}

pub struct QueryableDatabase<'a> {
    pub db: &'a DB,
    pub cf_personal: &'a ColumnFamily,
    pub cf_game: &'a ColumnFamily,
}

impl QueryableDatabase<'_> {
    pub fn db_property(&self, name: &str) -> Result<Option<String>, rocksdb::Error> {
        self.db.property_value(name)
    }

    pub fn game_property(&self, name: &str) -> Result<Option<String>, rocksdb::Error> {
        self.db.property_value_cf(self.cf_game, name)
    }

    pub fn personal_property(&self, name: &str) -> Result<Option<String>, rocksdb::Error> {
        self.db.property_value_cf(self.cf_personal, name)
    }

    pub fn merge_game_info(&self, id: GameId, info: GameInfo) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::new());
        info.write(&mut cursor).expect("serialize game info");
        self.db
            .merge_cf(self.cf_game, id.to_bytes(), cursor.into_inner())
    }

    pub fn get_game_info(&self, id: GameId) -> Result<Option<GameInfo>, rocksdb::Error> {
        Ok(self.db.get_cf(self.cf_game, id.to_bytes())?.map(|buf| {
            let mut cursor = Cursor::new(buf);
            GameInfo::read(&mut cursor).expect("deserialize game info")
        }))
    }

    pub fn merge_personal(
        &self,
        key: PersonalKey,
        entry: PersonalEntry,
    ) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::new());
        entry.write(&mut cursor).expect("serialize personal entry");
        self.db
            .merge_cf(self.cf_personal, key.into_bytes(), cursor.into_inner())
    }

    pub fn get_personal(
        &self,
        key: PersonalKeyPrefix,
        since: AnnoLichess,
    ) -> Result<PersonalEntry, rocksdb::Error> {
        let mut entry = PersonalEntry::default();

        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        let iterator = self.db.iterator_cf_opt(
            self.cf_personal,
            opt,
            IteratorMode::From(&key.with_year(since).into_bytes(), Direction::Forward),
        );

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

        Ok(entry)
    }
}

fn 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<GameInfo> = None;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = GameInfo::read(&mut cursor).expect("read for game merge");
        if let Some(old_info) = info {
            new_info.indexed.white |= old_info.indexed.white;
            new_info.indexed.black |= old_info.indexed.black;
        }
        info = Some(new_info);
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::new());
        info.write(&mut cursor).expect("write game");
        cursor.into_inner()
    })
}

fn personal_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PersonalEntry::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 personal merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write personal entry");
    Some(cursor.into_inner())
}
<commit_msg>add block bloom filters<commit_after>use crate::model::{AnnoLichess, GameId, GameInfo, PersonalEntry, PersonalKey, PersonalKeyPrefix};
use rocksdb::{
    BlockBasedIndexType, BlockBasedOptions, ColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode,
    Direction, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform, DB,
};
use std::{io::Cursor, path::Path};

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

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 mut personal_opts = Options::default();
        personal_opts.set_merge_operator_associative("personal merge", personal_merge);
        personal_opts.set_prefix_extractor(SliceTransform::create_fixed_prefix(16));
        let mut personal_block_opts = BlockBasedOptions::default();
        personal_block_opts.set_index_type(BlockBasedIndexType::HashSearch);
        personal_block_opts.set_block_size(4 * 1024);
        personal_block_opts.set_bloom_filter(10, true);
        personal_opts.set_block_based_table_factory(&personal_block_opts);

        let mut game_opts = Options::default();
        game_opts.set_merge_operator_associative("game merge", game_merge);
        game_opts.set_prefix_extractor(SliceTransform::create_noop());
        let mut game_block_opts = BlockBasedOptions::default();
        game_block_opts.set_index_type(BlockBasedIndexType::HashSearch);
        game_block_opts.set_block_size(1024);
        game_opts.set_block_based_table_factory(&game_block_opts);

        let inner = DBWithThreadMode::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                ColumnFamilyDescriptor::new("personal", personal_opts),
                ColumnFamilyDescriptor::new("game", game_opts),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn queryable(&self) -> QueryableDatabase<'_> {
        QueryableDatabase {
            db: &self.inner,
            cf_personal: self.inner.cf_handle("personal").expect("cf personal"),
            cf_game: self.inner.cf_handle("game").expect("cf game"),
        }
    }
}

pub struct QueryableDatabase<'a> {
    pub db: &'a DB,
    pub cf_personal: &'a ColumnFamily,
    pub cf_game: &'a ColumnFamily,
}

impl QueryableDatabase<'_> {
    pub fn db_property(&self, name: &str) -> Result<Option<String>, rocksdb::Error> {
        self.db.property_value(name)
    }

    pub fn game_property(&self, name: &str) -> Result<Option<String>, rocksdb::Error> {
        self.db.property_value_cf(self.cf_game, name)
    }

    pub fn personal_property(&self, name: &str) -> Result<Option<String>, rocksdb::Error> {
        self.db.property_value_cf(self.cf_personal, name)
    }

    pub fn merge_game_info(&self, id: GameId, info: GameInfo) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::new());
        info.write(&mut cursor).expect("serialize game info");
        self.db
            .merge_cf(self.cf_game, id.to_bytes(), cursor.into_inner())
    }

    pub fn get_game_info(&self, id: GameId) -> Result<Option<GameInfo>, rocksdb::Error> {
        Ok(self.db.get_cf(self.cf_game, id.to_bytes())?.map(|buf| {
            let mut cursor = Cursor::new(buf);
            GameInfo::read(&mut cursor).expect("deserialize game info")
        }))
    }

    pub fn merge_personal(
        &self,
        key: PersonalKey,
        entry: PersonalEntry,
    ) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::new());
        entry.write(&mut cursor).expect("serialize personal entry");
        self.db
            .merge_cf(self.cf_personal, key.into_bytes(), cursor.into_inner())
    }

    pub fn get_personal(
        &self,
        key: PersonalKeyPrefix,
        since: AnnoLichess,
    ) -> Result<PersonalEntry, rocksdb::Error> {
        let mut entry = PersonalEntry::default();

        let mut opt = ReadOptions::default();
        opt.set_prefix_same_as_start(true);
        let iterator = self.db.iterator_cf_opt(
            self.cf_personal,
            opt,
            IteratorMode::From(&key.with_year(since).into_bytes(), Direction::Forward),
        );

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

        Ok(entry)
    }
}

fn 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<GameInfo> = None;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = GameInfo::read(&mut cursor).expect("read for game merge");
        if let Some(old_info) = info {
            new_info.indexed.white |= old_info.indexed.white;
            new_info.indexed.black |= old_info.indexed.black;
        }
        info = Some(new_info);
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::new());
        info.write(&mut cursor).expect("write game");
        cursor.into_inner()
    })
}

fn personal_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PersonalEntry::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 personal merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write personal entry");
    Some(cursor.into_inner())
}
<|endoftext|>


<commit_before><commit_msg>Don't choke on messages containing newlines<commit_after><|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use fnv::FnvHashMap;
use std::hash::Hash;
use std::ops;

#[cfg(test)]
mod test;

pub struct SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    map: FnvHashMap<K, V>,
    undo_log: Vec<UndoLog<K, V>>,
}

pub struct Snapshot {
    len: usize
}

enum UndoLog<K, V> {
    OpenSnapshot,
    CommittedSnapshot,
    Inserted(K),
    Overwrite(K, V),
}

impl<K, V> SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    pub fn new() -> Self {
        SnapshotMap {
            map: FnvHashMap(),
            undo_log: vec![]
        }
    }

    pub fn insert(&mut self, key: K, value: V) -> bool {
        match self.map.insert(key.clone(), value) {
            None => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Inserted(key));
                }
                true
            }
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                false
            }
        }
    }

    pub fn remove(&mut self, key: K) -> bool {
        match self.map.remove(&key) {
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                true
            }
            None => {
                false
            }
        }
    }

    pub fn get(&self, key: &K) -> Option<&V> {
        self.map.get(key)
    }

    pub fn snapshot(&mut self) -> Snapshot {
        self.undo_log.push(UndoLog::OpenSnapshot);
        let len = self.undo_log.len() - 1;
        Snapshot { len: len }
    }

    fn assert_open_snapshot(&self, snapshot: &Snapshot) {
        assert!(snapshot.len < self.undo_log.len());
        assert!(match self.undo_log[snapshot.len] {
            UndoLog::OpenSnapshot => true,
            _ => false
        });
    }

    pub fn commit(&mut self, snapshot: Snapshot) {
        self.assert_open_snapshot(&snapshot);
        if snapshot.len == 0 {
            // The root snapshot.
            self.undo_log.truncate(0);
        } else {
            self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot;
        }
    }

    pub fn rollback_to(&mut self, snapshot: Snapshot) {
        self.assert_open_snapshot(&snapshot);
        while self.undo_log.len() > snapshot.len + 1 {
            match self.undo_log.pop().unwrap() {
                UndoLog::OpenSnapshot => {
                    panic!("cannot rollback an uncommitted snapshot");
                }

                UndoLog::CommittedSnapshot => { }

                UndoLog::Inserted(key) => {
                    self.map.remove(&key);
                }

                UndoLog::Overwrite(key, old_value) => {
                    self.map.insert(key, old_value);
                }
            }
        }

        let v = self.undo_log.pop().unwrap();
        assert!(match v { UndoLog::OpenSnapshot => true, _ => false });
        assert!(self.undo_log.len() == snapshot.len);
    }
}

impl<'k, K, V> ops::Index<&'k K> for SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    type Output = V;
    fn index(&self, key: &'k K) -> &V {
        &self.map[key]
    }
}
<commit_msg>Rollup merge of #37288 - srinivasreddy:snapshot_map, r=eddyb<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use fnv::FnvHashMap;
use std::hash::Hash;
use std::ops;

#[cfg(test)]
mod test;

pub struct SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    map: FnvHashMap<K, V>,
    undo_log: Vec<UndoLog<K, V>>,
}

pub struct Snapshot {
    len: usize,
}

enum UndoLog<K, V> {
    OpenSnapshot,
    CommittedSnapshot,
    Inserted(K),
    Overwrite(K, V),
}

impl<K, V> SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    pub fn new() -> Self {
        SnapshotMap {
            map: FnvHashMap(),
            undo_log: vec![],
        }
    }

    pub fn insert(&mut self, key: K, value: V) -> bool {
        match self.map.insert(key.clone(), value) {
            None => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Inserted(key));
                }
                true
            }
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                false
            }
        }
    }

    pub fn remove(&mut self, key: K) -> bool {
        match self.map.remove(&key) {
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                true
            }
            None => false,
        }
    }

    pub fn get(&self, key: &K) -> Option<&V> {
        self.map.get(key)
    }

    pub fn snapshot(&mut self) -> Snapshot {
        self.undo_log.push(UndoLog::OpenSnapshot);
        let len = self.undo_log.len() - 1;
        Snapshot { len: len }
    }

    fn assert_open_snapshot(&self, snapshot: &Snapshot) {
        assert!(snapshot.len < self.undo_log.len());
        assert!(match self.undo_log[snapshot.len] {
            UndoLog::OpenSnapshot => true,
            _ => false,
        });
    }

    pub fn commit(&mut self, snapshot: Snapshot) {
        self.assert_open_snapshot(&snapshot);
        if snapshot.len == 0 {
            // The root snapshot.
            self.undo_log.truncate(0);
        } else {
            self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot;
        }
    }

    pub fn rollback_to(&mut self, snapshot: Snapshot) {
        self.assert_open_snapshot(&snapshot);
        while self.undo_log.len() > snapshot.len + 1 {
            match self.undo_log.pop().unwrap() {
                UndoLog::OpenSnapshot => {
                    panic!("cannot rollback an uncommitted snapshot");
                }

                UndoLog::CommittedSnapshot => {}

                UndoLog::Inserted(key) => {
                    self.map.remove(&key);
                }

                UndoLog::Overwrite(key, old_value) => {
                    self.map.insert(key, old_value);
                }
            }
        }

        let v = self.undo_log.pop().unwrap();
        assert!(match v {
            UndoLog::OpenSnapshot => true,
            _ => false,
        });
        assert!(self.undo_log.len() == snapshot.len);
    }
}

impl<'k, K, V> ops::Index<&'k K> for SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    type Output = V;
    fn index(&self, key: &'k K) -> &V {
        &self.map[key]
    }
}
<|endoftext|>


<commit_before>//! Bounding boxes that know their coordinate space.

use crate::rect::Rect;
use crate::transform::Transform;

#[derive(Debug, Copy, Clone)]
pub struct BoundingBox {
    pub transform: Transform,
    pub rect: Option<Rect>,     // without stroke
    pub ink_rect: Option<Rect>, // with stroke
}

impl Default for BoundingBox {
    fn default() -> Self {
        BoundingBox {
            transform: Default::default(),
            rect: None,
            ink_rect: None,
        }
    }
}

impl BoundingBox {
    pub fn new() -> BoundingBox {
        Default::default()
    }

    pub fn with_transform(self, transform: Transform) -> BoundingBox {
        BoundingBox { transform, ..self }
    }

    pub fn with_rect(self, rect: Rect) -> BoundingBox {
        BoundingBox {
            rect: Some(rect),
            ..self
        }
    }

    pub fn with_ink_rect(self, ink_rect: Rect) -> BoundingBox {
        BoundingBox {
            ink_rect: Some(ink_rect),
            ..self
        }
    }

    pub fn clear(mut self) {
        self.rect = None;
        self.ink_rect = None;
    }

    fn combine(&mut self, src: &BoundingBox, clip: bool) {
        if src.rect.is_none() && src.ink_rect.is_none() {
            return;
        }

        // this will panic!() if it's not invertible... should we check on our own?
        let transform = self
            .transform
            .invert()
            .unwrap()
            .pre_transform(&src.transform);

        self.rect = combine_rects(self.rect, src.rect, &transform, clip);
        self.ink_rect = combine_rects(self.ink_rect, src.ink_rect, &transform, clip);
    }

    pub fn insert(&mut self, src: &BoundingBox) {
        self.combine(src, false);
    }

    pub fn clip(&mut self, src: &BoundingBox) {
        self.combine(src, true);
    }
}

fn combine_rects(
    r1: Option<Rect>,
    r2: Option<Rect>,
    transform: &Transform,
    clip: bool,
) -> Option<Rect> {
    match (r1, r2, clip) {
        (r1, None, _) => r1,
        (None, Some(r2), _) => Some(transform.transform_rect(&r2)),
        (Some(r1), Some(r2), true) => transform
            .transform_rect(&r2)
            .intersection(&r1)
            .or_else(|| Some(Rect::default())),
        (Some(r1), Some(r2), false) => Some(transform.transform_rect(&r2).union(&r1)),
    }
}

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

    #[test]
    fn combine() {
        let r1 = Rect::new(1.0, 2.0, 3.0, 4.0);
        let r2 = Rect::new(1.5, 2.5, 3.5, 4.5);
        let r3 = Rect::new(10.0, 11.0, 12.0, 13.0);
        let t = Transform::new(1.0, 0.0, 0.0, 1.0, 0.5, 0.5);

        let res = combine_rects(None, None, &t, true);
        assert_eq!(res, None);

        let res = combine_rects(None, None, &t, false);
        assert_eq!(res, None);

        let res = combine_rects(Some(r1), None, &t, true);
        assert_eq!(res, Some(r1));

        let res = combine_rects(Some(r1), None, &t, false);
        assert_eq!(res, Some(r1));

        let res = combine_rects(None, Some(r2), &t, true);
        assert_eq!(res, Some(Rect::new(2.0, 3.0, 4.0, 5.0)));

        let res = combine_rects(None, Some(r2), &t, false);
        assert_eq!(res, Some(Rect::new(2.0, 3.0, 4.0, 5.0)));

        let res = combine_rects(Some(r1), Some(r2), &t, true);
        assert_eq!(res, Some(Rect::new(2.0, 3.0, 3.0, 4.0)));

        let res = combine_rects(Some(r1), Some(r3), &t, true);
        assert_eq!(res, Some(Rect::default()));

        let res = combine_rects(Some(r1), Some(r2), &t, false);
        assert_eq!(res, Some(Rect::new(1.0, 2.0, 4.0, 5.0)));
    }
}
<commit_msg>Default impl for BoundingBox can be created by #[derive(Default)]<commit_after>//! Bounding boxes that know their coordinate space.

use crate::rect::Rect;
use crate::transform::Transform;

#[derive(Debug, Default, Copy, Clone)]
pub struct BoundingBox {
    pub transform: Transform,
    pub rect: Option<Rect>,     // without stroke
    pub ink_rect: Option<Rect>, // with stroke
}

impl BoundingBox {
    pub fn new() -> BoundingBox {
        Default::default()
    }

    pub fn with_transform(self, transform: Transform) -> BoundingBox {
        BoundingBox { transform, ..self }
    }

    pub fn with_rect(self, rect: Rect) -> BoundingBox {
        BoundingBox {
            rect: Some(rect),
            ..self
        }
    }

    pub fn with_ink_rect(self, ink_rect: Rect) -> BoundingBox {
        BoundingBox {
            ink_rect: Some(ink_rect),
            ..self
        }
    }

    pub fn clear(mut self) {
        self.rect = None;
        self.ink_rect = None;
    }

    fn combine(&mut self, src: &BoundingBox, clip: bool) {
        if src.rect.is_none() && src.ink_rect.is_none() {
            return;
        }

        // this will panic!() if it's not invertible... should we check on our own?
        let transform = self
            .transform
            .invert()
            .unwrap()
            .pre_transform(&src.transform);

        self.rect = combine_rects(self.rect, src.rect, &transform, clip);
        self.ink_rect = combine_rects(self.ink_rect, src.ink_rect, &transform, clip);
    }

    pub fn insert(&mut self, src: &BoundingBox) {
        self.combine(src, false);
    }

    pub fn clip(&mut self, src: &BoundingBox) {
        self.combine(src, true);
    }
}

fn combine_rects(
    r1: Option<Rect>,
    r2: Option<Rect>,
    transform: &Transform,
    clip: bool,
) -> Option<Rect> {
    match (r1, r2, clip) {
        (r1, None, _) => r1,
        (None, Some(r2), _) => Some(transform.transform_rect(&r2)),
        (Some(r1), Some(r2), true) => transform
            .transform_rect(&r2)
            .intersection(&r1)
            .or_else(|| Some(Rect::default())),
        (Some(r1), Some(r2), false) => Some(transform.transform_rect(&r2).union(&r1)),
    }
}

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

    #[test]
    fn combine() {
        let r1 = Rect::new(1.0, 2.0, 3.0, 4.0);
        let r2 = Rect::new(1.5, 2.5, 3.5, 4.5);
        let r3 = Rect::new(10.0, 11.0, 12.0, 13.0);
        let t = Transform::new(1.0, 0.0, 0.0, 1.0, 0.5, 0.5);

        let res = combine_rects(None, None, &t, true);
        assert_eq!(res, None);

        let res = combine_rects(None, None, &t, false);
        assert_eq!(res, None);

        let res = combine_rects(Some(r1), None, &t, true);
        assert_eq!(res, Some(r1));

        let res = combine_rects(Some(r1), None, &t, false);
        assert_eq!(res, Some(r1));

        let res = combine_rects(None, Some(r2), &t, true);
        assert_eq!(res, Some(Rect::new(2.0, 3.0, 4.0, 5.0)));

        let res = combine_rects(None, Some(r2), &t, false);
        assert_eq!(res, Some(Rect::new(2.0, 3.0, 4.0, 5.0)));

        let res = combine_rects(Some(r1), Some(r2), &t, true);
        assert_eq!(res, Some(Rect::new(2.0, 3.0, 3.0, 4.0)));

        let res = combine_rects(Some(r1), Some(r3), &t, true);
        assert_eq!(res, Some(Rect::default()));

        let res = combine_rects(Some(r1), Some(r2), &t, false);
        assert_eq!(res, Some(Rect::new(1.0, 2.0, 4.0, 5.0)));
    }
}
<|endoftext|>


<commit_before>fn main() {

	let args = std::os::args();
	println!("{}", args);

	let mut first = 0;
	let mut second = 1;
	let mut result = 0;

	for i in range (1, 30) {
		result = first + second;
		first = second;
		second = result;
		println!("{}: {}", i, result);
	}
}<commit_msg>Just adding a small comment for rust-based fibonacci.<commit_after>fn main() {
	//very simple non-recursive fibonacci
	//would be nice to have a problem that would be more efficient by threading, but oh well

	let args = std::os::args();
	println!("{}", args);

	let mut first = 0;
	let mut second = 1;
	let mut result = 0;

	for i in range (1, 30) {
		result = first + second;
		first = second;
		second = result;
		println!("{}: {}", i, result);
	}
}<|endoftext|>


<commit_before>use codec::{
    async_trait::async_trait,
    eyre::{bail, Result},
    stencila_schema::Node,
    utils::vec_string,
    Codec, CodecTrait, DecodeOptions, EncodeOptions,
};
use codec_json::JsonCodec;
use std::{fs, path::Path};

/// Encode and decode a document node to a reproducible PNG image.
///
/// This codec encodes the JSON representation of the node in a iTXt chunk of a PNG
/// image. You can use the tool `pnginfo` to examine the text chunks in the
/// encoded image e.g.
///
/// ```sh
/// pnginfo my.png
/// ```
///
/// For reference, there is a previous implementation of this codec, written
/// in Node.js at https://github.com/stencila/encoda/blob/v0.119.0/src/codecs/rpng/index.ts.
pub struct RpngCodec {}

#[async_trait]
impl CodecTrait for RpngCodec {
    fn spec() -> Codec {
        Codec {
            status: "alpha".to_string(),
            formats: vec_string!["rpng"],
            root_types: vec_string!["*"],
            from_string: true,
            from_path: true,
            to_string: true,
            to_path: true,
            ..Default::default()
        }
    }

    /// Decode a document node from a string
    ///
    /// This function scans the PNG for a `iTXt` chunk with a matching keyword and then delegates
    /// to the [`JsonCodec`] to decode it.
    fn from_str(content: &str, options: Option<DecodeOptions>) -> Result<Node> {
        // Remove any dataURI prefix
        let data = if let Some(data) = content.strip_prefix("data:image/png;base64,") {
            data
        } else {
            content
        };

        // Decode the Base64 to bytes and then a node
        let bytes = base64::decode(data)?;
        bytes_to_node(bytes.as_slice(), options)
    }

    /// Decode a document node from a file system path
    ///
    /// This override is necessary to read the file as bytes, not as a string, and then to
    /// directly decode those bytes, rather than Base64 decoding them first.
    /// Decode a document node from a file system path
    async fn from_path(path: &Path, options: Option<DecodeOptions>) -> Result<Node> {
        let bytes = fs::read(path)?;
        bytes_to_node(bytes.as_slice(), options)
    }

    /// Encode a document node to a string
    ///
    /// Returns a Base64 encoded dataURI (with media type `image/png`) and the node embedded as JSON.
    async fn to_string_async(node: &Node, options: Option<EncodeOptions>) -> Result<String> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        let string = ["data:image/png;base64,", &base64::encode(&bytes[0])].concat();
        Ok(string)
    }

    /// Encode a document node to a file system path
    ///
    /// This override is necessary to avoid the dataURI prefix and Base64 encoding that `to_string_async`
    /// does. It simply writes that bytes to a file at the path.
    async fn to_path(node: &Node, path: &Path, options: Option<EncodeOptions>) -> Result<()> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        fs::write(path, &bytes[0])?;
        Ok(())
    }
}

/// Decode bytes to a document node
fn bytes_to_node(bytes: &[u8], options: Option<DecodeOptions>) -> Result<Node> {
    // Decode the bytes to PNG and extract any matching iTXt chunk
    let decoder = png::Decoder::new(bytes);
    let reader = decoder.read_info()?;
    let info = reader.info();
    let json = info
        .utf8_text
        .iter()
        .find_map(|chunk| match chunk.keyword == "json" {
            true => {
                let mut chunk = chunk.clone();
                if chunk.decompress_text().is_err() {
                    return None;
                }
                chunk.get_text().ok()
            }
            false => None,
        });

    match json {
        Some(json) => codec_json::JsonCodec::from_str(&json, options),
        None => bail!("The PNG does not have an embedded Stencila node"),
    }
}

/// Encode a set of document nodes to RPNGs as bytes
///
/// As with the `nodes_to_bytes` function in the `codec-png` crate, this function
/// is based around creating multiple RPNGs, rather than a single one, to
/// reduce the per-image overhead of starting the browser, loading the theme etc.
pub async fn nodes_to_bytes(
    nodes: &[&Node],
    options: Option<EncodeOptions>,
) -> Result<Vec<Vec<u8>>> {
    // Generate the plain old PNGs
    let pngs = codec_png::nodes_to_bytes(
        nodes,
        Some(EncodeOptions {
            theme: "rpng".to_string(),
            ..options.unwrap_or_default()
        }),
    )
    .await?;

    // Transform each PNG into an RPNG...
    let mut rpngs = Vec::with_capacity(nodes.len());
    for index in 0..nodes.len() {
        // Encode the node to JSON for embedding in the PNG
        let json = JsonCodec::to_string(
            nodes[index],
            Some(EncodeOptions {
                theme: "compact".to_string(),
                ..Default::default()
            }),
        )?;

        // Encode the node to a PNG, as bytes, and decode the image to get its size etc
        let image_bytes = &pngs[index];
        let decoder = png::Decoder::new(image_bytes.as_slice());
        let mut reader = decoder.read_info()?;
        let mut image_data = vec![0; reader.output_buffer_size()];
        let image_info = reader.next_frame(&mut image_data)?;

        // Re-encode the PNG, as bytes, with JSON embedded
        let mut image_bytes: Vec<u8> = Vec::new();
        let mut encoder = png::Encoder::new(&mut image_bytes, image_info.width, image_info.height);
        encoder.set_color(image_info.color_type);
        encoder.set_depth(image_info.bit_depth);
        encoder.add_itxt_chunk("json".to_string(), json).unwrap();
        let mut writer = encoder.write_header()?;
        writer.write_image_data(&image_data)?;
        drop(writer);

        rpngs.push(image_bytes);
    }

    Ok(rpngs)
}

#[cfg(test)]
mod tests {
    use super::*;
    use codec::stencila_schema::CodeChunk;
    use test_utils::{assert_json_eq, tempfile};

    /// End-to-end test of encoding a node to a PNG and then decoding
    /// it from the PNG. See `../tests/prop.rs` for more intensive end-to-end testing.
    #[tokio::test]
    async fn encode_decode() -> Result<()> {
        let input = Node::CodeChunk(CodeChunk {
            programming_language: "python".to_string(),
            text: "print(\"Hello world!\")".to_string(),
            outputs: Some(vec![Node::String("Hello world!".to_string())]),
            ..Default::default()
        });

        let data_uri = RpngCodec::to_string_async(&input, None).await?;
        assert!(data_uri.starts_with("data:image/png;base64,"));
        let output = RpngCodec::from_str(&data_uri, None)?;
        assert_json_eq!(input, output);

        let dir = tempfile::tempdir()?;
        let path = dir.path().join("temp.png");
        RpngCodec::to_path(&input, &path, None).await?;
        assert!(path.exists());
        let output = RpngCodec::from_path(&path, None).await?;
        assert_json_eq!(input, output);

        Ok(())
    }
}
<commit_msg>chore(RPNG codec): Update spec<commit_after>use codec::{
    async_trait::async_trait,
    eyre::{bail, Result},
    stencila_schema::Node,
    utils::vec_string,
    Codec, CodecTrait, DecodeOptions, EncodeOptions,
};
use codec_json::JsonCodec;
use std::{fs, path::Path};

/// Encode and decode a document node to a reproducible PNG image.
///
/// This codec encodes the JSON representation of the node in a iTXt chunk of a PNG
/// image. You can use the tool `pnginfo` to examine the text chunks in the
/// encoded image e.g.
///
/// ```sh
/// pnginfo my.png
/// ```
///
/// For reference, there is a previous implementation of this codec, written
/// in Node.js at https://github.com/stencila/encoda/blob/v0.119.0/src/codecs/rpng/index.ts.
pub struct RpngCodec {}

#[async_trait]
impl CodecTrait for RpngCodec {
    fn spec() -> Codec {
        Codec {
            status: "alpha".to_string(),
            formats: vec_string!["rpng"],
            root_types: vec_string!["*"],
            ..Default::default()
        }
    }

    /// Decode a document node from a string
    ///
    /// This function scans the PNG for a `iTXt` chunk with a matching keyword and then delegates
    /// to the [`JsonCodec`] to decode it.
    fn from_str(content: &str, options: Option<DecodeOptions>) -> Result<Node> {
        // Remove any dataURI prefix
        let data = if let Some(data) = content.strip_prefix("data:image/png;base64,") {
            data
        } else {
            content
        };

        // Decode the Base64 to bytes and then a node
        let bytes = base64::decode(data)?;
        bytes_to_node(bytes.as_slice(), options)
    }

    /// Decode a document node from a file system path
    ///
    /// This override is necessary to read the file as bytes, not as a string, and then to
    /// directly decode those bytes, rather than Base64 decoding them first.
    /// Decode a document node from a file system path
    async fn from_path(path: &Path, options: Option<DecodeOptions>) -> Result<Node> {
        let bytes = fs::read(path)?;
        bytes_to_node(bytes.as_slice(), options)
    }

    /// Encode a document node to a string
    ///
    /// Returns a Base64 encoded dataURI (with media type `image/png`) and the node embedded as JSON.
    async fn to_string_async(node: &Node, options: Option<EncodeOptions>) -> Result<String> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        let string = ["data:image/png;base64,", &base64::encode(&bytes[0])].concat();
        Ok(string)
    }

    /// Encode a document node to a file system path
    ///
    /// This override is necessary to avoid the dataURI prefix and Base64 encoding that `to_string_async`
    /// does. It simply writes that bytes to a file at the path.
    async fn to_path(node: &Node, path: &Path, options: Option<EncodeOptions>) -> Result<()> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        fs::write(path, &bytes[0])?;
        Ok(())
    }
}

/// Decode bytes to a document node
fn bytes_to_node(bytes: &[u8], options: Option<DecodeOptions>) -> Result<Node> {
    // Decode the bytes to PNG and extract any matching iTXt chunk
    let decoder = png::Decoder::new(bytes);
    let reader = decoder.read_info()?;
    let info = reader.info();
    let json = info
        .utf8_text
        .iter()
        .find_map(|chunk| match chunk.keyword == "json" {
            true => {
                let mut chunk = chunk.clone();
                if chunk.decompress_text().is_err() {
                    return None;
                }
                chunk.get_text().ok()
            }
            false => None,
        });

    match json {
        Some(json) => codec_json::JsonCodec::from_str(&json, options),
        None => bail!("The PNG does not have an embedded Stencila node"),
    }
}

/// Encode a set of document nodes to RPNGs as bytes
///
/// As with the `nodes_to_bytes` function in the `codec-png` crate, this function
/// is based around creating multiple RPNGs, rather than a single one, to
/// reduce the per-image overhead of starting the browser, loading the theme etc.
pub async fn nodes_to_bytes(
    nodes: &[&Node],
    options: Option<EncodeOptions>,
) -> Result<Vec<Vec<u8>>> {
    // Generate the plain old PNGs
    let pngs = codec_png::nodes_to_bytes(
        nodes,
        Some(EncodeOptions {
            theme: "rpng".to_string(),
            ..options.unwrap_or_default()
        }),
    )
    .await?;

    // Transform each PNG into an RPNG...
    let mut rpngs = Vec::with_capacity(nodes.len());
    for index in 0..nodes.len() {
        // Encode the node to JSON for embedding in the PNG
        let json = JsonCodec::to_string(
            nodes[index],
            Some(EncodeOptions {
                theme: "compact".to_string(),
                ..Default::default()
            }),
        )?;

        // Encode the node to a PNG, as bytes, and decode the image to get its size etc
        let image_bytes = &pngs[index];
        let decoder = png::Decoder::new(image_bytes.as_slice());
        let mut reader = decoder.read_info()?;
        let mut image_data = vec![0; reader.output_buffer_size()];
        let image_info = reader.next_frame(&mut image_data)?;

        // Re-encode the PNG, as bytes, with JSON embedded
        let mut image_bytes: Vec<u8> = Vec::new();
        let mut encoder = png::Encoder::new(&mut image_bytes, image_info.width, image_info.height);
        encoder.set_color(image_info.color_type);
        encoder.set_depth(image_info.bit_depth);
        encoder.add_itxt_chunk("json".to_string(), json).unwrap();
        let mut writer = encoder.write_header()?;
        writer.write_image_data(&image_data)?;
        drop(writer);

        rpngs.push(image_bytes);
    }

    Ok(rpngs)
}

#[cfg(test)]
mod tests {
    use super::*;
    use codec::stencila_schema::CodeChunk;
    use test_utils::{assert_json_eq, tempfile};

    /// End-to-end test of encoding a node to a PNG and then decoding
    /// it from the PNG. See `../tests/prop.rs` for more intensive end-to-end testing.
    #[tokio::test]
    async fn encode_decode() -> Result<()> {
        let input = Node::CodeChunk(CodeChunk {
            programming_language: "python".to_string(),
            text: "print(\"Hello world!\")".to_string(),
            outputs: Some(vec![Node::String("Hello world!".to_string())]),
            ..Default::default()
        });

        let data_uri = RpngCodec::to_string_async(&input, None).await?;
        assert!(data_uri.starts_with("data:image/png;base64,"));
        let output = RpngCodec::from_str(&data_uri, None)?;
        assert_json_eq!(input, output);

        let dir = tempfile::tempdir()?;
        let path = dir.path().join("temp.png");
        RpngCodec::to_path(&input, &path, None).await?;
        assert!(path.exists());
        let output = RpngCodec::from_path(&path, None).await?;
        assert_json_eq!(input, output);

        Ok(())
    }
}
<|endoftext|>


<commit_before>use num::bigint::{
    BigInt,
    RandBigInt
};
use num::{One, Zero};
use rand::Rng;

pub struct SecretKeyExtra {
    p: BigInt,
    q: BigInt,
    dmp1: BigInt,
    dmq1: BigInt,
    qinv: BigInt
}

pub enum Key {
    Public {
        n: BigInt,
        e: BigInt,
    },
    Private {
        n: BigInt,
        d: BigInt,
        extra: Option<SecretKeyExtra>,
    }
}

type KeyPair = (Key, Key);

// fn inverse(a: BigInt, n: BigInt) -> Option<BigInt> {
//     let mut t = Zero::zero();
//     let mut r = n.clone();
//     let mut newt = One::one();
//     let mut newr = a;

//     while !newr.is_zero() {
//         let quo = &r / &newr;
//         let t = newt;

//     }

//     Some(One::one())
// }

impl Key {
    pub fn keypair_from_primes<T: Into<BigInt>>(p: T, q: T, e: T) -> () {
        let (p, q, e) = (p.into(), q.into(), e.into());

        let n = &p * &q;
        let fin = &n - (&p + &q - BigInt::one());

        assert!(&fin % &e == BigInt::one());

        let public = Key::Public { n: n.clone(), e: e.clone() };
    }

    pub fn generate_keypair<G, T>(mut rng: G, e: T, bits: usize) -> ()
        where G: RandBigInt, T: Into<BigInt> {
            let p = rng.gen_bigint(bits);
            let q = rng.gen_bigint(bits);

            Self::keypair_from_primes(p, q, e.into())
        }
}
<commit_msg>Write documentation<commit_after>use num::bigint::{
    BigInt,
    RandBigInt
};
use num::{One, Zero};
use rand::Rng;

pub struct SecretKeyExtra {
    p: BigInt,
    q: BigInt,
    dmp1: BigInt,
    dmq1: BigInt,
    qinv: BigInt
}

pub enum Key {
    Public {
        /// Modulus
        n: BigInt,
        /// Exponent
        e: BigInt,
    },
    Private {
        /// Modulus
        n: BigInt,
        /// Exponent
        d: BigInt,
        extra: Option<SecretKeyExtra>,
    }
}

type KeyPair = (Key, Key);

// fn inverse(a: BigInt, n: BigInt) -> Option<BigInt> {
//     let mut t = Zero::zero();
//     let mut r = n.clone();
//     let mut newt = One::one();
//     let mut newr = a;

//     while !newr.is_zero() {
//         let quo = &r / &newr;
//         let t = newt;

//     }

//     Some(One::one())
// }

impl Key {
    pub fn keypair_from_primes<P, Q, E>(p: P, q: Q, e: E) -> ()
        where P: Into<BigInt>, Q: Into<BigInt>, E: Into<BigInt> {
            let (p, q, e) = (p.into(), q.into(), e.into());

            let n = &p * &q;
            let fin = &n - (&p + &q - BigInt::one());

            assert!(&fin % &e == BigInt::one());

            let public = Key::Public { n: n.clone(), e: e.clone() };
        }

    pub fn generate_keypair<G, T>(mut rng: G, e: T, bits: usize) -> ()
        where G: RandBigInt, T: Into<BigInt> {
            let p = rng.gen_bigint(bits);
            let q = rng.gen_bigint(bits);

            Self::keypair_from_primes(p, q, e)
        }
}
<|endoftext|>


<commit_before>#![recursion_limit = "128"]

extern crate proc_macro;
use proc_macro::TokenStream;

extern crate heck;
extern crate proc_macro2;
extern crate syn;

#[macro_use]
extern crate quote;

use syn::{Lit, Meta, MetaList, NestedMeta};

mod ex_struct;
mod map;
mod record;
mod tuple;
mod unit_enum;
mod untagged_enum;
mod util;

#[derive(Debug)]
enum RustlerAttr {
    Encode,
    Decode,
    Module(String),
    Tag(String),
}

#[derive(Debug)]
struct Context {
    attrs: Vec<RustlerAttr>,
}

impl Context {
    fn from_ast(ast: &syn::DeriveInput) -> Self {
        let mut attrs: Vec<_> = ast
            .attrs
            .iter()
            .filter_map(Context::get_rustler_attrs)
            .flatten()
            .collect();

        //
        // Default: generate encoder and decoder
        //
        if !Context::encode_decode_attr_set(&attrs) {
            attrs.push(RustlerAttr::Encode);
            attrs.push(RustlerAttr::Decode);
        }

        Self {
            attrs
        }
    }

    fn encode(&self) -> bool {
        self.attrs.iter().find(|attr| match attr {
            RustlerAttr::Encode => true,
            _ => false
        }).is_some()
    }

    fn decode(&self) -> bool {
        self.attrs.iter().find(|attr| match attr {
            RustlerAttr::Decode => true,
            _ => false
        }).is_some()
    }

    fn encode_decode_attr_set(attrs: &[RustlerAttr]) -> bool {
        attrs.iter().find(|attr| match attr {
            RustlerAttr::Encode => true,
            RustlerAttr::Decode => true,
            _ => false
        }).is_some()
    }

    fn get_rustler_attrs(attr: &syn::Attribute) -> Option<Vec<RustlerAttr>> {
        if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "rustler" {
            let meta = attr.parse_meta().expect("can parse meta");
            match meta {
                Meta::List(list) => Some(Context::parse_attribute_list(list)),
                Meta::Word(word) => {
                    panic!("Unexpected Ident {}", word);
                }
                Meta::NameValue(name_value) => {
                    panic!("Unexpected name-value {}", name_value.ident);
                }
            }
        } else {
            None
        }
    }

    fn parse_attribute_list(list: MetaList) -> Vec<RustlerAttr> {
        list.nested
            .iter()
            .map(|nested| match nested {
                NestedMeta::Meta(meta) => match meta.name().to_string().as_ref() {
                    "encode" => RustlerAttr::Encode,
                    "decode" => RustlerAttr::Decode,
                    "module" => match meta {
                        Meta::NameValue(name_value) => {
                            let module = match name_value.lit {
                                Lit::Str(ref module) => module.value(),
                                _ => panic!("Cannot parse module"),
                            };
                            RustlerAttr::Module(module)
                        }
                        _ => panic!("Cannot parse module"),
                    },
                    "tag" => match meta {
                        Meta::NameValue(name_value) => {
                            let tag = match name_value.lit {
                                Lit::Str(ref tag) => tag.value(),
                                _ => panic!("Cannot parse tag"),
                            };
                            RustlerAttr::Tag(tag)
                        }
                        _ => panic!("Cannot parse tag"),
                    },
                    attribute => panic!("Unhandled attribute {}", attribute),
                },
                NestedMeta::Literal(lit) => match lit {
                    Lit::Str(lit_str) => panic!("Unexpected literal {}", lit_str.value()),
                    _ => panic!("Unexpected literal"),
                },
            })
            .collect()
    }
}

/// Implementation of the `NifStruct` macro that lets the user annotate a struct that will
/// be translated directly from an Elixir struct to a Rust struct. For example, the following
/// struct, annotated as such:
///
/// ```text
/// #[derive(Debug, NifStruct)]
/// #[module = "AddStruct"]
/// struct AddStruct {
///    lhs: i32,
///    rhs: i32,
/// }
/// ```
///
/// This would be translated by Rustler into:
///
/// ```text
/// defmodule AddStruct do
///     defstruct lhs: 0, rhs: 0
/// end
/// ```
#[proc_macro_derive(NifStruct, attributes(module, rustler))]
pub fn nif_struct(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    ex_struct::transcoder_decorator(&ast).into()
}

/// Implementation of a macro that lets the user annotate a struct with `NifMap` so that the
/// struct can be encoded or decoded from an Elixir map. For example, the following struct
/// annotated as such:
///
/// ```text
/// #[derive(NifMap)]
/// struct AddMap {
///     lhs: i32,
///     rhs: i32,
/// }
/// ```
///
/// Given the values 33 and 21 for this struct, this would result, when encoded, in an elixir
/// map with two elements like:
///
/// ```text
/// %{lhs: 33, rhs: 21}
/// ```
#[proc_macro_derive(NifMap, attributes(rustler))]
pub fn nif_map(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    map::transcoder_decorator(&ast).into()
}

/// Implementation of a macro that lets the user annotate a struct with `NifTuple` so that the
/// struct can be encoded or decoded from an Elixir map. For example, the following struct
/// annotated as such:
///
/// ```text
/// #[derive(NifTuple)]
/// struct AddTuple {
///     lhs: i32,
///     rhs: i32,
/// }
/// ```
///
/// Given the values 33 and 21 for this struct, this would result, when encoded, in an elixir
/// tuple with two elements like:
///
/// ```text
/// {33, 21}
/// ```
///
/// The size of the tuple will depend on the number of elements in the struct.
#[proc_macro_derive(NifTuple, attributes(rustler))]
pub fn nif_tuple(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    tuple::transcoder_decorator(&ast).into()
}

/// Implementation of the `NifRecord` macro that lets the user annotate a struct that will
/// be translated directly from an Elixir struct to a Rust struct. For example, the following
/// struct, annotated as such:
///
/// ```text
/// #[derive(Debug, NifRecord)]
/// #[tag = "record"]
/// struct AddRecord {
///    lhs: i32,
///    rhs: i32,
/// }
/// ```
///
/// This would be translated by Rustler into:
///
/// ```text
/// defmodule AddRecord do
///     import Record
///     defrecord :record, [lhs: 1, rhs: 2]
/// end
/// ```
#[proc_macro_derive(NifRecord, attributes(tag, rustler))]
pub fn nif_record(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    record::transcoder_decorator(&ast).into()
}

/// Implementation of the `NifUnitEnum` macro that lets the user annotate an enum with a unit type
/// that will generate elixir atoms when encoded
///
/// ```text
/// #[derive(NifUnitEnum)]
/// enum UnitEnum {
///    FooBar,
///    Baz,
/// }
/// ```
///
/// An example usage in elixir would look like the following.
///
/// ```text
/// test "unit enum transcoder" do
///    assert :foo_bar == RustlerTest.unit_enum_echo(:foo_bar)
///    assert :baz == RustlerTest.unit_enum_echo(:baz)
///    assert :invalid_variant == RustlerTest.unit_enum_echo(:somethingelse)
/// end
/// ```
///
/// Note that the `:invalid_variant` atom is returned if the user tries to encode something
/// that isn't in the Rust enum.
#[proc_macro_derive(NifUnitEnum, attributes(rustler))]
pub fn nif_unit_enum(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    unit_enum::transcoder_decorator(&ast).into()
}

/// Implementation of the `NifUntaggedEnum` macro that lets the user annotate an enum that will
/// generate elixir values when decoded. This can be used for rust enums that contain data and
/// will generate a value based on the kind of data encoded. For example from the test code:
///
/// ```text
/// #[derive(NifUntaggedEnum)]
/// enum UntaggedEnum {
///     Foo(u32),
///     Bar(String),
///     Baz(AddStruct),
/// }
///
/// pub fn untagged_enum_echo<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
///     let untagged_enum: UntaggedEnum = args[0].decode()?;
///     Ok(untagged_enum.encode(env))
/// }
/// ```
///
/// This can be used from elixir in the following manner.
///
/// ```text
///   test "untagged enum transcoder" do
///    assert 123 == RustlerTest.untagged_enum_echo(123)
///    assert "Hello" == RustlerTest.untagged_enum_echo("Hello")
///    assert %AddStruct{lhs: 45, rhs: 123} = RustlerTest.untagged_enum_echo(%AddStruct{lhs: 45, rhs: 123})
///    assert :invalid_variant == RustlerTest.untagged_enum_echo([1,2,3,4])
///  end
/// ```
///
/// Note that the type of elixir return is dependent on the data in the enum and the actual enum
/// type is lost in the translation because Elixir has no such concept.
#[proc_macro_derive(NifUntaggedEnum, attributes(rustler))]
pub fn nif_untagged_enum(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    untagged_enum::transcoder_decorator(&ast).into()
}
<commit_msg>Fix documentation for NifTuple (#202)<commit_after>#![recursion_limit = "128"]

extern crate proc_macro;
use proc_macro::TokenStream;

extern crate heck;
extern crate proc_macro2;
extern crate syn;

#[macro_use]
extern crate quote;

use syn::{Lit, Meta, MetaList, NestedMeta};

mod ex_struct;
mod map;
mod record;
mod tuple;
mod unit_enum;
mod untagged_enum;
mod util;

#[derive(Debug)]
enum RustlerAttr {
    Encode,
    Decode,
    Module(String),
    Tag(String),
}

#[derive(Debug)]
struct Context {
    attrs: Vec<RustlerAttr>,
}

impl Context {
    fn from_ast(ast: &syn::DeriveInput) -> Self {
        let mut attrs: Vec<_> = ast
            .attrs
            .iter()
            .filter_map(Context::get_rustler_attrs)
            .flatten()
            .collect();

        //
        // Default: generate encoder and decoder
        //
        if !Context::encode_decode_attr_set(&attrs) {
            attrs.push(RustlerAttr::Encode);
            attrs.push(RustlerAttr::Decode);
        }

        Self {
            attrs
        }
    }

    fn encode(&self) -> bool {
        self.attrs.iter().find(|attr| match attr {
            RustlerAttr::Encode => true,
            _ => false
        }).is_some()
    }

    fn decode(&self) -> bool {
        self.attrs.iter().find(|attr| match attr {
            RustlerAttr::Decode => true,
            _ => false
        }).is_some()
    }

    fn encode_decode_attr_set(attrs: &[RustlerAttr]) -> bool {
        attrs.iter().find(|attr| match attr {
            RustlerAttr::Encode => true,
            RustlerAttr::Decode => true,
            _ => false
        }).is_some()
    }

    fn get_rustler_attrs(attr: &syn::Attribute) -> Option<Vec<RustlerAttr>> {
        if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "rustler" {
            let meta = attr.parse_meta().expect("can parse meta");
            match meta {
                Meta::List(list) => Some(Context::parse_attribute_list(list)),
                Meta::Word(word) => {
                    panic!("Unexpected Ident {}", word);
                }
                Meta::NameValue(name_value) => {
                    panic!("Unexpected name-value {}", name_value.ident);
                }
            }
        } else {
            None
        }
    }

    fn parse_attribute_list(list: MetaList) -> Vec<RustlerAttr> {
        list.nested
            .iter()
            .map(|nested| match nested {
                NestedMeta::Meta(meta) => match meta.name().to_string().as_ref() {
                    "encode" => RustlerAttr::Encode,
                    "decode" => RustlerAttr::Decode,
                    "module" => match meta {
                        Meta::NameValue(name_value) => {
                            let module = match name_value.lit {
                                Lit::Str(ref module) => module.value(),
                                _ => panic!("Cannot parse module"),
                            };
                            RustlerAttr::Module(module)
                        }
                        _ => panic!("Cannot parse module"),
                    },
                    "tag" => match meta {
                        Meta::NameValue(name_value) => {
                            let tag = match name_value.lit {
                                Lit::Str(ref tag) => tag.value(),
                                _ => panic!("Cannot parse tag"),
                            };
                            RustlerAttr::Tag(tag)
                        }
                        _ => panic!("Cannot parse tag"),
                    },
                    attribute => panic!("Unhandled attribute {}", attribute),
                },
                NestedMeta::Literal(lit) => match lit {
                    Lit::Str(lit_str) => panic!("Unexpected literal {}", lit_str.value()),
                    _ => panic!("Unexpected literal"),
                },
            })
            .collect()
    }
}

/// Implementation of the `NifStruct` macro that lets the user annotate a struct that will
/// be translated directly from an Elixir struct to a Rust struct. For example, the following
/// struct, annotated as such:
///
/// ```text
/// #[derive(Debug, NifStruct)]
/// #[module = "AddStruct"]
/// struct AddStruct {
///    lhs: i32,
///    rhs: i32,
/// }
/// ```
///
/// This would be translated by Rustler into:
///
/// ```text
/// defmodule AddStruct do
///     defstruct lhs: 0, rhs: 0
/// end
/// ```
#[proc_macro_derive(NifStruct, attributes(module, rustler))]
pub fn nif_struct(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    ex_struct::transcoder_decorator(&ast).into()
}

/// Implementation of a macro that lets the user annotate a struct with `NifMap` so that the
/// struct can be encoded or decoded from an Elixir map. For example, the following struct
/// annotated as such:
///
/// ```text
/// #[derive(NifMap)]
/// struct AddMap {
///     lhs: i32,
///     rhs: i32,
/// }
/// ```
///
/// Given the values 33 and 21 for this struct, this would result, when encoded, in an elixir
/// map with two elements like:
///
/// ```text
/// %{lhs: 33, rhs: 21}
/// ```
#[proc_macro_derive(NifMap, attributes(rustler))]
pub fn nif_map(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    map::transcoder_decorator(&ast).into()
}

/// Implementation of a macro that lets the user annotate a struct with `NifTuple` so that the
/// struct can be encoded or decoded from an Elixir tuple. For example, the following struct
/// annotated as such:
///
/// ```text
/// #[derive(NifTuple)]
/// struct AddTuple {
///     lhs: i32,
///     rhs: i32,
/// }
/// ```
///
/// Given the values 33 and 21 for this struct, this would result, when encoded, in an elixir
/// tuple with two elements like:
///
/// ```text
/// {33, 21}
/// ```
///
/// The size of the tuple will depend on the number of elements in the struct.
#[proc_macro_derive(NifTuple, attributes(rustler))]
pub fn nif_tuple(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    tuple::transcoder_decorator(&ast).into()
}

/// Implementation of the `NifRecord` macro that lets the user annotate a struct that will
/// be translated directly from an Elixir struct to a Rust struct. For example, the following
/// struct, annotated as such:
///
/// ```text
/// #[derive(Debug, NifRecord)]
/// #[tag = "record"]
/// struct AddRecord {
///    lhs: i32,
///    rhs: i32,
/// }
/// ```
///
/// This would be translated by Rustler into:
///
/// ```text
/// defmodule AddRecord do
///     import Record
///     defrecord :record, [lhs: 1, rhs: 2]
/// end
/// ```
#[proc_macro_derive(NifRecord, attributes(tag, rustler))]
pub fn nif_record(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    record::transcoder_decorator(&ast).into()
}

/// Implementation of the `NifUnitEnum` macro that lets the user annotate an enum with a unit type
/// that will generate elixir atoms when encoded
///
/// ```text
/// #[derive(NifUnitEnum)]
/// enum UnitEnum {
///    FooBar,
///    Baz,
/// }
/// ```
///
/// An example usage in elixir would look like the following.
///
/// ```text
/// test "unit enum transcoder" do
///    assert :foo_bar == RustlerTest.unit_enum_echo(:foo_bar)
///    assert :baz == RustlerTest.unit_enum_echo(:baz)
///    assert :invalid_variant == RustlerTest.unit_enum_echo(:somethingelse)
/// end
/// ```
///
/// Note that the `:invalid_variant` atom is returned if the user tries to encode something
/// that isn't in the Rust enum.
#[proc_macro_derive(NifUnitEnum, attributes(rustler))]
pub fn nif_unit_enum(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    unit_enum::transcoder_decorator(&ast).into()
}

/// Implementation of the `NifUntaggedEnum` macro that lets the user annotate an enum that will
/// generate elixir values when decoded. This can be used for rust enums that contain data and
/// will generate a value based on the kind of data encoded. For example from the test code:
///
/// ```text
/// #[derive(NifUntaggedEnum)]
/// enum UntaggedEnum {
///     Foo(u32),
///     Bar(String),
///     Baz(AddStruct),
/// }
///
/// pub fn untagged_enum_echo<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
///     let untagged_enum: UntaggedEnum = args[0].decode()?;
///     Ok(untagged_enum.encode(env))
/// }
/// ```
///
/// This can be used from elixir in the following manner.
///
/// ```text
///   test "untagged enum transcoder" do
///    assert 123 == RustlerTest.untagged_enum_echo(123)
///    assert "Hello" == RustlerTest.untagged_enum_echo("Hello")
///    assert %AddStruct{lhs: 45, rhs: 123} = RustlerTest.untagged_enum_echo(%AddStruct{lhs: 45, rhs: 123})
///    assert :invalid_variant == RustlerTest.untagged_enum_echo([1,2,3,4])
///  end
/// ```
///
/// Note that the type of elixir return is dependent on the data in the enum and the actual enum
/// type is lost in the translation because Elixir has no such concept.
#[proc_macro_derive(NifUntaggedEnum, attributes(rustler))]
pub fn nif_untagged_enum(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    untagged_enum::transcoder_decorator(&ast).into()
}
<|endoftext|>


<commit_before>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{cell::RefCell, io, path::PathBuf, process::Command};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }

    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        let mut res = WorkspaceBuildScripts::default();
        let outputs = &mut res.outputs;
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
        for package in workspace.packages() {
            outputs.insert(package, None);
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };

        tracing::info!("Running build scripts: {:?}", cmd);
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        let out_dir = message.out_dir.into_os_string();
                        if !out_dir.is_empty() {
                            let data = outputs[package].get_or_insert_with(Default::default);
                            let out_dir = Some(AbsPathBuf::assert(PathBuf::from(out_dir)));
                            (data.out_dir, data.cfgs) = (out_dir, cfgs);
                        }
                        if !message.env.is_empty() {
                            outputs[package].get_or_insert_with(Default::default).envs =
                                message.env;
                        }
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                outputs[package]
                                    .get_or_insert_with(Default::default)
                                    .proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            if let Some(package_build_data) = &mut outputs[package] {
                tracing::info!(
                    "{}: {:?}",
                    workspace[package].manifest.parent().display(),
                    package_build_data,
                );
                // inject_cargo_env(package, package_build_data);
                if let Some(out_dir) = &package_build_data.out_dir {
                    // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                    if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                        package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                    }
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }

    pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
        self.outputs.get(idx)?.as_ref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<commit_msg>Auto merge of #12480 - KOLANICH-tools:unneeded_destr_assignment, r=jonas-schievink<commit_after>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{cell::RefCell, io, path::PathBuf, process::Command};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }

    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        let mut res = WorkspaceBuildScripts::default();
        let outputs = &mut res.outputs;
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
        for package in workspace.packages() {
            outputs.insert(package, None);
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };

        tracing::info!("Running build scripts: {:?}", cmd);
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        let out_dir = message.out_dir.into_os_string();
                        if !out_dir.is_empty() {
                            let data = outputs[package].get_or_insert_with(Default::default);
                            data.out_dir = Some(AbsPathBuf::assert(PathBuf::from(out_dir)));
                            data.cfgs = cfgs;
                        }
                        if !message.env.is_empty() {
                            outputs[package].get_or_insert_with(Default::default).envs =
                                message.env;
                        }
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                outputs[package]
                                    .get_or_insert_with(Default::default)
                                    .proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            if let Some(package_build_data) = &mut outputs[package] {
                tracing::info!(
                    "{}: {:?}",
                    workspace[package].manifest.parent().display(),
                    package_build_data,
                );
                // inject_cargo_env(package, package_build_data);
                if let Some(out_dir) = &package_build_data.out_dir {
                    // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                    if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                        package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                    }
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }

    pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
        self.outputs.get(idx)?.as_ref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<|endoftext|>


<commit_before>#![cfg(windows)]
use crate::Error;
use std::io::prelude::*;
use std::io::Error as IoError;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::io::FromRawHandle;
use std::path::PathBuf;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::PollEvented;
use tokio::prelude::*;
use winapi::um::fileapi::*;
use winapi::um::winbase::*;
use winapi::um::winnt::*;

/// Spiritually similar in intent to the tokio-named-pipes crate
/// equivalent, but this implementation works with pipe clients
/// rather than servers, and works with the new async/await
/// futures impl
pub struct NamedPipe {
    io: PollEvented<mio_named_pipes::NamedPipe>,
}

impl NamedPipe {
    pub async fn connect(path: PathBuf) -> Result<Self, Error> {
        let win_path = path
            .as_os_str()
            .encode_wide()
            .chain(Some(0))
            .collect::<Vec<_>>();

        let handle = unsafe {
            CreateFileW(
                win_path.as_ptr(),
                GENERIC_READ | GENERIC_WRITE,
                0,
                std::ptr::null_mut(),
                OPEN_EXISTING,
                FILE_FLAG_OVERLAPPED,
                std::ptr::null_mut(),
            )
        };
        if handle.is_null() {
            let err = IoError::last_os_error();
            return Err(Error::Connect {
                endpoint: path,
                source: Box::new(err),
            });
        }

        let pipe = unsafe { mio_named_pipes::NamedPipe::from_raw_handle(handle) };
        let io = PollEvented::new(pipe)?;
        Ok(Self { io })
    }
}

impl AsyncRead for NamedPipe {
    fn poll_read(
        self: Pin<&mut Self>,
        ctx: &mut Context,
        buf: &mut [u8],
    ) -> Poll<Result<usize, IoError>> {
        match self.io.poll_read_ready(ctx, mio::Ready::readable()) {
            Poll::Ready(res) => res?,
            Poll::Pending => return Poll::Pending,
        };

        match self.io.get_ref().read(buf) {
            Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
                self.io.clear_read_ready(ctx, mio::Ready::readable())?;
                Poll::Pending
            }

            x => Poll::Ready(x),
        }
    }
}

impl AsyncWrite for NamedPipe {
    fn poll_write(
        self: Pin<&mut Self>,
        ctx: &mut Context,
        buf: &[u8],
    ) -> Poll<Result<usize, IoError>> {
        match self.io.poll_write_ready(ctx) {
            Poll::Ready(res) => res?,
            Poll::Pending => return Poll::Pending,
        };

        match self.io.get_ref().write(buf) {
            Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
                self.io.clear_write_ready(ctx)?;
                Poll::Pending
            }

            x => Poll::Ready(x),
        }
    }

    fn poll_flush(self: Pin<&mut Self>, _ctx: &mut Context) -> Poll<Result<(), IoError>> {
        Poll::Ready(Ok(()))
    }

    fn poll_shutdown(self: Pin<&mut Self>, _ctx: &mut Context) -> Poll<Result<(), IoError>> {
        Poll::Ready(Ok(()))
    }
}

impl crate::ReadWriteStream for NamedPipe {}
<commit_msg>Fix the Rust client library on Windows (#926)<commit_after>#![cfg(windows)]
use crate::Error;
use std::io::Error as IoError;
use std::os::windows::ffi::OsStrExt;
use std::path::PathBuf;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::windows::named_pipe::NamedPipeClient;
use winapi::um::fileapi::*;
use winapi::um::winbase::*;
use winapi::um::winnt::*;

/// Wrapper around a tokio [`NamedPipeClient`]
pub struct NamedPipe {
    io: NamedPipeClient,
}

impl NamedPipe {
    pub async fn connect(path: PathBuf) -> Result<Self, Error> {
        let win_path = path
            .as_os_str()
            .encode_wide()
            .chain(Some(0))
            .collect::<Vec<_>>();

        let handle = unsafe {
            CreateFileW(
                win_path.as_ptr(),
                GENERIC_READ | GENERIC_WRITE,
                0,
                std::ptr::null_mut(),
                OPEN_EXISTING,
                FILE_FLAG_OVERLAPPED,
                std::ptr::null_mut(),
            )
        };
        if handle.is_null() {
            let err = IoError::last_os_error();
            return Err(Error::Connect {
                endpoint: path,
                source: Box::new(err),
            });
        }

        let io = unsafe { NamedPipeClient::from_raw_handle(handle)? };
        Ok(Self { io })
    }
}

impl AsyncRead for NamedPipe {
    fn poll_read(
        self: Pin<&mut Self>,
        ctx: &mut Context,
        buf: &mut ReadBuf,
    ) -> Poll<Result<(), IoError>> {
        AsyncRead::poll_read(Pin::new(&mut self.get_mut().io), ctx, buf)
    }
}

impl AsyncWrite for NamedPipe {
    fn poll_write(
        self: Pin<&mut Self>,
        ctx: &mut Context,
        buf: &[u8],
    ) -> Poll<Result<usize, IoError>> {
        AsyncWrite::poll_write(Pin::new(&mut self.get_mut().io), ctx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Result<(), IoError>> {
        AsyncWrite::poll_flush(Pin::new(&mut self.get_mut().io), ctx)
    }

    fn poll_shutdown(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Result<(), IoError>> {
        AsyncWrite::poll_shutdown(Pin::new(&mut self.get_mut().io), ctx)
    }
}

impl crate::ReadWriteStream for NamedPipe {}
<|endoftext|>


<commit_before>pub struct MMU
{
    pub mem_map: Vec<u8>,
    pub cartridge_type: u8,
    pub rom_size: u32,
    pub ram_size: u16,
    pub rom_bank: u8,
}

impl MMU
{
    pub fn new() -> MMU
    {
        return MMU
        {
            mem_map: vec![0; 0x10000],
            cartridge_type: 0,
            rom_size: 0x00008000,
            ram_size: 0x0000,
            rom_bank: 1,
        }
    }

    pub fn set_to_memory(&mut self, location: usize, value: u8, masked_set: bool)
    {
        let mut rom_flag = false;
        if masked_set
        {
            match self.cartridge_type
            {
                0x00 => (),
                0x01 => rom_flag = self.mbc1_parse(location, value),
                _ => (),
            }
        }
        if !rom_flag
        {
            self.mem_map[location] = value;
        }
    }

    pub fn get_from_memory(&self, location: usize, masked_read: bool) -> u8
    {
        return self.mem_map[location];
    }

    fn mbc1_parse(&mut self, location: usize, value: u8) -> bool
    {
        match location
        {
            0x2000...0x3FFF =>
            {
                let mut bank = value & 0x1F;
                if bank == 0
                {
                    bank += 1;
                }
                self.rom_bank &= 0xE0;
                self.rom_bank |= bank;
                return true;
            },
            _ => return false,
        }
    }
}

#[cfg(test)]
mod mmu_tests
{
    use MMU;

    #[test]
    fn get_and_set_legal_memory_test() {
        let mut mmu = MMU::new();
        mmu.set_to_memory(0x1234, 0xFF, false);
        assert_eq!(mmu.mem_map[0x1234], 0xFF);
        assert_eq!(mmu.get_from_memory(0x1234, false), 0xFF);
    }

    #[test]
    fn mbc1_test() {
        let mut mmu = MMU::new();
        mmu.cartridge_type = 0x01;

        let memory_locations: Vec<usize> = vec![0x00, 0x2000, 0x3000, 0x3FFF, 0x4000];
        let values: Vec<u8> = vec![0xFF, 0xFF, 0x00, 0xFF, 0xFF];
        let rom_bank_values: Vec<u8> = vec![0x01, 0x1F, 0x01, 0x1F, 0x01];
        let mem_loc_values: Vec<u8> = vec![0xFF, 0x00, 0x00, 0x00, 0xFF];

        for i in 0..memory_locations.len()
        {
            mmu.rom_bank = 0x01;
            mmu.mem_map[memory_locations[i]] = 0;
            mmu.set_to_memory(memory_locations[i], values[i], true);
            assert_eq!(mmu.rom_bank, rom_bank_values[i]);
            assert_eq!(mmu.mem_map[memory_locations[i]], mem_loc_values[i]);
        }
    }
}<commit_msg>Added upper 2 bit set in mbc1 mapping<commit_after>pub struct MMU
{
    pub mem_map: Vec<u8>,
    pub cartridge_type: u8,
    pub rom_size: u32,
    pub ram_size: u16,
    pub rom_bank: u8,
}

impl MMU
{
    pub fn new() -> MMU
    {
        return MMU
        {
            mem_map: vec![0; 0x10000],
            cartridge_type: 0,
            rom_size: 0x00008000,
            ram_size: 0x0000,
            rom_bank: 1,
        }
    }

    pub fn set_to_memory(&mut self, location: usize, value: u8, masked_set: bool)
    {
        let mut rom_flag = false;
        if masked_set
        {
            match self.cartridge_type
            {
                0x00 => (),
                0x01 => rom_flag = self.mbc1_parse(location, value),
                _ => (),
            }
        }
        if !rom_flag
        {
            self.mem_map[location] = value;
        }
    }

    pub fn get_from_memory(&self, location: usize, masked_read: bool) -> u8
    {
        return self.mem_map[location];
    }

    fn mbc1_parse(&mut self, location: usize, value: u8) -> bool
    {
        match location
        {
            0x2000...0x3FFF =>
            {
                let mut bank = value & 0x1F;
                if bank == 0
                {
                    bank += 1;
                }
                self.rom_bank &= 0xE0;
                self.rom_bank |= bank;
                return true;
            },
            0x4000...0x5FFF =>
            {
                self.rom_bank &= 0x1F;
                self.rom_bank |= (value & 0x03) << 5;
                return true;
            },
            _ => return false,
        }
    }
}

#[cfg(test)]
mod mmu_tests
{
    use MMU;

    #[test]
    fn get_and_set_legal_memory_test() {
        let mut mmu = MMU::new();
        mmu.set_to_memory(0x1234, 0xFF, false);
        assert_eq!(mmu.mem_map[0x1234], 0xFF);
        assert_eq!(mmu.get_from_memory(0x1234, false), 0xFF);
    }

    #[test]
    fn mbc1_test() {
        let mut mmu = MMU::new();
        mmu.cartridge_type = 0x01;

        let memory_locations: Vec<usize> = vec![0x00, 0x2000, 0x3000, 0x3FFF, 0x4000];
        let values: Vec<u8> = vec![0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x03];
        let rom_bank_values: Vec<u8> = vec![0x01, 0x1F, 0x01, 0x1F, 0x61];
        let mem_loc_values: Vec<u8> = vec![0xFF, 0x00, 0x00, 0x00, 0x00];

        for i in 0..memory_locations.len()
        {
            mmu.rom_bank = 0x01;
            mmu.mem_map[memory_locations[i]] = 0;
            mmu.set_to_memory(memory_locations[i], values[i], true);
            assert_eq!(mmu.rom_bank, rom_bank_values[i]);
            assert_eq!(mmu.mem_map[memory_locations[i]], mem_loc_values[i]);
        }
    }
}<|endoftext|>


<commit_before>use std::option::Option;

type OptInt32 = Option<i32>;
type OptInt64 = Option<i64>;
type OptString = Option<String>;

pub enum CellType {
    Int32 (OptInt32),
    Int64 (OptInt64),
    Text  (OptString)
}

pub enum CellMetatype {
    tInt32
}

trait Metainf {
    fn name(&self) -> String;
}

impl Metainf for CellType {
    fn name(&self) -> String {
        return match *self {
            CellType::Int32(_) => "INT32".to_string(),
            CellType::Int64(_) => "INT64".to_string(),
            CellType::Text(_) => "TEXT".to_string()
        }
    }
}

trait Metaformat<T> {
    fn construct(type_: CellMetatype, name: String) -> T;
}

pub struct CellFormat {
    name: String,
    type_: CellMetatype
}

pub struct Cell {
    value: CellType
}

pub impl Metaformat<Cell> for Cell {
    fn construct(type_: CellMetatype, name: String) -> Cell {
        return match type {
            CellMetatype::tInt32 => Cell { value: CellType::Int32(None), name: name }
        }
    }
}

pub struct Row {
    cells: Vec<CellType>
}

pub struct Table {
    rows: Vec<Row>
}

trait ISet {
    fn size(&self) -> usize;
    fn add(&mut self, row: Row);
    fn remove(&mut self, index: usize); // FIXME
}
<commit_msg>Fix build<commit_after>use std::option::Option;

type OptInt32 = Option<i32>;
type OptInt64 = Option<i64>;
type OptString = Option<String>;

pub enum CellType {
    Int32 (OptInt32),
    Int64 (OptInt64),
    Text  (OptString)
}

pub enum CellMetatype {
    tInt32
}

trait Metainf {
    fn name(&self) -> String;
}

impl Metainf for CellType {
    fn name(&self) -> String {
        return match *self {
            CellType::Int32(_) => "INT32".to_string(),
            CellType::Int64(_) => "INT64".to_string(),
            CellType::Text(_) => "TEXT".to_string()
        }
    }
}

trait Metaformat<T> {
    fn construct(type_: CellMetatype, name: String) -> T;
}

pub struct CellFormat {
    name: String,
    type_: CellMetatype
}

pub struct Cell {
    value: CellType
}

pub impl Metaformat<Cell> for Cell {
    fn construct(type_: CellMetatype, name: String) -> Cell {
        return match type_ {
            CellMetatype::tInt32 => Cell { value: CellType::Int32(None), name: name }
        }
    }
}

pub struct Row {
    cells: Vec<CellType>
}

pub struct Table {
    rows: Vec<Row>
}

trait ISet {
    fn size(&self) -> usize;
    fn add(&mut self, row: Row);
    fn remove(&mut self, index: usize); // FIXME
}
<|endoftext|>


<commit_before><commit_msg>[config] Add episode related config options<commit_after><|endoftext|>


<commit_before><commit_msg>Fix start sizes<commit_after><|endoftext|>