<commit_before>use utils::bigint::{U256, M256};
use utils::gas::Gas;
use utils::address::Address;

use vm::{Memory, Storage, Instruction};
use vm::errors::{MachineError, EvalError};

use vm::eval::{State, ControlCheck};
use super::utils::{check_range, check_memory_write_range};

fn check_callstack_overflow<M: Memory, S: Storage>(state: &State<M, S>) -> Result<(), MachineError> {
    if state.depth >= 1024 {
        return Err(MachineError::CallstackOverflow);
    } else {
        return Ok(());
    }
}

fn check_balance<M: Memory, S: Storage + Default + Clone>(state: &State<M, S>, address: Address, balance: U256) -> Result<(), MachineError> {
    if state.account_state.balance(address).unwrap() < balance {
        return Err(MachineError::EmptyBalance);
    } else {
        return Ok(());
    }
}

pub fn extra_check_opcode<M: Memory + Default, S: Storage + Default + Clone>(instruction: Instruction, state: &State<M, S>, stipend_gas: Gas, after_gas: Gas) -> Result<(), EvalError> {
    match instruction {
        Instruction::CALL => {
            if after_gas - stipend_gas < state.stack.peek(0).unwrap().into() {
                Err(EvalError::Machine(MachineError::EmptyGas))
            } else {
                Ok(())
            }
        },
        _ => Ok(())
    }
}

#[allow(unused_variables)]
pub fn check_opcode<M: Memory + Default, S: Storage + Default + Clone>(instruction: Instruction, state: &State<M, S>) -> Result<Option<ControlCheck>, EvalError> {
    match instruction {
        Instruction::STOP => Ok(None),
        Instruction::ADD => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::MUL => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SUB => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::DIV => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SDIV => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::MOD => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SMOD => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::ADDMOD => { state.stack.check_pop_push(3, 1)?; Ok(None) },
        Instruction::MULMOD => { state.stack.check_pop_push(3, 1)?; Ok(None) },
        Instruction::EXP => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SIGNEXTEND => { state.stack.check_pop_push(2, 1)?; Ok(None) },

        Instruction::LT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::GT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SLT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SGT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::EQ => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::ISZERO => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::AND => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::OR => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::XOR => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::NOT => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::BYTE => { state.stack.check_pop_push(2, 1)?; Ok(None) },

        Instruction::SHA3 => {
            state.stack.check_pop_push(2, 1)?;
            check_range(state.stack.peek(0).unwrap(), state.stack.peek(1).unwrap())?;
            Ok(None)
        },

        Instruction::ADDRESS => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::BALANCE => {
            state.stack.check_pop_push(1, 1)?;
            state.account_state.require(state.stack.peek(0).unwrap().into())?;
            Ok(None)
        },
        Instruction::ORIGIN => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLER => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLVALUE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLDATALOAD => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::CALLDATASIZE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLDATACOPY => {
            state.stack.check_pop_push(3, 0)?;
            check_memory_write_range(&state.memory,
                                     state.stack.peek(0).unwrap(), state.stack.peek(2).unwrap())?;
            Ok(None)
        },
        Instruction::CODESIZE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CODECOPY => {
            state.stack.check_pop_push(3, 0)?;
            check_memory_write_range(&state.memory,
                               state.stack.peek(0).unwrap(), state.stack.peek(2).unwrap())?;
            Ok(None)
        },
        Instruction::GASPRICE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::EXTCODESIZE => {
            state.stack.check_pop_push(1, 1)?;
            state.account_state.require_code(state.stack.peek(0).unwrap().into())?;
            Ok(None)
        },
        Instruction::EXTCODECOPY => {
            state.stack.check_pop_push(4, 0)?;
            state.account_state.require_code(state.stack.peek(0).unwrap().into())?;
            check_memory_write_range(&state.memory,
                                     state.stack.peek(1).unwrap(), state.stack.peek(3).unwrap())?;
            Ok(None)
        },

        Instruction::BLOCKHASH => {
            state.stack.check_pop_push(1, 1)?;
            let current_number = state.block.number;
            let number = state.stack.peek(0).unwrap();
            if !(number >= current_number || current_number - number > M256::from(256u64)) {
                state.blockhash_state.get(number)?;
            }
            Ok(None)
        },
        Instruction::COINBASE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::TIMESTAMP => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::NUMBER => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::DIFFICULTY => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::GASLIMIT => { state.stack.check_pop_push(0, 1)?; Ok(None) },

        Instruction::POP => { state.stack.check_pop_push(1, 0)?; Ok(None) },
        Instruction::MLOAD => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::MSTORE => {
            state.stack.check_pop_push(2, 0)?;
            state.memory.check_write(state.stack.peek(0).unwrap())?;
            Ok(None)
        },
        Instruction::MSTORE8 => {
            state.stack.check_pop_push(2, 0)?;
            state.memory.check_write(state.stack.peek(0).unwrap())?;
            Ok(None)
        },
        Instruction::SLOAD => {
            state.stack.check_pop_push(1, 1)?;
            state.account_state.require(state.context.address)?;
            Ok(None)
        },
        Instruction::SSTORE => {
            state.stack.check_pop_push(2, 0)?;
            state.account_state.require(state.context.address)?;
            state.account_state.storage(state.context.address)
                .unwrap().check_write(state.stack.peek(0).unwrap())?;
            Ok(None)
        },
        Instruction::JUMP => {
            state.stack.check_pop_push(1, 0)?;
            Ok(Some(ControlCheck::Jump(state.stack.peek(0).unwrap())))
        },
        Instruction::JUMPI => {
            state.stack.check_pop_push(2, 0)?;
            if state.stack.peek(1).unwrap() != M256::zero() {
                Ok(Some(ControlCheck::Jump(state.stack.peek(0).unwrap())))
            } else {
                Ok(None)
            }
        },
        Instruction::PC => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::MSIZE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::GAS => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::JUMPDEST => Ok(None),

        Instruction::PUSH(v) => { state.stack.check_pop_push(0, 1)?; Ok(None) },

        Instruction::DUP(v) => { state.stack.check_pop_push(v, v+1)?; Ok(None) },
        Instruction::SWAP(v) => { state.stack.check_pop_push(v+1, v+1)?; Ok(None) },

        Instruction::LOG(v) => {
            state.stack.check_pop_push(v+2, 0)?;
            check_range(state.stack.peek(0).unwrap(), state.stack.peek(1).unwrap())?;
            Ok(None)
        },
        Instruction::CREATE => {
            check_callstack_overflow(state)?;
            state.stack.check_pop_push(3, 1)?;
            check_range(state.stack.peek(1).unwrap(), state.stack.peek(2).unwrap())?;
            state.account_state.require(state.context.address)?;
            Ok(None)
        },
        Instruction::CALL => {
            check_callstack_overflow(state)?;
            state.stack.check_pop_push(7, 1)?;
            check_range(state.stack.peek(3).unwrap(), state.stack.peek(4).unwrap())?;
            check_memory_write_range(&state.memory,
                                     state.stack.peek(5).unwrap(), state.stack.peek(6).unwrap())?;
            state.account_state.require(state.context.address)?;
            check_balance(state, state.context.address, state.stack.peek(2).unwrap().into())?;
            state.account_state.require(state.stack.peek(1).unwrap().into())?;
            Ok(None)
        },
        Instruction::CALLCODE => unimplemented!(),
        Instruction::RETURN => {
            state.stack.check_pop_push(2, 0)?;
            check_range(state.stack.peek(0).unwrap(), state.stack.peek(1).unwrap())?;
            Ok(None)
        },
        Instruction::DELEGATECALL => unimplemented!(),
        Instruction::SUICIDE => {
            state.stack.check_pop_push(1, 0)?;
            state.account_state.require(state.context.address)?;
            Ok(None)
        },
    }
}
<commit_msg>Problem: check_balance should return EmptyBalance Solution: fix typo<commit_after>use utils::bigint::{U256, M256};
use utils::gas::Gas;
use utils::address::Address;

use vm::{Memory, Storage, Instruction};
use vm::errors::{MachineError, EvalError};

use vm::eval::{State, ControlCheck};
use super::utils::{check_range, check_memory_write_range};

fn check_callstack_overflow<M: Memory, S: Storage>(state: &State<M, S>) -> Result<(), MachineError> {
    if state.depth >= 1024 {
        return Err(MachineError::CallstackOverflow);
    } else {
        return Ok(());
    }
}

fn check_balance<M: Memory, S: Storage + Default + Clone>(state: &State<M, S>, address: Address, balance: U256) -> Result<(), MachineError> {
    if state.account_state.balance(address).unwrap() < balance {
        return Err(MachineError::EmptyBalance);
    } else {
        return Ok(());
    }
}

pub fn extra_check_opcode<M: Memory + Default, S: Storage + Default + Clone>(instruction: Instruction, state: &State<M, S>, stipend_gas: Gas, after_gas: Gas) -> Result<(), EvalError> {
    match instruction {
        Instruction::CALL => {
            if after_gas - stipend_gas < state.stack.peek(0).unwrap().into() {
                Err(EvalError::Machine(MachineError::EmptyBalance))
            } else {
                Ok(())
            }
        },
        _ => Ok(())
    }
}

#[allow(unused_variables)]
pub fn check_opcode<M: Memory + Default, S: Storage + Default + Clone>(instruction: Instruction, state: &State<M, S>) -> Result<Option<ControlCheck>, EvalError> {
    match instruction {
        Instruction::STOP => Ok(None),
        Instruction::ADD => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::MUL => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SUB => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::DIV => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SDIV => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::MOD => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SMOD => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::ADDMOD => { state.stack.check_pop_push(3, 1)?; Ok(None) },
        Instruction::MULMOD => { state.stack.check_pop_push(3, 1)?; Ok(None) },
        Instruction::EXP => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SIGNEXTEND => { state.stack.check_pop_push(2, 1)?; Ok(None) },

        Instruction::LT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::GT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SLT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::SGT => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::EQ => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::ISZERO => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::AND => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::OR => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::XOR => { state.stack.check_pop_push(2, 1)?; Ok(None) },
        Instruction::NOT => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::BYTE => { state.stack.check_pop_push(2, 1)?; Ok(None) },

        Instruction::SHA3 => {
            state.stack.check_pop_push(2, 1)?;
            check_range(state.stack.peek(0).unwrap(), state.stack.peek(1).unwrap())?;
            Ok(None)
        },

        Instruction::ADDRESS => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::BALANCE => {
            state.stack.check_pop_push(1, 1)?;
            state.account_state.require(state.stack.peek(0).unwrap().into())?;
            Ok(None)
        },
        Instruction::ORIGIN => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLER => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLVALUE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLDATALOAD => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::CALLDATASIZE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CALLDATACOPY => {
            state.stack.check_pop_push(3, 0)?;
            check_memory_write_range(&state.memory,
                                     state.stack.peek(0).unwrap(), state.stack.peek(2).unwrap())?;
            Ok(None)
        },
        Instruction::CODESIZE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::CODECOPY => {
            state.stack.check_pop_push(3, 0)?;
            check_memory_write_range(&state.memory,
                               state.stack.peek(0).unwrap(), state.stack.peek(2).unwrap())?;
            Ok(None)
        },
        Instruction::GASPRICE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::EXTCODESIZE => {
            state.stack.check_pop_push(1, 1)?;
            state.account_state.require_code(state.stack.peek(0).unwrap().into())?;
            Ok(None)
        },
        Instruction::EXTCODECOPY => {
            state.stack.check_pop_push(4, 0)?;
            state.account_state.require_code(state.stack.peek(0).unwrap().into())?;
            check_memory_write_range(&state.memory,
                                     state.stack.peek(1).unwrap(), state.stack.peek(3).unwrap())?;
            Ok(None)
        },

        Instruction::BLOCKHASH => {
            state.stack.check_pop_push(1, 1)?;
            let current_number = state.block.number;
            let number = state.stack.peek(0).unwrap();
            if !(number >= current_number || current_number - number > M256::from(256u64)) {
                state.blockhash_state.get(number)?;
            }
            Ok(None)
        },
        Instruction::COINBASE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::TIMESTAMP => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::NUMBER => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::DIFFICULTY => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::GASLIMIT => { state.stack.check_pop_push(0, 1)?; Ok(None) },

        Instruction::POP => { state.stack.check_pop_push(1, 0)?; Ok(None) },
        Instruction::MLOAD => { state.stack.check_pop_push(1, 1)?; Ok(None) },
        Instruction::MSTORE => {
            state.stack.check_pop_push(2, 0)?;
            state.memory.check_write(state.stack.peek(0).unwrap())?;
            Ok(None)
        },
        Instruction::MSTORE8 => {
            state.stack.check_pop_push(2, 0)?;
            state.memory.check_write(state.stack.peek(0).unwrap())?;
            Ok(None)
        },
        Instruction::SLOAD => {
            state.stack.check_pop_push(1, 1)?;
            state.account_state.require(state.context.address)?;
            Ok(None)
        },
        Instruction::SSTORE => {
            state.stack.check_pop_push(2, 0)?;
            state.account_state.require(state.context.address)?;
            state.account_state.storage(state.context.address)
                .unwrap().check_write(state.stack.peek(0).unwrap())?;
            Ok(None)
        },
        Instruction::JUMP => {
            state.stack.check_pop_push(1, 0)?;
            Ok(Some(ControlCheck::Jump(state.stack.peek(0).unwrap())))
        },
        Instruction::JUMPI => {
            state.stack.check_pop_push(2, 0)?;
            if state.stack.peek(1).unwrap() != M256::zero() {
                Ok(Some(ControlCheck::Jump(state.stack.peek(0).unwrap())))
            } else {
                Ok(None)
            }
        },
        Instruction::PC => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::MSIZE => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::GAS => { state.stack.check_pop_push(0, 1)?; Ok(None) },
        Instruction::JUMPDEST => Ok(None),

        Instruction::PUSH(v) => { state.stack.check_pop_push(0, 1)?; Ok(None) },

        Instruction::DUP(v) => { state.stack.check_pop_push(v, v+1)?; Ok(None) },
        Instruction::SWAP(v) => { state.stack.check_pop_push(v+1, v+1)?; Ok(None) },

        Instruction::LOG(v) => {
            state.stack.check_pop_push(v+2, 0)?;
            check_range(state.stack.peek(0).unwrap(), state.stack.peek(1).unwrap())?;
            Ok(None)
        },
        Instruction::CREATE => {
            check_callstack_overflow(state)?;
            state.stack.check_pop_push(3, 1)?;
            check_range(state.stack.peek(1).unwrap(), state.stack.peek(2).unwrap())?;
            state.account_state.require(state.context.address)?;
            Ok(None)
        },
        Instruction::CALL => {
            check_callstack_overflow(state)?;
            state.stack.check_pop_push(7, 1)?;
            check_range(state.stack.peek(3).unwrap(), state.stack.peek(4).unwrap())?;
            check_memory_write_range(&state.memory,
                                     state.stack.peek(5).unwrap(), state.stack.peek(6).unwrap())?;
            state.account_state.require(state.context.address)?;
            check_balance(state, state.context.address, state.stack.peek(2).unwrap().into())?;
            state.account_state.require(state.stack.peek(1).unwrap().into())?;
            Ok(None)
        },
        Instruction::CALLCODE => unimplemented!(),
        Instruction::RETURN => {
            state.stack.check_pop_push(2, 0)?;
            check_range(state.stack.peek(0).unwrap(), state.stack.peek(1).unwrap())?;
            Ok(None)
        },
        Instruction::DELEGATECALL => unimplemented!(),
        Instruction::SUICIDE => {
            state.stack.check_pop_push(1, 0)?;
            state.account_state.require(state.context.address)?;
            Ok(None)
        },
    }
}
<|endoftext|>


<commit_before>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
    rc::Rc,
};

use dces::prelude::{Entity, World};

use crate::{
    application::{Application, Tree},
    backend::{target_backend, BackendRunner},
    event::EventHandler,
    layout::{Layout, RootLayout},
    properties::{Bounds, Point},
    render_object::RenderObject,
    systems::{EventSystem, LayoutSystem, PostLayoutStateSystem, RenderSystem, StateSystem},
    theme::Theme,
    widget::{PropertyResult, State, Template},
    Global,
};

/// Represents a window. Each window has its own tree, event pipline and backend.
pub struct Window {
    pub backend_runner: Box<dyn BackendRunner>,
    pub render_objects: Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    pub layouts: Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    pub handlers: Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    pub states: Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    pub update: Rc<Cell<bool>>,
    pub debug_flag: Rc<Cell<bool>>,
}

impl Window {
    /// Executes the given window unitl quit is requested.
    pub fn run(&mut self) {
        self.backend_runner.run(self.update.clone());
    }
}

/// The `WindowBuilder` is used to define and build a `Window`.
pub struct WindowBuilder<'a> {
    pub application: &'a mut Application,
    pub bounds: Bounds,
    pub title: String,
    pub theme: Theme,
    pub root: Option<Template>,
    pub debug_flag: bool,
}

impl<'a> WindowBuilder<'a> {
    /// Used to define the render `bounds` of the window.
    pub fn with_bounds(mut self, bounds: Bounds) -> Self {
        self.bounds = bounds;
        self
    }

    /// Used to set the `title` of the window.
    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
        self.title = title.into();
        self
    }

    /// Used to set the css `theme` of the window.
    pub fn with_theme(mut self, theme: Theme) -> Self {
        self.theme = theme;
        self
    }

    /// Used to set the `root` template of the window.
    pub fn with_root(mut self, root: Template) -> Self {
        self.root = Some(root);
        self
    }

    /// Used to set the `debug` flag of the window.
    /// If the flag is set to `ture` debug informations will be printed to the console.
    pub fn with_debug_flag(mut self, debug: bool) -> Self {
        self.debug_flag = debug;
        self
    }

    /// Creates the window with the given properties and builds its widget tree.
    pub fn build(self) {
        let (mut runner, backend) = target_backend(&self.title, self.bounds, self.theme);
        let mut world = World::from_container(Tree::default());
        let render_objects = Rc::new(RefCell::new(BTreeMap::new()));
        let layouts = Rc::new(RefCell::new(BTreeMap::new()));
        let handlers = Rc::new(RefCell::new(BTreeMap::new()));
        let states = Rc::new(RefCell::new(BTreeMap::new()));
        let update = Rc::new(Cell::new(true));
        let debug_flag = Rc::new(Cell::new(self.debug_flag));

        if debug_flag.get() {
            println!("------ Start build tree ------\n");
        }

        if let Some(root) = self.root {
            build_tree(
                root,
                &mut world,
                &render_objects,
                &layouts,
                &handlers,
                &states,
                &debug_flag,
            );
        }

        world
            .create_system(EventSystem {
                backend: backend.clone(),
                handlers: handlers.clone(),
                update: update.clone(),
            })
            .with_priority(0)
            .build();

        world
            .create_system(StateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
                is_init: Cell::new(false),
            })
            .with_priority(1)
            .build();

        world
            .create_system(LayoutSystem {
                backend: backend.clone(),
                layouts: layouts.clone(),
                update: update.clone(),
            })
            .with_priority(2)
            .build();

        world
            .create_system(PostLayoutStateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
            })
            .with_priority(3)
            .build();

        world
            .create_system(RenderSystem {
                backend: backend.clone(),
                render_objects: render_objects.clone(),
                update: update.clone(),
                debug_flag: debug_flag.clone(),
            })
            .with_priority(4)
            .build();

        runner.world(world);

        self.application.windows.push(Window {
            backend_runner: runner,
            render_objects,
            layouts,
            handlers,
            states,
            update,
            debug_flag,
        })
    }
}

// Builds the widget tree.
fn build_tree(
    root: Template,
    world: &mut World<Tree>,
    render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    debug_flag: &Rc<Cell<bool>>,
) {
    fn expand(
        world: &mut World<Tree>,
        render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
        layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
        handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
        states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
        template: Template,
        debug_flag: &Rc<Cell<bool>>,
    ) -> Entity {
        // register window as entity with global properties
        if world.entity_container().is_empty() {
            let root = world
                .create_entity()
                .with(Global::default())
                .with(Bounds::default())
                .with(Point::default())
                .build();

            layouts
                .borrow_mut()
                .insert(root, Box::new(RootLayout));
        }

        let mut template = template;

        let entity = {
            let mut entity_builder = world
                .create_entity()
                .with(Bounds::default())
                .with(Point::default());

            // normal properties
            for (_, value) in template.properties.drain() {
                entity_builder = entity_builder.with_box(value);
            }

            // shared property
            for (_, value) in template.shared_properties {
                match value.build() {
                    PropertyResult::Property(property, source) => {
                        entity_builder = entity_builder.with_box(property);
                        source.set(Some(entity_builder.entity));
                    }
                    PropertyResult::Source(source) => {
                        entity_builder = entity_builder.with_shared_box(source);
                    }
                    PropertyResult::PropertyNotFound => {}
                }
            }

            let entity = entity_builder.build();

            if let Some(render_object) = template.render_object {
                render_objects.borrow_mut().insert(entity, render_object);
            }

            layouts
                .borrow_mut()
                .insert(entity, template.layout);

            let widget_handlers = template.event_handlers;

            if !widget_handlers.is_empty() {
                let mut event_handlers = vec![];

                for handler in widget_handlers {
                    event_handlers.push(handler.clone());
                }

                handlers.borrow_mut().insert(entity, event_handlers);
            }

            if let Some(state) = template.state {
                states.borrow_mut().insert(entity, state.clone());
            }

            entity
        };

        if debug_flag.get() {
            println!(
                "{} (id = {}, children_lenght = {})",
                template.debug_name,
                entity,
                template.children.len()
            );
        }

        if world.entity_container().len() == 2 {
            let root = world.entity_container().root;
            let _result = world.entity_container().append_child(root, entity);
        }

        for child in template.children.drain(0..) {
            let child = expand(
                world,
                render_objects,
                layouts,
                handlers,
                states,
                child,
                debug_flag,
            );
            let _result = world.entity_container().append_child(entity, child);
        }

        entity
    }

    expand(
        world,
        render_objects,
        layouts,
        handlers,
        states,
        root,
        debug_flag,
    );

    if debug_flag.get() {
        println!("\n------  End build tree  ------ ");
    }
}
<commit_msg>Added a depth parameter to the expand function to support pretty-printing the view hierarchy in debug mode.<commit_after>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
    rc::Rc,
};

use dces::prelude::{Entity, World};

use crate::{
    application::{Application, Tree},
    backend::{target_backend, BackendRunner},
    event::EventHandler,
    layout::{Layout, RootLayout},
    properties::{Bounds, Point},
    render_object::RenderObject,
    systems::{EventSystem, LayoutSystem, PostLayoutStateSystem, RenderSystem, StateSystem},
    theme::Theme,
    widget::{PropertyResult, State, Template},
    Global,
};

/// Represents a window. Each window has its own tree, event pipline and backend.
pub struct Window {
    pub backend_runner: Box<dyn BackendRunner>,
    pub render_objects: Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    pub layouts: Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    pub handlers: Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    pub states: Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    pub update: Rc<Cell<bool>>,
    pub debug_flag: Rc<Cell<bool>>,
}

impl Window {
    /// Executes the given window until quit is requested.
    pub fn run(&mut self) {
        self.backend_runner.run(self.update.clone());
    }
}

/// The `WindowBuilder` is used to define and build a `Window`.
pub struct WindowBuilder<'a> {
    pub application: &'a mut Application,
    pub bounds: Bounds,
    pub title: String,
    pub theme: Theme,
    pub root: Option<Template>,
    pub debug_flag: bool,
}

impl<'a> WindowBuilder<'a> {
    /// Used to define the render `bounds` of the window.
    pub fn with_bounds(mut self, bounds: Bounds) -> Self {
        self.bounds = bounds;
        self
    }

    /// Used to set the `title` of the window.
    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
        self.title = title.into();
        self
    }

    /// Used to set the css `theme` of the window.
    pub fn with_theme(mut self, theme: Theme) -> Self {
        self.theme = theme;
        self
    }

    /// Used to set the `root` template of the window.
    pub fn with_root(mut self, root: Template) -> Self {
        self.root = Some(root);
        self
    }

    /// Used to set the `debug` flag of the window.
    /// If the flag is set to `ture` debug informations will be printed to the console.
    pub fn with_debug_flag(mut self, debug: bool) -> Self {
        self.debug_flag = debug;
        self
    }

    /// Creates the window with the given properties and builds its widget tree.
    pub fn build(self) {
        let (mut runner, backend) = target_backend(&self.title, self.bounds, self.theme);
        let mut world = World::from_container(Tree::default());
        let render_objects = Rc::new(RefCell::new(BTreeMap::new()));
        let layouts = Rc::new(RefCell::new(BTreeMap::new()));
        let handlers = Rc::new(RefCell::new(BTreeMap::new()));
        let states = Rc::new(RefCell::new(BTreeMap::new()));
        let update = Rc::new(Cell::new(true));
        let debug_flag = Rc::new(Cell::new(self.debug_flag));

        if debug_flag.get() {
            println!("------ Start build tree ------\n");
        }

        if let Some(root) = self.root {
            build_tree(
                root,
                &mut world,
                &render_objects,
                &layouts,
                &handlers,
                &states,
                &debug_flag,
            );
        }

        world
            .create_system(EventSystem {
                backend: backend.clone(),
                handlers: handlers.clone(),
                update: update.clone(),
            })
            .with_priority(0)
            .build();

        world
            .create_system(StateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
                is_init: Cell::new(false),
            })
            .with_priority(1)
            .build();

        world
            .create_system(LayoutSystem {
                backend: backend.clone(),
                layouts: layouts.clone(),
                update: update.clone(),
            })
            .with_priority(2)
            .build();

        world
            .create_system(PostLayoutStateSystem {
                backend: backend.clone(),
                states: states.clone(),
                update: update.clone(),
            })
            .with_priority(3)
            .build();

        world
            .create_system(RenderSystem {
                backend: backend.clone(),
                render_objects: render_objects.clone(),
                update: update.clone(),
                debug_flag: debug_flag.clone(),
            })
            .with_priority(4)
            .build();

        runner.world(world);

        self.application.windows.push(Window {
            backend_runner: runner,
            render_objects,
            layouts,
            handlers,
            states,
            update,
            debug_flag,
        })
    }
}

// Builds the widget tree.
fn build_tree(
    root: Template,
    world: &mut World<Tree>,
    render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
    handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
    states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    debug_flag: &Rc<Cell<bool>>,
) {
    fn expand(
        world: &mut World<Tree>,
        render_objects: &Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
        layouts: &Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
        handlers: &Rc<RefCell<BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>>>,
        states: &Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
        template: Template,
        debug_flag: &Rc<Cell<bool>>,
        depth: usize,
    ) -> Entity {
        // register window as entity with global properties
        if world.entity_container().is_empty() {
            let root = world
                .create_entity()
                .with(Global::default())
                .with(Bounds::default())
                .with(Point::default())
                .build();

            layouts
                .borrow_mut()
                .insert(root, Box::new(RootLayout));
        }

        let mut template = template;

        let entity = {
            let mut entity_builder = world
                .create_entity()
                .with(Bounds::default())
                .with(Point::default());

            // normal properties
            for (_, value) in template.properties.drain() {
                entity_builder = entity_builder.with_box(value);
            }

            // shared property
            for (_, value) in template.shared_properties {
                match value.build() {
                    PropertyResult::Property(property, source) => {
                        entity_builder = entity_builder.with_box(property);
                        source.set(Some(entity_builder.entity));
                    }
                    PropertyResult::Source(source) => {
                        entity_builder = entity_builder.with_shared_box(source);
                    }
                    PropertyResult::PropertyNotFound => {}
                }
            }

            let entity = entity_builder.build();

            if let Some(render_object) = template.render_object {
                render_objects.borrow_mut().insert(entity, render_object);
            }

            layouts
                .borrow_mut()
                .insert(entity, template.layout);

            let widget_handlers = template.event_handlers;

            if !widget_handlers.is_empty() {
                let mut event_handlers = vec![];

                for handler in widget_handlers {
                    event_handlers.push(handler.clone());
                }

                handlers.borrow_mut().insert(entity, event_handlers);
            }

            if let Some(state) = template.state {
                states.borrow_mut().insert(entity, state.clone());
            }

            entity
        };

        if debug_flag.get() {
            println!(
                "{}{} (id = {}, children_len = {})",
                "| ".repeat(depth),
                template.debug_name,
                entity,
                template.children.len()
            );
        }

        if world.entity_container().len() == 2 {
            let root = world.entity_container().root;
            let _result = world.entity_container().append_child(root, entity);
        }

        for child in template.children.drain(0..) {
            let child = expand(
                world,
                render_objects,
                layouts,
                handlers,
                states,
                child,
                debug_flag,
                depth + 1,
            );
            let _result = world.entity_container().append_child(entity, child);
        }

        entity
    }

    expand(
        world,
        render_objects,
        layouts,
        handlers,
        states,
        root,
        debug_flag,
        0
    );

    if debug_flag.get() {
        println!("\n------  End build tree  ------ ");
    }
}
<|endoftext|>


<commit_before>extern crate websocket;
extern crate futures;
extern crate tokio_core;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

use std::fmt::Debug;

use websocket::message::{Message, OwnedMessage};
use websocket::server::InvalidConnection;
use websocket::client::async::Framed;
use websocket::async::{Server, MessageCodec};
use websocket::WebSocketError;

use tokio_core::net::TcpStream;
use tokio_core::reactor::{Handle, Core};

use futures::{Future, Sink, Stream};
use futures::future::{self, Loop};
use std::sync::{Mutex, Arc};

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let server = Server::bind("localhost:8081", &handle).unwrap();

    let f = server.incoming()
        // we don't wanna save the stream if it drops
        .map_err(|InvalidConnection { error, .. }| error)
        .for_each(|(upgrade, addr)| {
            println!("Got a connection from: {}", addr);
            if !upgrade.protocols().iter().any(|s| s == "rust-websocket") {
                spawn_future(upgrade.reject(), "Upgrade Rejection", &handle);
                return Ok(());
            }

            let client = upgrade
                .use_protocol("rust-websocket")
                .accept();
            let f = client
                .and_then(|(stream, _)| {
                 future::loop_fn(stream, |stream| {
                        stream
                            .into_future()
                            .or_else(|(err, stream)| {
                                println!("Could not send message: {:?}", err);
                                stream.send(OwnedMessage::Close(None)).map(|s| (None, s))
                            })
                            .and_then(|(msg, stream)|{
                                let mut state = State::new();
                                handle_incoming(&mut state, &msg);
                                Ok((msg, stream))
                            }).map(|(msg, stream)| {
                                match msg {
                                    Some(m) => Loop::Continue(stream),
                                    None => Loop::Break(()),
                                }
                            })
                            .boxed()
                    })
            });
            spawn_future(f, "Client Status", &handle);
            Ok(())
        });

    core.run(f).unwrap();
}

fn spawn_future<F, I, E>(f: F, desc: &'static str, handle: &Handle)
    where F: Future<Item = I, Error = E> + 'static,
          E: Debug
{
    handle.spawn(f.map_err(move |e| println!("{}: '{:?}'", desc, e))
        .map(move |_| println!("{}: Finished.", desc)));
}

fn handle_incoming(state: &mut State, msg: &Option<OwnedMessage>) {
    match msg {
        &Some(OwnedMessage::Text(ref txt)) => {
            println!("Received message: {}", txt);
        }
        _ => {}
    }
}

type FramedStream = Framed<TcpStream, MessageCodec<OwnedMessage>>;

fn send(state: &State, stream: FramedStream) -> Box<Future<Item=Loop<(), FramedStream>,Error=WebSocketError>> {
    let msg = serde_json::to_string(&state).unwrap();
    println!("Sending message: {}", msg);
    stream
        .send(OwnedMessage::Text(msg))
        .map(|s| {
            if shutdown() {
                Loop::Break(())
            } else {
                Loop::Continue(s)
            }
        })
        .boxed()

}

fn shutdown() -> bool {
    false
}

#[derive(Serialize)]
struct State {
    msg: String,
}

impl State {
    fn new() -> State {
        State {
            msg: "git gud".to_string()
        }
    }
}

<commit_msg>Core: Remove dead code<commit_after>extern crate websocket;
extern crate futures;
extern crate tokio_core;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

use std::fmt::Debug;

use websocket::message::OwnedMessage;
use websocket::server::InvalidConnection;
use websocket::client::async::Framed;
use websocket::async::{Server, MessageCodec};

use tokio_core::net::TcpStream;
use tokio_core::reactor::{Handle, Core};

use futures::{Future, Sink, Stream};
use futures::future::{self, Loop};

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let server = Server::bind("localhost:8081", &handle).unwrap();

    let f = server.incoming()
        // we don't wanna save the stream if it drops
        .map_err(|InvalidConnection { error, .. }| error)
        .for_each(|(upgrade, addr)| {
            println!("Got a connection from: {}", addr);
            if !upgrade.protocols().iter().any(|s| s == "rust-websocket") {
                spawn_future(upgrade.reject(), "Upgrade Rejection", &handle);
                return Ok(());
            }

            let client = upgrade
                .use_protocol("rust-websocket")
                .accept();
            let f = client
                .and_then(|(stream, _)| {
                 future::loop_fn(stream, |stream| {
                        stream
                            .into_future()
                            .or_else(|(err, stream)| {
                                println!("Could not send message: {:?}", err);
                                stream.send(OwnedMessage::Close(None)).map(|s| (None, s))
                            })
                            .and_then(|(msg, stream)|{
                                let mut state = State::new();
                                handle_incoming(&mut state, &msg);
                                Ok((msg, stream))
                            }).map(|(msg, stream)| {
                                match msg {
                                    Some(_) => Loop::Continue(stream),
                                    None => Loop::Break(()),
                                }
                            })
                            .boxed()
                    })
            });
            spawn_future(f, "Client Status", &handle);
            Ok(())
        });

    core.run(f).unwrap();
}

fn spawn_future<F, I, E>(f: F, desc: &'static str, handle: &Handle)
    where F: Future<Item = I, Error = E> + 'static,
          E: Debug
{
    handle.spawn(f.map_err(move |e| println!("{}: '{:?}'", desc, e))
        .map(move |_| println!("{}: Finished.", desc)));
}

fn handle_incoming(state: &mut State, msg: &Option<OwnedMessage>) {
    match msg {
        &Some(OwnedMessage::Text(ref txt)) => {
            println!("Received message: {}", txt);
            state.msg = txt.clone();
        }
        _ => {}
    }
}

type FramedStream = Framed<TcpStream, MessageCodec<OwnedMessage>>;

fn send(state: &State, stream: FramedStream) -> futures::sink::Send<FramedStream> {
    let msg = serde_json::to_string(&state).unwrap();
    println!("Sending message: {}", msg);
    stream.send(OwnedMessage::Text(msg))
}

#[derive(Serialize)]
struct State {
    msg: String,
}

impl State {
    fn new() -> State {
        State {
            msg: "git gud".to_string()
        }
    }
}

<|endoftext|>


<commit_before>use itertools::Itertools;
use regex::Regex;
use reqwest;
use rust_htslib::bcf;
use rust_htslib::bcf::Read;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::str;

#[derive(Serialize, Deserialize, Debug)]
struct Dgidb {
    #[serde(rename = "matchedTerms")]
    matched_terms: Vec<MatchedTerm>,
}

#[derive(Serialize, Deserialize, Debug)]
struct MatchedTerm {
    #[serde(rename = "geneName")]
    gene_name: String,
    interactions: Vec<Interaction>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Interaction {
    #[serde(rename = "drugName")]
    drug_name: String,
    #[serde(rename = "interactionTypes")]
    interaction_types: Vec<String>,
}

pub fn annotate_dgidb(
    vcf_path: &str,
    api_path: String,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let gene_drug_interactions = request_interaction_drugs(vcf_path, api_path)?;
    modify_vcf_entries(vcf_path, gene_drug_interactions, field_name)
}

fn request_interaction_drugs(
    vcf_path: &str,
    mut api_path: String,
) -> Result<HashMap<String, Vec<(String, Vec<String>)>>, Box<dyn Error>> {
    let mut genes = collect_genes(vcf_path)?;
    api_path.push_str(genes.drain().join(",").as_str());
    let res: Dgidb = reqwest::get(&api_path)?.json()?;

    let mut gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>> = HashMap::new();
    for term in res.matched_terms {
        if !term.interactions.is_empty() {
            gene_drug_interactions.insert(
                term.gene_name,
                term.interactions
                    .iter()
                    .map(|interaction| {
                        (
                            interaction.drug_name.clone(),
                            interaction.interaction_types.clone(),
                        )
                    })
                    .collect(),
            );
        }
    }
    Ok(gene_drug_interactions)
}

//TODO Remove split by ',' when updating to latest htslib release
fn collect_genes(vcf_path: &str) -> Result<HashSet<String>, Box<dyn Error>> {
    let mut total_genes = HashSet::new();
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    //let all_genes = HashSet::new();
    for result in reader.records() {
        let mut rec = result?;
        let genes_opt = extract_genes(&mut rec)?;
        match genes_opt {
            Some(genes) => {
                for gene in genes {
                    total_genes.insert(gene);
                }
            }
            None => {}
        }
    }
    Ok(total_genes)
}

//TODO Remove split by ',' when updating to latest htslib release
fn extract_genes<'a>(
    rec: &'a mut bcf::Record,
) -> Result<Option<impl Iterator<Item = String> + 'a>, Box<dyn Error>> {
    let annotation = rec.info("ANN".as_bytes()).string()?;
    match annotation {
        Some(transcripts) => Ok(Some(transcripts[0].split(|c| *c == b',').map(
            |transcript| {
                str::from_utf8(transcript.split(|c| *c == b'|').nth(3).unwrap())
                    .unwrap()
                    .to_owned()
            },
        ))),
        None => Ok(None),
    }
}

fn modify_vcf_entries(
    vcf_path: &str,
    gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>>,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    let mut header = bcf::header::Header::from_template(reader.header());
    header.push_record(format!("##INFO=<ID={},Number=.,Type=String,Description=\"Combination of gene, drug, interaction types extracted from dgiDB. Each combination is pipe-seperated annotated as GENE|DRUG|TYPE\">", field_name).as_bytes());
    let mut writer = bcf::Writer::from_stdout(&header, true, true)?;

    for result in reader.records() {
        let mut rec = result?;
        writer.translate(&mut rec);
        let genes = extract_genes(&mut rec)?.map(|genes| genes.collect());
        if let Some(genes) = genes {
            let field_entries = build_dgidb_field(&gene_drug_interactions, genes)?;
            let field_entries: Vec<&[u8]> = field_entries.iter().map(|v| v.as_slice()).collect();
            rec.push_info_string(field_name.as_bytes(), &field_entries[..])?;
        }
        writer.write(&rec)?;
    }
    Ok(())
}

fn build_dgidb_field(
    gene_drug_interactions: &HashMap<String, Vec<(String, Vec<String>)>>,
    genes: HashSet<String>,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
    let mut field_entries: Vec<Vec<u8>> = Vec::new();
    let re = Regex::new(r"\s\(\w+\)").unwrap();
    for gene in genes.iter() {
        match gene_drug_interactions.get(gene) {
            Some(drug_interactions) => {
                drug_interactions
                    .iter()
                    .for_each(|(drug, interaction_types)| {
                        if !interaction_types.is_empty(){
                        interaction_types.iter().for_each(|interaction_type| {
                            field_entries.push(
                                format!(
                                    "{g}|{d}|{t}",
                                    g = gene,
                                    d = re.replace(drug, ""),
                                    t = interaction_type
                                )
                                .as_bytes()
                                .to_vec(),
                            )
                        })} else {
                            field_entries.push(
                                format!(
                                    "{g}|{d}|.",
                                    g = gene,
                                    d = re.replace(drug, "")
                                )
                                .as_bytes()
                                .to_vec(),
                            )
                        }

                    });
            }
            None => field_entries.push(format!("{g}|.|.", g = gene).as_bytes().to_vec()),
        }
    }
    Ok(field_entries)
}
<commit_msg>Cargo fmt<commit_after>use itertools::Itertools;
use regex::Regex;
use reqwest;
use rust_htslib::bcf;
use rust_htslib::bcf::Read;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::str;

#[derive(Serialize, Deserialize, Debug)]
struct Dgidb {
    #[serde(rename = "matchedTerms")]
    matched_terms: Vec<MatchedTerm>,
}

#[derive(Serialize, Deserialize, Debug)]
struct MatchedTerm {
    #[serde(rename = "geneName")]
    gene_name: String,
    interactions: Vec<Interaction>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Interaction {
    #[serde(rename = "drugName")]
    drug_name: String,
    #[serde(rename = "interactionTypes")]
    interaction_types: Vec<String>,
}

pub fn annotate_dgidb(
    vcf_path: &str,
    api_path: String,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let gene_drug_interactions = request_interaction_drugs(vcf_path, api_path)?;
    modify_vcf_entries(vcf_path, gene_drug_interactions, field_name)
}

fn request_interaction_drugs(
    vcf_path: &str,
    mut api_path: String,
) -> Result<HashMap<String, Vec<(String, Vec<String>)>>, Box<dyn Error>> {
    let mut genes = collect_genes(vcf_path)?;
    api_path.push_str(genes.drain().join(",").as_str());
    let res: Dgidb = reqwest::get(&api_path)?.json()?;

    let mut gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>> = HashMap::new();
    for term in res.matched_terms {
        if !term.interactions.is_empty() {
            gene_drug_interactions.insert(
                term.gene_name,
                term.interactions
                    .iter()
                    .map(|interaction| {
                        (
                            interaction.drug_name.clone(),
                            interaction.interaction_types.clone(),
                        )
                    })
                    .collect(),
            );
        }
    }
    Ok(gene_drug_interactions)
}

//TODO Remove split by ',' when updating to latest htslib release
fn collect_genes(vcf_path: &str) -> Result<HashSet<String>, Box<dyn Error>> {
    let mut total_genes = HashSet::new();
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    //let all_genes = HashSet::new();
    for result in reader.records() {
        let mut rec = result?;
        let genes_opt = extract_genes(&mut rec)?;
        match genes_opt {
            Some(genes) => {
                for gene in genes {
                    total_genes.insert(gene);
                }
            }
            None => {}
        }
    }
    Ok(total_genes)
}

//TODO Remove split by ',' when updating to latest htslib release
fn extract_genes<'a>(
    rec: &'a mut bcf::Record,
) -> Result<Option<impl Iterator<Item = String> + 'a>, Box<dyn Error>> {
    let annotation = rec.info("ANN".as_bytes()).string()?;
    match annotation {
        Some(transcripts) => Ok(Some(transcripts[0].split(|c| *c == b',').map(
            |transcript| {
                str::from_utf8(transcript.split(|c| *c == b'|').nth(3).unwrap())
                    .unwrap()
                    .to_owned()
            },
        ))),
        None => Ok(None),
    }
}

fn modify_vcf_entries(
    vcf_path: &str,
    gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>>,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    let mut header = bcf::header::Header::from_template(reader.header());
    header.push_record(format!("##INFO=<ID={},Number=.,Type=String,Description=\"Combination of gene, drug, interaction types extracted from dgiDB. Each combination is pipe-seperated annotated as GENE|DRUG|TYPE\">", field_name).as_bytes());
    let mut writer = bcf::Writer::from_stdout(&header, true, true)?;

    for result in reader.records() {
        let mut rec = result?;
        writer.translate(&mut rec);
        let genes = extract_genes(&mut rec)?.map(|genes| genes.collect());
        if let Some(genes) = genes {
            let field_entries = build_dgidb_field(&gene_drug_interactions, genes)?;
            let field_entries: Vec<&[u8]> = field_entries.iter().map(|v| v.as_slice()).collect();
            rec.push_info_string(field_name.as_bytes(), &field_entries[..])?;
        }
        writer.write(&rec)?;
    }
    Ok(())
}

fn build_dgidb_field(
    gene_drug_interactions: &HashMap<String, Vec<(String, Vec<String>)>>,
    genes: HashSet<String>,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
    let mut field_entries: Vec<Vec<u8>> = Vec::new();
    let re = Regex::new(r"\s\(\w+\)").unwrap();
    for gene in genes.iter() {
        match gene_drug_interactions.get(gene) {
            Some(drug_interactions) => {
                drug_interactions
                    .iter()
                    .for_each(|(drug, interaction_types)| {
                        if !interaction_types.is_empty() {
                            interaction_types.iter().for_each(|interaction_type| {
                                field_entries.push(
                                    format!(
                                        "{g}|{d}|{t}",
                                        g = gene,
                                        d = re.replace(drug, ""),
                                        t = interaction_type
                                    )
                                    .as_bytes()
                                    .to_vec(),
                                )
                            })
                        } else {
                            field_entries.push(
                                format!("{g}|{d}|.", g = gene, d = re.replace(drug, ""))
                                    .as_bytes()
                                    .to_vec(),
                            )
                        }
                    });
            }
            None => field_entries.push(format!("{g}|.|.", g = gene).as_bytes().to_vec()),
        }
    }
    Ok(field_entries)
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate capnp;

mod hierarchy_capnp;
mod vm_capnp;
mod test_capnp;

use std::fs::File;
use std::path::Path;
use std::io::BufReader;
use std::process;

use hierarchy_capnp::{directories};
use capnp::{serialize, message};

fn main() {
    let matches = clap_app!(gaslighter =>
        (version: "0.1.0")
        (author: "Stewart Mackenzie <setori88@gmail.com>")
        (about: "Gaslighter - Ethereum Virtual Machine tester.")
        (@arg CAPNPROTO_TYPECHECKED_TEST_BIN: -t --capnp_test_bin +takes_value "Path to a type checked binary compiled by the capnp tool. The source of this artefact is in the tests directory. Please run `$ capnp eval -b tests/mod.capnp all > tests.bin` in the root directory to generate the binary.")
        (@arg TESTS_TO_RUN: -r --run_test +takes_value +required "The format is [directory]/[file]/[test] e.g. `--run_test arith/add/add1` will run the arith/add/add1 test, `--run_test arith/add/` will run every test in the tests/arith/add.capnp file. Likewise `--run_test arith//` will run every test in every file of the `arith` directory. Lastly `--run_test //` will run every single test available.")
        (@arg KEEP_GOING: -k --keep_going "Don't exit the program even if a test fails.")
    ).get_matches();
    let capnp_test_bin = match matches.value_of("CAPNPROTO_TYPECHECKED_TEST_BIN") {
        Some(c) => c,
        None => "",
    };
    let test_to_run = match matches.value_of("TESTS_TO_RUN") {
        Some(c) => c,
        None => "",
    };
    let keep_going = if matches.is_present("KEEP_GOING") { true } else { false };
    let path = Path::new(capnp_test_bin);
    let display = path.display();
    let file = match File::open(&path) {
        Err(_) => panic!("couldn't open {}", display),
        Ok(file) => file,
    };
    let (dir_to_run, file_to_run, test_to_run) = test_scope(test_to_run.into());
    let mut contents = BufReader::new(file);
    let tests_reader = serialize::read_message(&mut contents, message::ReaderOptions::new()).expect("read message failed.");
    let mut tests_to_execute = Vec::new();
    let top_level_tests = tests_reader.get_root::<directories::Reader>().expect("failed to get top level test root.");
    for dir in top_level_tests.get_dirs().expect("failed to directories.").iter() {
        let mut add_dir = false;
        let mut add_file = false;
        let mut add_test = false;
        let mut dirname = dir.get_name().expect("failed to get directory name.");
        let mut filename = "";
        let mut testname = "";
        if dirname == dir_to_run || dir_to_run == "" { add_dir = true; }
        for file in dir.get_files().expect("failed to files.").iter() {
            filename = file.get_name().expect("failed to get filename.");
            if filename == file_to_run || file_to_run == "" { add_file = true; }
            for test in file.get_tests().expect("failed to get tests.").iter() {
                testname = test.get_name().expect("failed to get test name.");
                if testname == test_to_run || test_to_run == "" {
                    add_test = true;
                    if add_dir && add_file && add_test {
                        tests_to_execute.push(test);
                    }
                }
            }
        }
    }
    if has_all_tests_passed(&tests_to_execute, keep_going) {
        process::exit(0);
    } else {
        process::exit(1);
    }
}

fn test_scope(test_to_run: String) -> (String, String, String) {
    let vec: Vec<&str> = test_to_run.split("/").collect();
    (vec[0].into(), vec[1].into(), vec[2].into())
}

fn has_all_tests_passed(tests_to_execute: &std::vec::Vec<test_capnp::input_output::Reader>, keep_going: bool) -> bool {
    println!("Executing tests:");
    let mut has_all_tests_passed :bool = true;
    for test in tests_to_execute {
        print!("--> {:?} ", test.get_name().expect("name expected") );
        let eo = test.get_expected_output().expect("failed to get expected output");
        let io = test.get_input_output().expect("failed to get actual input");
        let ao_gas = io.get_output().expect("").get_gas();
        let eo_gas = eo.get_gas();
        let ao_code = io.get_output().expect("").get_code().expect("").iter();
        let eo_code = eo.get_code().expect("").iter();
        let mut ao_vec = Vec::new();
        let mut eo_vec = Vec::new();
        let mut has_this_test_failed = false;
        for ao_char in ao_code {
            ao_vec.push(ao_char.expect("character expected")[0]);
        }
        for eo_char in eo_code {
            eo_vec.push(eo_char.expect("character expected")[0]);
        }
        let length = eo_vec.len();
        let matching = ao_vec.iter().zip(eo_vec.iter()).filter(|&(a, b)| a == b).count();
        if matching != length {
            has_this_test_failed = true;
            print!("\n\n code equality fail: only {} actual output opcodes matched the {} opcodes of the expected output.\n", matching, length);
            println!(" actual code output:\t{:?}", ao_vec);
            println!(" expected code output:\t{:?}", eo_vec);
        }
        if eo_gas != ao_gas {
            has_this_test_failed = true;
            print!("\n gas equality fail: actual output gas value of {} doesn't equal the expected output gas value of {}.", ao_gas, eo_gas);
            print!("\n actual gas output:\t{}", ao_gas);
            print!("\n expected gas output:\t{}\n", eo_gas);
        }
        if has_this_test_failed {
            print!("\n");
            has_all_tests_passed = false;
            if !keep_going {
                return has_all_tests_passed;
            }
        } else {
            print!("... ok\n")
        }
    }
    has_all_tests_passed
}
<commit_msg>Problem: printing just the test name makes it hard to find failing test<commit_after>#[macro_use]
extern crate clap;
extern crate capnp;

mod hierarchy_capnp;
mod vm_capnp;
mod test_capnp;

use std::fs::File;
use std::path::Path;
use std::io::BufReader;
use std::process;

use hierarchy_capnp::{directories};
use capnp::{serialize, message};

struct ExecuteTest<'a> {
    pub name: String,
    pub test: test_capnp::input_output::Reader<'a>,
}

fn main() {
    let matches = clap_app!(gaslighter =>
        (version: "0.1.0")
        (author: "Stewart Mackenzie <setori88@gmail.com>")
        (about: "Gaslighter - Ethereum Virtual Machine tester.")
        (@arg CAPNPROTO_TYPECHECKED_TEST_BIN: -t --capnp_test_bin +takes_value "Path to a type checked binary compiled by the capnp tool. The source of this artefact is in the tests directory. Please run `$ capnp eval -b tests/mod.capnp all > tests.bin` in the root directory to generate the binary.")
        (@arg TESTS_TO_RUN: -r --run_test +takes_value +required "The format is [directory]/[file]/[test] e.g. `--run_test arith/add/add1` will run the arith/add/add1 test, `--run_test arith/add/` will run every test in the tests/arith/add.capnp file. Likewise `--run_test arith//` will run every test in every file of the `arith` directory. Lastly `--run_test //` will run every single test available.")
        (@arg KEEP_GOING: -k --keep_going "Don't exit the program even if a test fails.")
    ).get_matches();
    let capnp_test_bin = match matches.value_of("CAPNPROTO_TYPECHECKED_TEST_BIN") {
        Some(c) => c,
        None => "",
    };
    let test_to_run = match matches.value_of("TESTS_TO_RUN") {
        Some(c) => c,
        None => "",
    };
    let keep_going = if matches.is_present("KEEP_GOING") { true } else { false };
    let path = Path::new(capnp_test_bin);
    let display = path.display();
    let file = match File::open(&path) {
        Err(_) => panic!("couldn't open {}", display),
        Ok(file) => file,
    };
    let (dir_to_run, file_to_run, test_to_run) = test_scope(test_to_run.into());
    let mut contents = BufReader::new(file);
    let tests_reader = serialize::read_message(&mut contents, message::ReaderOptions::new()).expect("read message failed.");
    let mut tests_to_execute :std::vec::Vec<ExecuteTest> = Vec::new();
    let top_level_tests = tests_reader.get_root::<directories::Reader>().expect("failed to get top level test root.");
    for dir in top_level_tests.get_dirs().expect("failed to directories.").iter() {
        let mut add_dir = false;
        let mut add_file = false;
        let mut add_test = false;
        let mut dirname = dir.get_name().expect("failed to get directory name.");
        let mut filename = "";
        let mut testname = "";
        if dirname == dir_to_run || dir_to_run == "" { add_dir = true; }
        for file in dir.get_files().expect("failed to files.").iter() {
            filename = file.get_name().expect("failed to get filename.");
            if filename == file_to_run || file_to_run == "" { add_file = true; }
            for test in file.get_tests().expect("failed to get tests.").iter() {
                testname = test.get_name().expect("failed to get test name.");
                if testname == test_to_run || test_to_run == "" {
                    add_test = true;
                    if add_dir && add_file && add_test {
                        let execute_test = ExecuteTest {
                            name: format!("{}/{}/{}", dirname, filename, testname),
                            test: test,
                        };
                        tests_to_execute.push(execute_test);
                    }
                }
            }
        }
    }
    if has_all_tests_passed(tests_to_execute, keep_going) {
        process::exit(0);
    } else {
        process::exit(1);
    }
}

fn test_scope(test_to_run: String) -> (String, String, String) {
    let vec: Vec<&str> = test_to_run.split("/").collect();
    (vec[0].into(), vec[1].into(), vec[2].into())
}

fn has_all_tests_passed(tests_to_execute: std::vec::Vec<ExecuteTest>, keep_going: bool) -> bool {
    println!("Executing tests:");
    let mut has_all_tests_passed = true;
    for test in tests_to_execute {
        print!("--> {:?} ", test.name);
        let test = test.test;
        let eo = test.get_expected_output().expect("failed to get expected output");
        let io = test.get_input_output().expect("failed to get actual input");
        let ao_gas = io.get_output().expect("").get_gas();
        let eo_gas = eo.get_gas();
        let ao_code = io.get_output().expect("").get_code().expect("").iter();
        let eo_code = eo.get_code().expect("").iter();
        let mut ao_vec = Vec::new();
        let mut eo_vec = Vec::new();
        let mut has_this_test_failed = false;
        for ao_char in ao_code {
            ao_vec.push(ao_char.expect("character expected")[0]);
        }
        for eo_char in eo_code {
            eo_vec.push(eo_char.expect("character expected")[0]);
        }
        let length = eo_vec.len();
        let matching = ao_vec.iter().zip(eo_vec.iter()).filter(|&(a, b)| a == b).count();
        if matching != length {
            has_this_test_failed = true;
            print!("\n\n code equality fail: only {} actual output opcodes matched the {} opcodes of the expected output.\n", matching, length);
            println!(" actual code output:\t{:?}", ao_vec);
            println!(" expected code output:\t{:?}", eo_vec);
        }
        if eo_gas != ao_gas {
            has_this_test_failed = true;
            print!("\n gas equality fail: actual output gas value of {} doesn't equal the expected output gas value of {}.", ao_gas, eo_gas);
            print!("\n actual gas output:\t{}", ao_gas);
            print!("\n expected gas output:\t{}\n", eo_gas);
        }
        if has_this_test_failed {
            print!("\n");
            has_all_tests_passed = false;
            if !keep_going {
                return has_all_tests_passed;
            }
        } else {
            print!("... ok\n")
        }
    }
    has_all_tests_passed
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate scraper;
extern crate chrono;
extern crate dotenv;

use hyper::Client;

use scraper::{Html, Selector};

use chrono::{UTC, TimeZone};

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::env;

const NEWS_URL: &'static str = "http://na.finalfantasyxiv.com/lodestone/news/";

struct NewsScraper {
  client: Client,
  database: NewsDatabase
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct NewsDatabase {
  items: HashMap<String, NewsItem>
}

#[derive(Debug, Serialize, Deserialize)]
struct NewsItem {
  title: String,
  url: String,
  kind: NewsKind,
  time: i64,
  tag: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
enum NewsKind {
  SpecialNotice,
  News,
  Topic
}

impl ToString for NewsKind {
  fn to_string(&self) -> String {
    match *self {
      NewsKind::News => "News",
      NewsKind::Topic => "Topic",
      NewsKind::SpecialNotice => "Special notice"
    }.to_string()
  }
}

fn main() {
  dotenv::dotenv().ok();

  let webhook_url = match env::var("LB_NEWS_WEBHOOK") {
    Ok(u) => u,
    Err(_) => {
      println!("No webhook specified");
      return;
    }
  };

  let database = match NewsDatabase::load() {
    Some(db) => db,
    None => NewsDatabase::default()
  };
  let mut scraper = NewsScraper::new(database);

  let news = match scraper.download_news() {
    Some(n) => n,
    None => {
      println!("could not download news");
      return;
    }
  };

  let downloaded_news = scraper.parse_news(&news);
  for (id, item) in downloaded_news {
    if !scraper.database.items.contains_key(&id) {
      let mut embed = json!({
        "type": "rich",
        "timestamp": UTC.timestamp(item.time, 0).to_rfc3339(),
        // "url": item.url,
        "fields": [
          {
            "name": "Title",
            "value": item.title,
            "inline": false
          },
          {
            "name": "Link",
            "value": item.url,
            "inline": false
          },
          {
            "name": "Kind",
            "value": item.kind.to_string(),
            "inline": true
          }
        ]
      });
      if let Some(ref tag) = item.tag {
        embed["fields"].as_array_mut().unwrap().push(json!({
          "name": "Tag",
          "value": tag,
          "inline": true
        }));
      }
      let data = json!({
        "embeds": [embed]
      });
      println!("{:#?}", item);
      scraper.database.items.insert(id, item);
      let res = scraper.client.post(&webhook_url)
        .header(hyper::header::ContentType::json())
        .body(&data.to_string())
        .send();
      let mut data = match res {
        Ok(r) => r,
        Err(e) => {
          println!("error sending webhook: {}", e);
          continue;
        }
      };
      let mut content = String::new();
      if let Err(e) = data.read_to_string(&mut content) {
        println!("could not read webhook response: {}", e);
        continue;
      }
      if data.status.class() != hyper::status::StatusClass::Success {
        println!("discord says no");
        println!("{}", content);
      } else {
        println!("webhook sent");
      }
    }
  }
  if scraper.database.save().is_none() {
    println!("could not save database");
  }
}

impl NewsDatabase {
  fn load() -> Option<Self> {
    let news_file = match File::open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::from_reader(news_file).ok()
  }

  fn save(&self) -> Option<()> {
    let news_file = match OpenOptions::new().create(true).write(true).open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::to_writer(news_file, self).ok()
  }
}

impl NewsScraper {
  fn new(database: NewsDatabase) -> Self {
    NewsScraper {
      client: Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
      database: database
    }
  }

  fn download_news(&self) -> Option<String> {
    let mut response = match self.client.get(NEWS_URL).send() {
      Ok(r) => r,
      Err(_) => return None
    };

    let mut content = String::new();
    if response.read_to_string(&mut content).is_err() {
      return None;
    }
    Some(content)
  }

  fn parse_news(&self, news: &str) -> HashMap<String, NewsItem> {
    let html = Html::parse_document(news);
    let news_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(2) > li").unwrap();
    let topics_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(3) > li").unwrap();
    let title_selector = Selector::parse("p.news__list--title").unwrap();
    let time_script_selector = Selector::parse("time.news__list--time > script").unwrap();
    let mut lis: Vec<_> = html.select(&news_selector).map(|x| (NewsKind::News, x)).collect();
    lis.append(&mut html.select(&topics_selector).map(|x| (NewsKind::Topic, x)).collect());
    let mut items = HashMap::with_capacity(lis.len());
    for (kind, li) in lis {
      let child = match kind {
        NewsKind::News => li.first_child().and_then(|v| v.value().as_element()),
        NewsKind::Topic => li.select(&title_selector).next().and_then(|v| v.first_child().and_then(|x| x.value().as_element())),
        _ => {
          println!("unsupported news kind");
          continue;
        }
      };

      let child = match child {
        Some(c) => c,
        None => {
          println!("could not get news item child");
          continue
        }
      };

      let href = match child.attr("href") {
        Some(h) => h,
        None => {
          println!("invalid link in news item");
          continue;
        }
      };

      let (title, tag) = match kind {
        NewsKind::News => {
          let title = match li.select(&title_selector).next() {
            Some(t) => t,
            None => {
              println!("missing title in news item");
              continue;
            }
          };

          let tag: Option<String> = title.first_child()
            .and_then(scraper::ElementRef::wrap)
            .map(|c| c.text().collect::<String>())
            .map(|tag| tag[1..tag.len() - 1].to_string());

          let text_iter = title.text();
          let title: String = if tag.is_some() {
            text_iter.skip(1).collect()
          } else {
            text_iter.collect()
          };

          (title, tag)
        },
        NewsKind::Topic => {
          let text = li.select(&title_selector).next()
            .and_then(|v| v.first_child())
            .and_then(scraper::ElementRef::wrap)
            .map(|v| v.text().collect());
          match text {
            Some(t) => (t, None),
            None => {
              println!("invalid topic: no title");
              continue;
            }
          }
        },
        _ => unreachable!()
      };

      let time_script = match li.select(&time_script_selector).next() {
        Some(ts) => ts,
        None => {
          println!("news item missing time script");
          continue
        }
      };

      let time_script: String = time_script.text().collect();
      let time_string = match time_script.split("strftime(").nth(1).and_then(|v| v.split(',').next()) {
        Some(time) => time,
        None => {
          println!("invalid script in news item");
          continue
        }
      };
      let time: i64 = match time_string.parse() {
        Ok(t) => t,
        Err(_) => {
          println!("invalid time in time script");
          continue
        }
      };

      let id = match href.split('/').last() {
        Some(i) => i,
        None => {
          println!("invalid href in news item");
          continue;
        }
      };

      let news_item = NewsItem {
        title: title,
        url: format!("http://na.finalfantasyxiv.com{}", href),
        kind: kind,
        time: time,
        tag: tag
      };
      items.insert(id.to_string(), news_item);
    }

    items
  }
}
<commit_msg>fix: only add item to database if webhook was successful<commit_after>extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate scraper;
extern crate chrono;
extern crate dotenv;

use hyper::Client;

use scraper::{Html, Selector};

use chrono::{UTC, TimeZone};

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::env;

const NEWS_URL: &'static str = "http://na.finalfantasyxiv.com/lodestone/news/";

struct NewsScraper {
  client: Client,
  database: NewsDatabase
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct NewsDatabase {
  items: HashMap<String, NewsItem>
}

#[derive(Debug, Serialize, Deserialize)]
struct NewsItem {
  title: String,
  url: String,
  kind: NewsKind,
  time: i64,
  tag: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
enum NewsKind {
  SpecialNotice,
  News,
  Topic
}

impl ToString for NewsKind {
  fn to_string(&self) -> String {
    match *self {
      NewsKind::News => "News",
      NewsKind::Topic => "Topic",
      NewsKind::SpecialNotice => "Special notice"
    }.to_string()
  }
}

fn main() {
  dotenv::dotenv().ok();

  let webhook_url = match env::var("LB_NEWS_WEBHOOK") {
    Ok(u) => u,
    Err(_) => {
      println!("No webhook specified");
      return;
    }
  };

  let database = match NewsDatabase::load() {
    Some(db) => db,
    None => NewsDatabase::default()
  };
  let mut scraper = NewsScraper::new(database);

  let news = match scraper.download_news() {
    Some(n) => n,
    None => {
      println!("could not download news");
      return;
    }
  };

  let downloaded_news = scraper.parse_news(&news);
  for (id, item) in downloaded_news {
    if !scraper.database.items.contains_key(&id) {
      let mut embed = json!({
        "type": "rich",
        "timestamp": UTC.timestamp(item.time, 0).to_rfc3339(),
        // "url": item.url,
        "fields": [
          {
            "name": "Title",
            "value": item.title,
            "inline": false
          },
          {
            "name": "Link",
            "value": item.url,
            "inline": false
          },
          {
            "name": "Kind",
            "value": item.kind.to_string(),
            "inline": true
          }
        ]
      });
      if let Some(ref tag) = item.tag {
        embed["fields"].as_array_mut().unwrap().push(json!({
          "name": "Tag",
          "value": tag,
          "inline": true
        }));
      }
      let data = json!({
        "embeds": [embed]
      });
      println!("{:#?}", item);
      let res = scraper.client.post(&webhook_url)
        .header(hyper::header::ContentType::json())
        .body(&data.to_string())
        .send();
      let mut data = match res {
        Ok(r) => r,
        Err(e) => {
          println!("error sending webhook: {}", e);
          continue;
        }
      };
      let mut content = String::new();
      if let Err(e) = data.read_to_string(&mut content) {
        println!("could not read webhook response: {}", e);
        continue;
      }
      if data.status.class() != hyper::status::StatusClass::Success {
        println!("discord says no");
        println!("{}", content);
      } else {
        println!("webhook sent");
        scraper.database.items.insert(id, item);
      }
    }
  }
  if scraper.database.save().is_none() {
    println!("could not save database");
  }
}

impl NewsDatabase {
  fn load() -> Option<Self> {
    let news_file = match File::open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::from_reader(news_file).ok()
  }

  fn save(&self) -> Option<()> {
    let news_file = match OpenOptions::new().create(true).write(true).open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::to_writer(news_file, self).ok()
  }
}

impl NewsScraper {
  fn new(database: NewsDatabase) -> Self {
    NewsScraper {
      client: Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
      database: database
    }
  }

  fn download_news(&self) -> Option<String> {
    let mut response = match self.client.get(NEWS_URL).send() {
      Ok(r) => r,
      Err(_) => return None
    };

    let mut content = String::new();
    if response.read_to_string(&mut content).is_err() {
      return None;
    }
    Some(content)
  }

  fn parse_news(&self, news: &str) -> HashMap<String, NewsItem> {
    let html = Html::parse_document(news);
    let news_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(2) > li").unwrap();
    let topics_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(3) > li").unwrap();
    let title_selector = Selector::parse("p.news__list--title").unwrap();
    let time_script_selector = Selector::parse("time.news__list--time > script").unwrap();
    let mut lis: Vec<_> = html.select(&news_selector).map(|x| (NewsKind::News, x)).collect();
    lis.append(&mut html.select(&topics_selector).map(|x| (NewsKind::Topic, x)).collect());
    let mut items = HashMap::with_capacity(lis.len());
    for (kind, li) in lis {
      let child = match kind {
        NewsKind::News => li.first_child().and_then(|v| v.value().as_element()),
        NewsKind::Topic => li.select(&title_selector).next().and_then(|v| v.first_child().and_then(|x| x.value().as_element())),
        _ => {
          println!("unsupported news kind");
          continue;
        }
      };

      let child = match child {
        Some(c) => c,
        None => {
          println!("could not get news item child");
          continue
        }
      };

      let href = match child.attr("href") {
        Some(h) => h,
        None => {
          println!("invalid link in news item");
          continue;
        }
      };

      let (title, tag) = match kind {
        NewsKind::News => {
          let title = match li.select(&title_selector).next() {
            Some(t) => t,
            None => {
              println!("missing title in news item");
              continue;
            }
          };

          let tag: Option<String> = title.first_child()
            .and_then(scraper::ElementRef::wrap)
            .map(|c| c.text().collect::<String>())
            .map(|tag| tag[1..tag.len() - 1].to_string());

          let text_iter = title.text();
          let title: String = if tag.is_some() {
            text_iter.skip(1).collect()
          } else {
            text_iter.collect()
          };

          (title, tag)
        },
        NewsKind::Topic => {
          let text = li.select(&title_selector).next()
            .and_then(|v| v.first_child())
            .and_then(scraper::ElementRef::wrap)
            .map(|v| v.text().collect());
          match text {
            Some(t) => (t, None),
            None => {
              println!("invalid topic: no title");
              continue;
            }
          }
        },
        _ => unreachable!()
      };

      let time_script = match li.select(&time_script_selector).next() {
        Some(ts) => ts,
        None => {
          println!("news item missing time script");
          continue
        }
      };

      let time_script: String = time_script.text().collect();
      let time_string = match time_script.split("strftime(").nth(1).and_then(|v| v.split(',').next()) {
        Some(time) => time,
        None => {
          println!("invalid script in news item");
          continue
        }
      };
      let time: i64 = match time_string.parse() {
        Ok(t) => t,
        Err(_) => {
          println!("invalid time in time script");
          continue
        }
      };

      let id = match href.split('/').last() {
        Some(i) => i,
        None => {
          println!("invalid href in news item");
          continue;
        }
      };

      let news_item = NewsItem {
        title: title,
        url: format!("http://na.finalfantasyxiv.com{}", href),
        kind: kind,
        time: time,
        tag: tag
      };
      items.insert(id.to_string(), news_item);
    }

    items
  }
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate rayon;
extern crate rayon_futures;

use futures::future::lazy;
use rayon_futures::ScopeFutureExt;

fn a() {
    let data = &mut [format!("Hello, ")];

    let mut future = None;
    rayon::scope(|s| {
        let data = &mut *data;
        future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
    });

    // `data` is still borrowed as part of future here:
    assert_eq!(data[0], "Hello, world!"); //~ ERROR E0501
}

fn b() {
    let data = &mut [format!("Hello, ")];

    let mut future = None;
    rayon::scope(|s| {
        future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
    });

    // `data` is moved into the scope above, can't use here
    assert_eq!(data[0], "Hello, world!"); //~ ERROR E0382
}

fn c() {
    let mut future = None;
    // borrowed value does not live long enough
    let data = &mut [format!("Hello, ")]; //~ ERROR E0597
    rayon::scope(|s| {
        future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
    });
}

fn main() { }
<commit_msg>use rayon_core to test futures<commit_after>extern crate futures;
extern crate rayon_core;
extern crate rayon_futures;

use futures::future::lazy;
use rayon_futures::ScopeFutureExt;

fn a() {
    let data = &mut [format!("Hello, ")];

    let mut future = None;
    rayon_core::scope(|s| {
        let data = &mut *data;
        future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
    });

    // `data` is still borrowed as part of future here:
    assert_eq!(data[0], "Hello, world!"); //~ ERROR E0501
}

fn b() {
    let data = &mut [format!("Hello, ")];

    let mut future = None;
    rayon_core::scope(|s| {
        future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
    });

    // `data` is moved into the scope above, can't use here
    assert_eq!(data[0], "Hello, world!"); //~ ERROR E0382
}

fn c() {
    let mut future = None;
    // borrowed value does not live long enough
    let data = &mut [format!("Hello, ")]; //~ ERROR E0597
    rayon_core::scope(|s| {
        future = Some(s.spawn_future(lazy(move || Ok::<_, ()>(&mut data[0]))));
    });
}

fn main() { }
<|endoftext|>