<commit_before>use llvm_sys::analysis::{LLVMVerifyModule, LLVMVerifierFailureAction};
use llvm_sys::bit_writer::{LLVMWriteBitcodeToFile, LLVMWriteBitcodeToMemoryBuffer};
use llvm_sys::core::{LLVMAddFunction, LLVMAddGlobal, LLVMCreateFunctionPassManagerForModule, LLVMDisposeMessage, LLVMDumpModule, LLVMGetNamedFunction, LLVMGetTypeByName, LLVMSetDataLayout, LLVMSetInitializer, LLVMSetTarget};
use llvm_sys::execution_engine::{LLVMCreateExecutionEngineForModule, LLVMLinkInInterpreter, LLVMLinkInMCJIT};
use llvm_sys::prelude::LLVMModuleRef;
use llvm_sys::target::{LLVM_InitializeNativeTarget, LLVM_InitializeNativeAsmPrinter, LLVM_InitializeNativeAsmParser, LLVM_InitializeNativeDisassembler};

// REVIEW: Drop for Module? There's a LLVM method, but I read context dispose takes care of it...
use std::ffi::{CString, CStr};
use std::mem::{uninitialized, zeroed};

use data_layout::DataLayout;
use execution_engine::ExecutionEngine;
use memory_buffer::MemoryBuffer;
use pass_manager::PassManager;
use types::{BasicType, FunctionType, BasicTypeEnum, AsLLVMTypeRef};
use values::{BasicValue, FunctionValue, PointerValue};

pub struct Module {
    pub(crate) module: LLVMModuleRef,
}

impl Module {
    pub(crate) fn new(module: LLVMModuleRef) -> Self {
        assert!(!module.is_null());

        Module {
            module: module
        }
    }

    pub fn add_function(&self, name: &str, return_type: &FunctionType) -> FunctionValue {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let value = unsafe {
            LLVMAddFunction(self.module, c_string.as_ptr(), return_type.as_llvm_type_ref())
        };

        // unsafe {
        //     LLVMSetLinkage(value, LLVMCommonLinkage);
        // }

        FunctionValue::new(value)
    }

    pub fn get_function(&self, name: &str) -> Option<FunctionValue> {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let value = unsafe {
            LLVMGetNamedFunction(self.module, c_string.as_ptr())
        };

        if value.is_null() {
            return None;
        }

        Some(FunctionValue::new(value))
    }

    pub fn get_type(&self, name: &str) -> Option<BasicTypeEnum> {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let type_ = unsafe {
            LLVMGetTypeByName(self.module, c_string.as_ptr())
        };

        if type_.is_null() {
            return None;
        }

        Some(BasicTypeEnum::new(type_))
    }

    pub fn set_target(&self, target_triple: &str) {
        let c_string = CString::new(target_triple).expect("Conversion to CString failed unexpectedly");

        unsafe {
            LLVMSetTarget(self.module, c_string.as_ptr())
        }
    }

    pub fn create_execution_engine(&self, jit_mode: bool) -> Result<ExecutionEngine, String> {
        let mut execution_engine = unsafe { uninitialized() };
        let mut err_str = unsafe { zeroed() };

        if jit_mode {
            unsafe {
                LLVMLinkInMCJIT();
            }
        }

        // TODO: Check that these calls are even needed
        let code = unsafe {
            LLVM_InitializeNativeTarget()
        };

        if code == 1 {
            return Err("Unknown error in initializing native target".into());
        }

        let code = unsafe {
            LLVM_InitializeNativeAsmPrinter()
        };

        if code == 1 {
            return Err("Unknown error in initializing native asm printer".into());
        }

        let code = unsafe {
            LLVM_InitializeNativeAsmParser()
        };

        if code == 1 { // REVIEW: Does parser need to go before printer?
            return Err("Unknown error in initializing native asm parser".into());
        }

        let code = unsafe {
            LLVM_InitializeNativeDisassembler()
        };

        if code == 1 {
            return Err("Unknown error in initializing native disassembler".into());
        }

        unsafe {
            LLVMLinkInInterpreter();
        }

        let code = unsafe {
            LLVMCreateExecutionEngineForModule(&mut execution_engine, self.module, &mut err_str) // Should take ownership of module
        };

        if code == 1 {
            let rust_str = unsafe {
                let rust_str = CStr::from_ptr(err_str).to_string_lossy().into_owned();

                LLVMDisposeMessage(err_str);

                rust_str
            };

            return Err(rust_str);
        }

        Ok(ExecutionEngine::new(execution_engine, jit_mode))
    }

    pub fn create_function_pass_manager(&self) -> PassManager {
        let pass_manager = unsafe {
            LLVMCreateFunctionPassManagerForModule(self.module)
        };

        PassManager::new(pass_manager)
    }

    // REVIEW: Is this really always a pointer? It would make sense...
    pub fn add_global(&self, type_: &BasicType, init_value: Option<&BasicValue>, name: &str) -> PointerValue {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let value = unsafe {
            LLVMAddGlobal(self.module, type_.as_llvm_type_ref(), c_string.as_ptr())
        };

        if let Some(ref init_val) = init_value {
            unsafe {
                LLVMSetInitializer(value, init_val.as_llvm_value_ref())
            }
        }

        PointerValue::new(value)
    }

    // REVIEW: Untested
    pub fn write_bitcode_to_file(&self, path: &str) -> bool {
        let c_string = CString::new(path).expect("Conversion to CString failed unexpectedly");

        let code = unsafe {
            LLVMWriteBitcodeToFile(self.module, c_string.as_ptr())
        };

        code == 0
    }

    // REVIEW: Untested
    pub fn write_bitcode_to_memory(&self) -> MemoryBuffer {
        let memory_buffer = unsafe {
            LLVMWriteBitcodeToMemoryBuffer(self.module)
        };

        MemoryBuffer::new(memory_buffer)
    }

    pub fn verify(&self, print: bool) -> bool {
        let err_str: *mut *mut i8 = unsafe { zeroed() };

        let action = if print {
            LLVMVerifierFailureAction::LLVMPrintMessageAction
        } else {
            LLVMVerifierFailureAction::LLVMReturnStatusAction
        };

        let code = unsafe {
            LLVMVerifyModule(self.module, action, err_str)
        };

        if code == 1 && !err_str.is_null() {
            unsafe {
                if print {
                    let rust_str = CStr::from_ptr(*err_str).to_str().unwrap();

                    println!("{}", rust_str); // FIXME: Should probably be stderr?
                }

                LLVMDisposeMessage(*err_str);
            }
        }

        code == 0
    }

    pub fn set_data_layout(&self, data_layout: DataLayout) {
        unsafe {
            LLVMSetDataLayout(self.module, data_layout.data_layout)
        }
    }

    pub fn dump(&self) {
        unsafe {
            LLVMDumpModule(self.module);
        }
    }
}
<commit_msg>Added write_bitcode_to_file and updated write_bitcode_to_path to take a Path object ref<commit_after>use llvm_sys::analysis::{LLVMVerifyModule, LLVMVerifierFailureAction};
use llvm_sys::bit_writer::{LLVMWriteBitcodeToFile, LLVMWriteBitcodeToMemoryBuffer, LLVMWriteBitcodeToFD};
use llvm_sys::core::{LLVMAddFunction, LLVMAddGlobal, LLVMCreateFunctionPassManagerForModule, LLVMDisposeMessage, LLVMDumpModule, LLVMGetNamedFunction, LLVMGetTypeByName, LLVMSetDataLayout, LLVMSetInitializer, LLVMSetTarget};
use llvm_sys::execution_engine::{LLVMCreateExecutionEngineForModule, LLVMLinkInInterpreter, LLVMLinkInMCJIT};
use llvm_sys::prelude::LLVMModuleRef;
use llvm_sys::target::{LLVM_InitializeNativeTarget, LLVM_InitializeNativeAsmPrinter, LLVM_InitializeNativeAsmParser, LLVM_InitializeNativeDisassembler};

// REVIEW: Drop for Module? There's a LLVM method, but I read context dispose takes care of it...
use std::ffi::{CString, CStr};
use std::fs::File;
use std::mem::{uninitialized, zeroed};
use std::path::Path;
use std::os::unix::io::AsRawFd;

use data_layout::DataLayout;
use execution_engine::ExecutionEngine;
use memory_buffer::MemoryBuffer;
use pass_manager::PassManager;
use types::{BasicType, FunctionType, BasicTypeEnum, AsLLVMTypeRef};
use values::{BasicValue, FunctionValue, PointerValue};

pub struct Module {
    pub(crate) module: LLVMModuleRef,
}

impl Module {
    pub(crate) fn new(module: LLVMModuleRef) -> Self {
        assert!(!module.is_null());

        Module {
            module: module
        }
    }

    pub fn add_function(&self, name: &str, return_type: &FunctionType) -> FunctionValue {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let value = unsafe {
            LLVMAddFunction(self.module, c_string.as_ptr(), return_type.as_llvm_type_ref())
        };

        // unsafe {
        //     LLVMSetLinkage(value, LLVMCommonLinkage);
        // }

        FunctionValue::new(value)
    }

    pub fn get_function(&self, name: &str) -> Option<FunctionValue> {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let value = unsafe {
            LLVMGetNamedFunction(self.module, c_string.as_ptr())
        };

        if value.is_null() {
            return None;
        }

        Some(FunctionValue::new(value))
    }

    pub fn get_type(&self, name: &str) -> Option<BasicTypeEnum> {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let type_ = unsafe {
            LLVMGetTypeByName(self.module, c_string.as_ptr())
        };

        if type_.is_null() {
            return None;
        }

        Some(BasicTypeEnum::new(type_))
    }

    pub fn set_target(&self, target_triple: &str) {
        let c_string = CString::new(target_triple).expect("Conversion to CString failed unexpectedly");

        unsafe {
            LLVMSetTarget(self.module, c_string.as_ptr())
        }
    }

    pub fn create_execution_engine(&self, jit_mode: bool) -> Result<ExecutionEngine, String> {
        let mut execution_engine = unsafe { uninitialized() };
        let mut err_str = unsafe { zeroed() };

        if jit_mode {
            unsafe {
                LLVMLinkInMCJIT();
            }
        }

        // TODO: Check that these calls are even needed
        let code = unsafe {
            LLVM_InitializeNativeTarget()
        };

        if code == 1 {
            return Err("Unknown error in initializing native target".into());
        }

        let code = unsafe {
            LLVM_InitializeNativeAsmPrinter()
        };

        if code == 1 {
            return Err("Unknown error in initializing native asm printer".into());
        }

        let code = unsafe {
            LLVM_InitializeNativeAsmParser()
        };

        if code == 1 { // REVIEW: Does parser need to go before printer?
            return Err("Unknown error in initializing native asm parser".into());
        }

        let code = unsafe {
            LLVM_InitializeNativeDisassembler()
        };

        if code == 1 {
            return Err("Unknown error in initializing native disassembler".into());
        }

        unsafe {
            LLVMLinkInInterpreter();
        }

        let code = unsafe {
            LLVMCreateExecutionEngineForModule(&mut execution_engine, self.module, &mut err_str) // Should take ownership of module
        };

        if code == 1 {
            let rust_str = unsafe {
                let rust_str = CStr::from_ptr(err_str).to_string_lossy().into_owned();

                LLVMDisposeMessage(err_str);

                rust_str
            };

            return Err(rust_str);
        }

        Ok(ExecutionEngine::new(execution_engine, jit_mode))
    }

    pub fn create_function_pass_manager(&self) -> PassManager {
        let pass_manager = unsafe {
            LLVMCreateFunctionPassManagerForModule(self.module)
        };

        PassManager::new(pass_manager)
    }

    // REVIEW: Is this really always a pointer? It would make sense...
    pub fn add_global(&self, type_: &BasicType, init_value: Option<&BasicValue>, name: &str) -> PointerValue {
        let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

        let value = unsafe {
            LLVMAddGlobal(self.module, type_.as_llvm_type_ref(), c_string.as_ptr())
        };

        if let Some(ref init_val) = init_value {
            unsafe {
                LLVMSetInitializer(value, init_val.as_llvm_value_ref())
            }
        }

        PointerValue::new(value)
    }

    // REVIEW: Untested
    pub fn write_bitcode_to_path(&self, path: &Path) -> bool {
        let path_str = path.to_str().expect("Did not find a valid Unicode path string");
        let c_string = CString::new(path_str).expect("Conversion to CString failed unexpectedly");

        unsafe {
            LLVMWriteBitcodeToFile(self.module, c_string.as_ptr()) == 0
        }
    }

    pub fn write_bitcode_to_file(&self, file: &File, should_close: bool, unbuffered: bool) -> bool {
        // REVIEW: as_raw_fd docs suggest it only works in *nix
        unsafe {
            LLVMWriteBitcodeToFD(self.module, file.as_raw_fd(), should_close as i32, unbuffered as i32) == 0
        }
    }

    // REVIEW: Untested
    pub fn write_bitcode_to_memory(&self) -> MemoryBuffer {
        let memory_buffer = unsafe {
            LLVMWriteBitcodeToMemoryBuffer(self.module)
        };

        MemoryBuffer::new(memory_buffer)
    }

    pub fn verify(&self, print: bool) -> bool {
        let err_str: *mut *mut i8 = unsafe { zeroed() };

        let action = if print {
            LLVMVerifierFailureAction::LLVMPrintMessageAction
        } else {
            LLVMVerifierFailureAction::LLVMReturnStatusAction
        };

        let code = unsafe {
            LLVMVerifyModule(self.module, action, err_str)
        };

        if code == 1 && !err_str.is_null() {
            unsafe {
                if print {
                    let rust_str = CStr::from_ptr(*err_str).to_str().unwrap();

                    println!("{}", rust_str); // FIXME: Should probably be stderr?
                }

                LLVMDisposeMessage(*err_str);
            }
        }

        code == 0
    }

    pub fn set_data_layout(&self, data_layout: DataLayout) {
        unsafe {
            LLVMSetDataLayout(self.module, data_layout.data_layout)
        }
    }

    pub fn dump(&self) {
        unsafe {
            LLVMDumpModule(self.module);
        }
    }
}
<|endoftext|>


<commit_before>//! Enumeration of all supported IRCv3 capability extensions.

/// List of all supported IRCv3 capability extensions from the
/// [IRCv3 specifications](http://ircv3.net/irc/).
#[derive(Debug, PartialEq)]
pub enum Capability {
    /// [multi-prefix](http://ircv3.net/specs/extensions/multi-prefix-3.1.html)
    MultiPrefix,
    /// [account-notify](http://ircv3.net/specs/extensions/account-notify-3.1.html)
    AccountNotify,
    /// [away-notify](http://ircv3.net/specs/extensions/away-notify-3.1.html)
    AwayNotify,
    /// [extended-join](http://ircv3.net/specs/extensions/extended-join-3.1.html)
    ExtendedJoin,
    /// [monitor](http://ircv3.net/specs/core/monitor-3.2.html)
    Monitor,
    /// [account-tag](http://ircv3.net/specs/extensions/account-tag-3.2.html)
    AccountTag,
    /// [cap-notify](http://ircv3.net/specs/extensions/cap-notify-3.2.html)
    CapNotify,
    /// [chghost](http://ircv3.net/specs/extensions/chghost-3.2.html)
    ChgHost,
    /// [echo-message](http://ircv3.net/specs/extensions/echo-message-3.2.html)
    EchoMessage,
    /// [invite-notify](http://ircv3.net/specs/extensions/invite-notify-3.2.html)
    InviteNotify,
}

/// List of IRCv3 capability negotiation versions.
pub enum NegotiationVersion {
    /// [IRCv3.1](http://ircv3.net/specs/core/capability-negotiation-3.1.html)
    V301,
    /// [IRCv3.2](http://ircv3.net/specs/core/capability-negotiation-3.2.html)
    V302,
}

impl AsRef<str> for Capability {
    fn as_ref(&self) -> &str {
        match *self {
            Capability::MultiPrefix => "multi-prefix",
            Capability::AccountNotify => "account-notify",
            Capability::AwayNotify => "away-notify",
            Capability::ExtendedJoin => "extended-join",
            Capability::Monitor => "monitor",
            Capability::AccountTag => "account-tag",
            Capability::CapNotify => "cap-notify",
            Capability::ChgHost => "chghost",
            Capability::EchoMessage => "echo-message",
            Capability::InviteNotify => "invite-notify",
        }
    }
}

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

    #[test]
    fn to_str() {
        assert_eq!(MultiPrefix.as_ref(), "multi-prefix");
        assert_eq!(AccountNotify.as_ref(), "account-notify");
        assert_eq!(AwayNotify.as_ref(), "away-notify");
        assert_eq!(ExtendedJoin.as_ref(), "extended-join");
        assert_eq!(Monitor.as_ref(), "monitor");
        assert_eq!(AccountTag.as_ref(), "account-tag");
        assert_eq!(CapNotify.as_ref(), "cap-notify");
        assert_eq!(ChgHost.as_ref(), "chghost");
        assert_eq!(EchoMessage.as_ref(), "echo-message");
        assert_eq!(InviteNotify.as_ref(), "invite-notify");
    }
}
<commit_msg>Added support for server-time capability.<commit_after>//! Enumeration of all supported IRCv3 capability extensions.

/// List of all supported IRCv3 capability extensions from the
/// [IRCv3 specifications](http://ircv3.net/irc/).
#[derive(Debug, PartialEq)]
pub enum Capability {
    /// [multi-prefix](http://ircv3.net/specs/extensions/multi-prefix-3.1.html)
    MultiPrefix,
    /// [account-notify](http://ircv3.net/specs/extensions/account-notify-3.1.html)
    AccountNotify,
    /// [away-notify](http://ircv3.net/specs/extensions/away-notify-3.1.html)
    AwayNotify,
    /// [extended-join](http://ircv3.net/specs/extensions/extended-join-3.1.html)
    ExtendedJoin,
    /// [monitor](http://ircv3.net/specs/core/monitor-3.2.html)
    Monitor,
    /// [account-tag](http://ircv3.net/specs/extensions/account-tag-3.2.html)
    AccountTag,
    /// [cap-notify](http://ircv3.net/specs/extensions/cap-notify-3.2.html)
    CapNotify,
    /// [chghost](http://ircv3.net/specs/extensions/chghost-3.2.html)
    ChgHost,
    /// [echo-message](http://ircv3.net/specs/extensions/echo-message-3.2.html)
    EchoMessage,
    /// [invite-notify](http://ircv3.net/specs/extensions/invite-notify-3.2.html)
    InviteNotify,
    /// [server-time](http://ircv3.net/specs/extensions/server-time-3.2.html)
    ServerTime,
}

/// List of IRCv3 capability negotiation versions.
pub enum NegotiationVersion {
    /// [IRCv3.1](http://ircv3.net/specs/core/capability-negotiation-3.1.html)
    V301,
    /// [IRCv3.2](http://ircv3.net/specs/core/capability-negotiation-3.2.html)
    V302,
}

impl AsRef<str> for Capability {
    fn as_ref(&self) -> &str {
        match *self {
            Capability::MultiPrefix => "multi-prefix",
            Capability::AccountNotify => "account-notify",
            Capability::AwayNotify => "away-notify",
            Capability::ExtendedJoin => "extended-join",
            Capability::Monitor => "monitor",
            Capability::AccountTag => "account-tag",
            Capability::CapNotify => "cap-notify",
            Capability::ChgHost => "chghost",
            Capability::EchoMessage => "echo-message",
            Capability::InviteNotify => "invite-notify",
            Capability::ServerTime => "server-time",
        }
    }
}

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

    #[test]
    fn to_str() {
        assert_eq!(MultiPrefix.as_ref(), "multi-prefix");
        assert_eq!(AccountNotify.as_ref(), "account-notify");
        assert_eq!(AwayNotify.as_ref(), "away-notify");
        assert_eq!(ExtendedJoin.as_ref(), "extended-join");
        assert_eq!(Monitor.as_ref(), "monitor");
        assert_eq!(AccountTag.as_ref(), "account-tag");
        assert_eq!(CapNotify.as_ref(), "cap-notify");
        assert_eq!(ChgHost.as_ref(), "chghost");
        assert_eq!(EchoMessage.as_ref(), "echo-message");
        assert_eq!(InviteNotify.as_ref(), "invite-notify");
        assert_eq!(ServerTime.as_ref(), "server-time");
    }
}
<|endoftext|>


<commit_before>use conduit::RequestExt;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection};
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
use prometheus::Histogram;
use std::sync::Arc;
use std::{ops::Deref, time::Duration};
use thiserror::Error;
use url::Url;

use crate::config;
use crate::middleware::app::RequestApp;

#[derive(Clone)]
pub enum DieselPool {
    Pool {
        pool: r2d2::Pool<ConnectionManager<PgConnection>>,
        time_to_obtain_connection_metric: Histogram,
    },
    Test(Arc<ReentrantMutex<PgConnection>>),
}

impl DieselPool {
    pub(crate) fn new(
        url: &str,
        config: &config::DatabasePools,
        r2d2_config: r2d2::Builder<ConnectionManager<PgConnection>>,
        time_to_obtain_connection_metric: Histogram,
    ) -> Result<DieselPool, PoolError> {
        let manager = ConnectionManager::new(connection_url(config, url));

        // For crates.io we want the behavior of creating a database pool to be slightly different
        // than the defaults of R2D2: the library's build() method assumes its consumers always
        // need a database connection to operate, so it blocks creating a pool until a minimum
        // number of connections is available.
        //
        // crates.io can actually operate in a limited capacity without a database connections,
        // especially by serving download requests to our users. Because of that we don't want to
        // block indefinitely waiting for a connection: we instead need to wait for a bit (to avoid
        // serving errors for the first connections until the pool is initialized) and if we can't
        // establish any connection continue booting up the application. The database pool will
        // automatically be marked as unhealthy and the rest of the application will adapt.
        let pool = DieselPool::Pool {
            pool: r2d2_config.build_unchecked(manager),
            time_to_obtain_connection_metric,
        };
        match pool.wait_until_healthy(Duration::from_secs(5)) {
            Ok(()) => {}
            Err(PoolError::UnhealthyPool) => {}
            Err(err) => return Err(err),
        }

        Ok(pool)
    }

    pub(crate) fn new_test(config: &config::DatabasePools, url: &str) -> DieselPool {
        let conn = PgConnection::establish(&connection_url(config, url))
            .expect("failed to establish connection");
        conn.begin_test_transaction()
            .expect("failed to begin test transaction");
        DieselPool::Test(Arc::new(ReentrantMutex::new(conn)))
    }

    pub fn get(&self) -> Result<DieselPooledConn<'_>, PoolError> {
        match self {
            DieselPool::Pool {
                pool,
                time_to_obtain_connection_metric,
            } => time_to_obtain_connection_metric.observe_closure_duration(|| {
                if let Some(conn) = pool.try_get() {
                    Ok(DieselPooledConn::Pool(conn))
                } else if !self.is_healthy() {
                    Err(PoolError::UnhealthyPool)
                } else {
                    Ok(DieselPooledConn::Pool(pool.get()?))
                }
            }),
            DieselPool::Test(conn) => Ok(DieselPooledConn::Test(conn.lock())),
        }
    }

    pub fn state(&self) -> PoolState {
        match self {
            DieselPool::Pool { pool, .. } => {
                let state = pool.state();
                PoolState {
                    connections: state.connections,
                    idle_connections: state.idle_connections,
                }
            }
            DieselPool::Test(_) => PoolState {
                connections: 0,
                idle_connections: 0,
            },
        }
    }

    pub fn wait_until_healthy(&self, timeout: Duration) -> Result<(), PoolError> {
        match self {
            DieselPool::Pool { pool, .. } => match pool.get_timeout(timeout) {
                Ok(_) => Ok(()),
                Err(_) if !self.is_healthy() => Err(PoolError::UnhealthyPool),
                Err(err) => Err(PoolError::R2D2(err)),
            },
            DieselPool::Test(_) => Ok(()),
        }
    }

    fn is_healthy(&self) -> bool {
        self.state().connections > 0
    }
}

#[derive(Debug, Copy, Clone)]
pub struct PoolState {
    pub connections: u32,
    pub idle_connections: u32,
}

pub enum DieselPooledConn<'a> {
    Pool(r2d2::PooledConnection<ConnectionManager<PgConnection>>),
    Test(ReentrantMutexGuard<'a, PgConnection>),
}

impl Deref for DieselPooledConn<'_> {
    type Target = PgConnection;

    fn deref(&self) -> &Self::Target {
        match self {
            DieselPooledConn::Pool(conn) => conn.deref(),
            DieselPooledConn::Test(conn) => conn.deref(),
        }
    }
}

pub fn oneoff_connection_with_config(
    config: &config::DatabasePools,
) -> ConnectionResult<PgConnection> {
    let url = connection_url(config, &config.primary.url);
    PgConnection::establish(&url)
}

pub fn oneoff_connection() -> ConnectionResult<PgConnection> {
    let config = config::DatabasePools::full_from_environment(&config::Base::from_environment());
    oneoff_connection_with_config(&config)
}

pub fn connection_url(config: &config::DatabasePools, url: &str) -> String {
    let mut url = Url::parse(url).expect("Invalid database URL");

    if config.enforce_tls {
        maybe_append_url_param(&mut url, "sslmode", "require");
    }

    // Configure the time it takes for diesel to return an error when there is full packet loss
    // between the application and the database.
    maybe_append_url_param(
        &mut url,
        "tcp_user_timeout",
        &config.tcp_timeout_ms.to_string(),
    );

    url.into()
}

fn maybe_append_url_param(url: &mut Url, key: &str, value: &str) {
    if !url.query_pairs().any(|(k, _)| k == key) {
        url.query_pairs_mut().append_pair(key, value);
    }
}

pub trait RequestTransaction {
    /// Obtain a read/write database connection from the primary pool
    fn db_write(&self) -> Result<DieselPooledConn<'_>, PoolError>;

    /// Obtain a readonly database connection from the replica pool
    ///
    /// If the replica pool is disabled or unavailable, the primary pool is used instead.
    fn db_read(&self) -> Result<DieselPooledConn<'_>, PoolError>;
}

impl<T: RequestExt + ?Sized> RequestTransaction for T {
    fn db_write(&self) -> Result<DieselPooledConn<'_>, PoolError> {
        self.app().primary_database.get()
    }

    fn db_read(&self) -> Result<DieselPooledConn<'_>, PoolError> {
        let read_only_pool = self.app().read_only_replica_database.as_ref();
        match read_only_pool.map(|pool| pool.get()) {
            // Replica is available
            Some(Ok(connection)) => Ok(connection),

            // Replica is not available, but primary might be available
            Some(Err(PoolError::UnhealthyPool)) => self.app().primary_database.get(),

            // Replica failed
            Some(Err(error)) => Err(error),

            // Replica is disabled, but primary might be available
            None => self.app().primary_database.get(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ConnectionConfig {
    pub statement_timeout: u64,
    pub read_only: bool,
}

impl CustomizeConnection<PgConnection, r2d2::Error> for ConnectionConfig {
    fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), r2d2::Error> {
        use diesel::sql_query;

        sql_query(format!(
            "SET statement_timeout = {}",
            self.statement_timeout * 1000
        ))
        .execute(conn)
        .map_err(r2d2::Error::QueryError)?;
        if self.read_only {
            sql_query("SET default_transaction_read_only = 't'")
                .execute(conn)
                .map_err(r2d2::Error::QueryError)?;
        }
        Ok(())
    }
}

#[cfg(test)]
pub(crate) fn test_conn() -> PgConnection {
    let conn = PgConnection::establish(&crate::env("TEST_DATABASE_URL")).unwrap();
    conn.begin_test_transaction().unwrap();
    conn
}

#[derive(Debug, Error)]
pub enum PoolError {
    #[error(transparent)]
    R2D2(#[from] r2d2::PoolError),
    #[error("unhealthy database pool")]
    UnhealthyPool,
}
<commit_msg>db: Implement `db_read_prefer_primary()` function<commit_after>use conduit::RequestExt;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager, CustomizeConnection};
use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
use prometheus::Histogram;
use std::sync::Arc;
use std::{ops::Deref, time::Duration};
use thiserror::Error;
use url::Url;

use crate::config;
use crate::middleware::app::RequestApp;

#[derive(Clone)]
pub enum DieselPool {
    Pool {
        pool: r2d2::Pool<ConnectionManager<PgConnection>>,
        time_to_obtain_connection_metric: Histogram,
    },
    Test(Arc<ReentrantMutex<PgConnection>>),
}

impl DieselPool {
    pub(crate) fn new(
        url: &str,
        config: &config::DatabasePools,
        r2d2_config: r2d2::Builder<ConnectionManager<PgConnection>>,
        time_to_obtain_connection_metric: Histogram,
    ) -> Result<DieselPool, PoolError> {
        let manager = ConnectionManager::new(connection_url(config, url));

        // For crates.io we want the behavior of creating a database pool to be slightly different
        // than the defaults of R2D2: the library's build() method assumes its consumers always
        // need a database connection to operate, so it blocks creating a pool until a minimum
        // number of connections is available.
        //
        // crates.io can actually operate in a limited capacity without a database connections,
        // especially by serving download requests to our users. Because of that we don't want to
        // block indefinitely waiting for a connection: we instead need to wait for a bit (to avoid
        // serving errors for the first connections until the pool is initialized) and if we can't
        // establish any connection continue booting up the application. The database pool will
        // automatically be marked as unhealthy and the rest of the application will adapt.
        let pool = DieselPool::Pool {
            pool: r2d2_config.build_unchecked(manager),
            time_to_obtain_connection_metric,
        };
        match pool.wait_until_healthy(Duration::from_secs(5)) {
            Ok(()) => {}
            Err(PoolError::UnhealthyPool) => {}
            Err(err) => return Err(err),
        }

        Ok(pool)
    }

    pub(crate) fn new_test(config: &config::DatabasePools, url: &str) -> DieselPool {
        let conn = PgConnection::establish(&connection_url(config, url))
            .expect("failed to establish connection");
        conn.begin_test_transaction()
            .expect("failed to begin test transaction");
        DieselPool::Test(Arc::new(ReentrantMutex::new(conn)))
    }

    pub fn get(&self) -> Result<DieselPooledConn<'_>, PoolError> {
        match self {
            DieselPool::Pool {
                pool,
                time_to_obtain_connection_metric,
            } => time_to_obtain_connection_metric.observe_closure_duration(|| {
                if let Some(conn) = pool.try_get() {
                    Ok(DieselPooledConn::Pool(conn))
                } else if !self.is_healthy() {
                    Err(PoolError::UnhealthyPool)
                } else {
                    Ok(DieselPooledConn::Pool(pool.get()?))
                }
            }),
            DieselPool::Test(conn) => Ok(DieselPooledConn::Test(conn.lock())),
        }
    }

    pub fn state(&self) -> PoolState {
        match self {
            DieselPool::Pool { pool, .. } => {
                let state = pool.state();
                PoolState {
                    connections: state.connections,
                    idle_connections: state.idle_connections,
                }
            }
            DieselPool::Test(_) => PoolState {
                connections: 0,
                idle_connections: 0,
            },
        }
    }

    pub fn wait_until_healthy(&self, timeout: Duration) -> Result<(), PoolError> {
        match self {
            DieselPool::Pool { pool, .. } => match pool.get_timeout(timeout) {
                Ok(_) => Ok(()),
                Err(_) if !self.is_healthy() => Err(PoolError::UnhealthyPool),
                Err(err) => Err(PoolError::R2D2(err)),
            },
            DieselPool::Test(_) => Ok(()),
        }
    }

    fn is_healthy(&self) -> bool {
        self.state().connections > 0
    }
}

#[derive(Debug, Copy, Clone)]
pub struct PoolState {
    pub connections: u32,
    pub idle_connections: u32,
}

pub enum DieselPooledConn<'a> {
    Pool(r2d2::PooledConnection<ConnectionManager<PgConnection>>),
    Test(ReentrantMutexGuard<'a, PgConnection>),
}

impl Deref for DieselPooledConn<'_> {
    type Target = PgConnection;

    fn deref(&self) -> &Self::Target {
        match self {
            DieselPooledConn::Pool(conn) => conn.deref(),
            DieselPooledConn::Test(conn) => conn.deref(),
        }
    }
}

pub fn oneoff_connection_with_config(
    config: &config::DatabasePools,
) -> ConnectionResult<PgConnection> {
    let url = connection_url(config, &config.primary.url);
    PgConnection::establish(&url)
}

pub fn oneoff_connection() -> ConnectionResult<PgConnection> {
    let config = config::DatabasePools::full_from_environment(&config::Base::from_environment());
    oneoff_connection_with_config(&config)
}

pub fn connection_url(config: &config::DatabasePools, url: &str) -> String {
    let mut url = Url::parse(url).expect("Invalid database URL");

    if config.enforce_tls {
        maybe_append_url_param(&mut url, "sslmode", "require");
    }

    // Configure the time it takes for diesel to return an error when there is full packet loss
    // between the application and the database.
    maybe_append_url_param(
        &mut url,
        "tcp_user_timeout",
        &config.tcp_timeout_ms.to_string(),
    );

    url.into()
}

fn maybe_append_url_param(url: &mut Url, key: &str, value: &str) {
    if !url.query_pairs().any(|(k, _)| k == key) {
        url.query_pairs_mut().append_pair(key, value);
    }
}

pub trait RequestTransaction {
    /// Obtain a read/write database connection from the primary pool
    fn db_write(&self) -> Result<DieselPooledConn<'_>, PoolError>;

    /// Obtain a readonly database connection from the replica pool
    ///
    /// If the replica pool is disabled or unavailable, the primary pool is used instead.
    fn db_read(&self) -> Result<DieselPooledConn<'_>, PoolError>;

    /// Obtain a readonly database connection from the primary pool
    ///
    /// If the primary pool is unavailable, the replica pool is used instead, if not disabled.
    fn db_read_prefer_primary(&self) -> Result<DieselPooledConn<'_>, PoolError>;
}

impl<T: RequestExt + ?Sized> RequestTransaction for T {
    fn db_write(&self) -> Result<DieselPooledConn<'_>, PoolError> {
        self.app().primary_database.get()
    }

    fn db_read(&self) -> Result<DieselPooledConn<'_>, PoolError> {
        let read_only_pool = self.app().read_only_replica_database.as_ref();
        match read_only_pool.map(|pool| pool.get()) {
            // Replica is available
            Some(Ok(connection)) => Ok(connection),

            // Replica is not available, but primary might be available
            Some(Err(PoolError::UnhealthyPool)) => self.app().primary_database.get(),

            // Replica failed
            Some(Err(error)) => Err(error),

            // Replica is disabled, but primary might be available
            None => self.app().primary_database.get(),
        }
    }

    fn db_read_prefer_primary(&self) -> Result<DieselPooledConn<'_>, PoolError> {
        match (
            self.app().primary_database.get(),
            &self.app().read_only_replica_database,
        ) {
            // Primary is available
            (Ok(connection), _) => Ok(connection),

            // Primary is not available, but replica might be available
            (Err(PoolError::UnhealthyPool), Some(read_only_pool)) => read_only_pool.get(),

            // Primary failed and replica is disabled
            (Err(error), None) => Err(error),

            // Primary failed
            (Err(error), _) => Err(error),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ConnectionConfig {
    pub statement_timeout: u64,
    pub read_only: bool,
}

impl CustomizeConnection<PgConnection, r2d2::Error> for ConnectionConfig {
    fn on_acquire(&self, conn: &mut PgConnection) -> Result<(), r2d2::Error> {
        use diesel::sql_query;

        sql_query(format!(
            "SET statement_timeout = {}",
            self.statement_timeout * 1000
        ))
        .execute(conn)
        .map_err(r2d2::Error::QueryError)?;
        if self.read_only {
            sql_query("SET default_transaction_read_only = 't'")
                .execute(conn)
                .map_err(r2d2::Error::QueryError)?;
        }
        Ok(())
    }
}

#[cfg(test)]
pub(crate) fn test_conn() -> PgConnection {
    let conn = PgConnection::establish(&crate::env("TEST_DATABASE_URL")).unwrap();
    conn.begin_test_transaction().unwrap();
    conn
}

#[derive(Debug, Error)]
pub enum PoolError {
    #[error(transparent)]
    R2D2(#[from] r2d2::PoolError),
    #[error("unhealthy database pool")]
    UnhealthyPool,
}
<|endoftext|>


<commit_before>use std::hash::{Hash};
use std::slice::{
    Items,
};
use std::fmt;
use test;

// FIXME: These aren't stable, so a public wrapper of node/edge indices
// should be lifetimed just like pointers.
#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct NodeIndex(uint);
#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct EdgeIndex(uint);

const EdgeEnd: EdgeIndex = EdgeIndex(::std::uint::MAX);
//const InvalidNode: NodeIndex = NodeIndex(::std::uint::MAX);

/// Index into the NodeIndex and EdgeIndex arrays
#[deriving(Copy, Clone, Show, PartialEq)]
enum Dir {
    Out = 0,
    In = 1
}

#[deriving(Show)]
pub struct Node<N> {
    pub data: N,
    /// Next edge in outgoing and incoming edge lists.
    next: [EdgeIndex, ..2],
}

#[deriving(Show, Copy)]
pub struct Edge<E> {
    pub data: E,
    /// Next edge in outgoing and incoming edge lists.
    next: [EdgeIndex, ..2],
    /// Start and End node index
    node: [NodeIndex, ..2],
}

/// **OGraph\<N, E\>** is a directed graph using an adjacency list representation.
///
/// The graph maintains unique indices for nodes and edges, so both node and edge
/// data may be accessed mutably. Removing nodes or edges from the graph is expensive,
/// while adding is very cheap.
///
/// Based upon the graph implementation in rustc.
//#[deriving(Show)]
pub struct OGraph<N> {
    nodes: Vec<Node<N>>,
    edges: Vec<Edge<()>>,
}

impl<N: fmt::Show> fmt::Show for OGraph<N>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for n in self.nodes.iter() {
            try!(writeln!(f, "{}", n));
        }
        for n in self.edges.iter() {
            try!(writeln!(f, "{}", n));
        }
        Ok(())
    }
}

pub enum Pair<'a, T: 'a> {
    Both(&'a mut T, &'a mut T),
    One(&'a mut T),
    None,
}

pub fn index_twice<T>(slc: &mut [T], a: uint, b: uint) -> Pair<T>
{
    if a == b {
        slc.get_mut(a).map_or(Pair::None, Pair::One)
    } else {
        if a >= slc.len() || b >= slc.len() {
            Pair::None
        } else {
            // safe because a, b are in bounds and distinct
            unsafe {
                let ar = &mut *(slc.unsafe_mut(a) as *mut _);
                let br = &mut *(slc.unsafe_mut(b) as *mut _);
                Pair::Both(ar, br)
            }
        }
    }
}

/// Iterate over an edge list.
fn walk_edge_list<E, F: FnMut(EdgeIndex, &mut Edge<E>) -> bool>(
    fst: EdgeIndex, edges: &mut [Edge<E>], d: Dir, mut f: F)
{
    let k = d as uint;
    let mut cur = fst;
    loop {
        match edges.get_mut(cur.0) {
            None => {
                debug_assert!(cur == EdgeEnd);
                break;
            }
            Some(curedge) => {
                if !f(cur, curedge) {
                    break;
                }
                cur = curedge.next[k];
            }
        }
    }
}

impl<N> OGraph<N>
//where N: fmt::Show
{
    pub fn new() -> OGraph<N>
    {
        OGraph{nodes: Vec::new(), edges: Vec::new()}
    }

    pub fn add_node(&mut self, data: N) -> NodeIndex
    {
        let node = Node{data: data, next: [EdgeEnd, EdgeEnd]};
        let node_idx = NodeIndex(self.nodes.len());
        self.nodes.push(node);
        node_idx
    }

    pub fn node(&self, a: NodeIndex) -> Option<&N>
    {
        self.nodes.get(a.0).map(|n| &n.data)
    }

    pub fn node_mut(&mut self, a: NodeIndex) -> Option<&mut N>
    {
        self.nodes.get_mut(a.0).map(|n| &mut n.data)
    }

    pub fn neighbors(&self, a: NodeIndex) -> Neighbors<N>
    {
        Neighbors{
            graph: self,
            next: match self.nodes.get(a.0) {
                None => EdgeEnd,
                Some(n) => n.next[0],
            }
        }
    }

    pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex) -> EdgeIndex
    {
        let edge_idx = EdgeIndex(self.edges.len());
        match index_twice(self.nodes[mut], a.0, b.0) {
            Pair::None => panic!("NodeIndices out of bounds"),
            Pair::One(an) => {
                let edge = Edge {
                    data: (),
                    node: [a, b],
                    next: an.next,
                };
                an.next[0] = edge_idx;
                an.next[1] = edge_idx;
                self.edges.push(edge);
            }
            Pair::Both(an, bn) => {
                // a and b are different indices
                let edge = Edge {
                    data: (),
                    node: [a, b],
                    next: [an.next[0], bn.next[1]],
                };
                an.next[0] = edge_idx;
                bn.next[1] = edge_idx;
                self.edges.push(edge);
            }
        }
        edge_idx
    }

    pub fn remove_node(&mut self, a: NodeIndex) -> Option<N>
    {
        match self.nodes.get(a.0) {
            None => return None,
            _ => {}
        }
        for d in [Dir::Out, Dir::In].iter() {
            let k = *d as uint;
            /*
            println!("Starting edge removal for k={}, node={}", k, a);
            for (i, n) in self.nodes.iter().enumerate() {
                println!("Node {}: Edges={}", i, n.next);
            }
            for (i, ed) in self.edges.iter().enumerate() {
                println!("Edge {}: {}", i, ed);
            }
            */
            // Remove all edges from and to this node.
            loop {
                let next = self.nodes[a.0].next[k];
                if next == EdgeEnd {
                    break
                }
                self.remove_edge(next);
            }
        }

        // Adjust all node indices affected
        for edge in self.edges.iter_mut() {
            debug_assert!(edge.node[0] != a);
            debug_assert!(edge.node[1] != a);
            if edge.node[0] > a {
                edge.node[0].0 -= 1;
            }
            if edge.node[1] > a {
                edge.node[1].0 -= 1;
            }
        }
        self.nodes.remove(a.0).map(|n|n.data)
    }

    pub fn edge_mut(&mut self, e: EdgeIndex) -> &mut Edge<()>
    {
        &mut self.edges[e.0]
    }

    pub fn remove_edge(&mut self, e: EdgeIndex)
    {
        // every edge is part of two lists,
        // outgoing and incoming edges.
        // Remove it from both
        debug_assert!(self.edges.get(e.0).is_some(), "No such edge: {}", e);
        let (edge_node, edge_next) = match self.edges.get(e.0) {
            None => return,
            Some(x) => (x.node, x.next),
        };
        {
            // List out from A
            let node = match self.nodes.get_mut(edge_node[0].0) {
                Some(r) => r,
                None => { debug_assert!(false, "Edge.A not in nodes"); return }
            };
            let fst = node.next[0];
            if fst == e {
                //println!("Updating first edge 0 for node {}, set to {}", edge_node[0], edge_next[0]);
                node.next[0] = edge_next[0];
            } else {
                walk_edge_list(fst, self.edges[mut], Dir::Out, |eidx, curedge| {
                    if curedge.next[0] == e {
                        curedge.next[0] = edge_next[0];
                        false
                    } else { true }
                });
            }
        }
        {
            // List in to B
            let node = match self.nodes.get_mut(edge_node[1].0) {
                Some(r) => r,
                None => { debug_assert!(false, "Edge.B not in nodes"); return }
            };
            let fst = node.next[1];
            if fst == e {
                //println!("Updating first edge 1 for node {}, set to {}", edge_node[1], edge_next[1]);
                node.next[1] = edge_next[1];
            } else {
                walk_edge_list(fst, self.edges[mut], Dir::In, |eidx, curedge| {
                    if curedge.next[1] == e {
                        curedge.next[1] = edge_next[1];
                        false
                    } else { true }
                });
            }
        }
        self.remove_edge_adjust_indices(e);
    }

    fn remove_edge_adjust_indices(&mut self, e: EdgeIndex) -> Option<()>
    {
        // swap_remove the edge -- only the removed edge
        // and the edge swapped into place are affected and need updating
        // indices.
        let edge = self.edges.swap_remove(e.0).unwrap();
        let swap = match self.edges.get(e.0) {
            // no elment needed to be swapped.
            None => return Some(edge.data),
            Some(ed) => ed.node,
        };
        let swapped_e = EdgeIndex(self.edges.len());

        {
            // List out from A
            let node = &mut self.nodes[swap[0].0];
            let fst = node.next[0];
            if fst == swapped_e {
                node.next[0] = e;
            } else {
                walk_edge_list(fst, self.edges[mut], Dir::Out, |eidx, curedge| {
                    if curedge.next[0] == swapped_e {
                        curedge.next[0] = e;
                        false
                    } else { true }
                });
            }
        }
        {
            // List in to B
            let node = &mut self.nodes[swap[1].0];
            let fst = node.next[1];
            if fst == swapped_e {
                node.next[1] = e;
            } else {
                walk_edge_list(fst, self.edges[mut], Dir::In, |eidx, curedge| {
                    if curedge.next[1] == swapped_e {
                        curedge.next[1] = e;
                        false
                    } else { true }
                });
            }
        }
        // All that refer to the swapped edge need to update.
        //
        /*
        // Edge lists are fine, so remove the edge and adjust all edge indices in nodes
        let edge_data = self.edges.remove(e.0).unwrap().data;
        for &k in [0u, 1].iter() {
            for node in self.nodes.iter_mut() {
                debug_assert!(node.next[k] != e);
                if node.next[k] != EdgeEnd && node.next[k] > e {
                    node.next[k].0 -= 1;
                }
            }
            for edge in self.edges.iter_mut() {
                debug_assert!(edge.next[k] != e);
                if edge.next[k] != EdgeEnd && edge.next[k] > e {
                    edge.next[k].0 -= 1;
                }
            }
        }
        */
        let edge_data = edge.data;
        Some(edge_data)
    }
}

pub struct Neighbors<'a, N: 'a> {
    graph: &'a OGraph<N>,
    next: EdgeIndex,
}

impl<'a, N> Iterator<NodeIndex> for Neighbors<'a, N>
{
    fn next(&mut self) -> Option<NodeIndex>
    {
        match self.graph.edges.get(self.next.0) {
            None => None,
            Some(edge) => {
                self.next = edge.next[0];
                Some(edge.node[1])
            }
        }
    }
}

#[bench]
fn bench_inser(b: &mut test::Bencher) {
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    for x in range(1, 125) {
        let n = og.add_node(x);
        og.add_edge(fst, n);
    }
    b.iter(|| {
        og.add_node(1)
    })
}

#[bench]
fn bench_remove(b: &mut test::Bencher) {
    // removal is very slow in a big graph.
    // and this one doesn't even have many nodes.
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    let mut prev = fst;
    for x in range(1, 1250) {
        let n = og.add_node(x);
        og.add_edge(prev, n);
        prev = n;
    }
    //println!("{}", og);
    b.iter(|| {
        for _ in range(0, 100i) {
            og.remove_node(fst);
        }
    })
}
<commit_msg>Reduce duplication by looping over Out, In<commit_after>use std::hash::{Hash};
use std::slice::{
    Items,
};
use std::fmt;
use test;

// FIXME: These aren't stable, so a public wrapper of node/edge indices
// should be lifetimed just like pointers.
#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct NodeIndex(uint);
#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct EdgeIndex(uint);

const EdgeEnd: EdgeIndex = EdgeIndex(::std::uint::MAX);
//const InvalidNode: NodeIndex = NodeIndex(::std::uint::MAX);

/// Index into the NodeIndex and EdgeIndex arrays
#[deriving(Copy, Clone, Show, PartialEq)]
enum Dir {
    Out = 0,
    In = 1
}

const DIRECTIONS: [Dir, ..2] = [Dir::Out, Dir::In];

#[deriving(Show)]
pub struct Node<N> {
    pub data: N,
    /// Next edge in outgoing and incoming edge lists.
    next: [EdgeIndex, ..2],
}

#[deriving(Show, Copy)]
pub struct Edge<E> {
    pub data: E,
    /// Next edge in outgoing and incoming edge lists.
    next: [EdgeIndex, ..2],
    /// Start and End node index
    node: [NodeIndex, ..2],
}

/// **OGraph\<N, E\>** is a directed graph using an adjacency list representation.
///
/// The graph maintains unique indices for nodes and edges, so both node and edge
/// data may be accessed mutably. Removing nodes or edges from the graph is expensive,
/// while adding is very cheap.
///
/// Based upon the graph implementation in rustc.
//#[deriving(Show)]
pub struct OGraph<N> {
    nodes: Vec<Node<N>>,
    edges: Vec<Edge<()>>,
}

impl<N: fmt::Show> fmt::Show for OGraph<N>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for n in self.nodes.iter() {
            try!(writeln!(f, "{}", n));
        }
        for n in self.edges.iter() {
            try!(writeln!(f, "{}", n));
        }
        Ok(())
    }
}

pub enum Pair<'a, T: 'a> {
    Both(&'a mut T, &'a mut T),
    One(&'a mut T),
    None,
}

pub fn index_twice<T>(slc: &mut [T], a: uint, b: uint) -> Pair<T>
{
    if a == b {
        slc.get_mut(a).map_or(Pair::None, Pair::One)
    } else {
        if a >= slc.len() || b >= slc.len() {
            Pair::None
        } else {
            // safe because a, b are in bounds and distinct
            unsafe {
                let ar = &mut *(slc.unsafe_mut(a) as *mut _);
                let br = &mut *(slc.unsafe_mut(b) as *mut _);
                Pair::Both(ar, br)
            }
        }
    }
}

/// Iterate over an edge list.
fn walk_edge_list<E, F: FnMut(EdgeIndex, &mut Edge<E>) -> bool>(
    fst: EdgeIndex, edges: &mut [Edge<E>], d: Dir, mut f: F)
{
    let k = d as uint;
    let mut cur = fst;
    loop {
        match edges.get_mut(cur.0) {
            None => {
                debug_assert!(cur == EdgeEnd);
                break;
            }
            Some(curedge) => {
                if !f(cur, curedge) {
                    break;
                }
                cur = curedge.next[k];
            }
        }
    }
}

impl<N> OGraph<N>
//where N: fmt::Show
{
    pub fn new() -> OGraph<N>
    {
        OGraph{nodes: Vec::new(), edges: Vec::new()}
    }

    pub fn add_node(&mut self, data: N) -> NodeIndex
    {
        let node = Node{data: data, next: [EdgeEnd, EdgeEnd]};
        let node_idx = NodeIndex(self.nodes.len());
        self.nodes.push(node);
        node_idx
    }

    pub fn node(&self, a: NodeIndex) -> Option<&N>
    {
        self.nodes.get(a.0).map(|n| &n.data)
    }

    pub fn node_mut(&mut self, a: NodeIndex) -> Option<&mut N>
    {
        self.nodes.get_mut(a.0).map(|n| &mut n.data)
    }

    pub fn neighbors(&self, a: NodeIndex) -> Neighbors<N>
    {
        Neighbors{
            graph: self,
            next: match self.nodes.get(a.0) {
                None => EdgeEnd,
                Some(n) => n.next[0],
            }
        }
    }

    pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex) -> EdgeIndex
    {
        let edge_idx = EdgeIndex(self.edges.len());
        match index_twice(self.nodes[mut], a.0, b.0) {
            Pair::None => panic!("NodeIndices out of bounds"),
            Pair::One(an) => {
                let edge = Edge {
                    data: (),
                    node: [a, b],
                    next: an.next,
                };
                an.next[0] = edge_idx;
                an.next[1] = edge_idx;
                self.edges.push(edge);
            }
            Pair::Both(an, bn) => {
                // a and b are different indices
                let edge = Edge {
                    data: (),
                    node: [a, b],
                    next: [an.next[0], bn.next[1]],
                };
                an.next[0] = edge_idx;
                bn.next[1] = edge_idx;
                self.edges.push(edge);
            }
        }
        edge_idx
    }

    pub fn remove_node(&mut self, a: NodeIndex) -> Option<N>
    {
        match self.nodes.get(a.0) {
            None => return None,
            _ => {}
        }
        for d in DIRECTIONS.iter() {
            let k = *d as uint;
            /*
            println!("Starting edge removal for k={}, node={}", k, a);
            for (i, n) in self.nodes.iter().enumerate() {
                println!("Node {}: Edges={}", i, n.next);
            }
            for (i, ed) in self.edges.iter().enumerate() {
                println!("Edge {}: {}", i, ed);
            }
            */
            // Remove all edges from and to this node.
            loop {
                let next = self.nodes[a.0].next[k];
                if next == EdgeEnd {
                    break
                }
                self.remove_edge(next);
            }
        }

        // Adjust all node indices affected
        for edge in self.edges.iter_mut() {
            debug_assert!(edge.node[0] != a);
            debug_assert!(edge.node[1] != a);
            if edge.node[0] > a {
                edge.node[0].0 -= 1;
            }
            if edge.node[1] > a {
                edge.node[1].0 -= 1;
            }
        }
        self.nodes.remove(a.0).map(|n|n.data)
    }

    pub fn edge_mut(&mut self, e: EdgeIndex) -> &mut Edge<()>
    {
        &mut self.edges[e.0]
    }

    pub fn remove_edge(&mut self, e: EdgeIndex)
    {
        // every edge is part of two lists,
        // outgoing and incoming edges.
        // Remove it from both
        debug_assert!(self.edges.get(e.0).is_some(), "No such edge: {}", e);
        let (edge_node, edge_next) = match self.edges.get(e.0) {
            None => return,
            Some(x) => (x.node, x.next),
        };
        // List out from A
        // List in from B
        for &d in DIRECTIONS.iter() {
            let k = d as uint;
            let node = match self.nodes.get_mut(edge_node[k].0) {
                Some(r) => r,
                None => { debug_assert!(false, "Edge not in nodes"); return }
            };
            let fst = node.next[k];
            if fst == e {
                //println!("Updating first edge 0 for node {}, set to {}", edge_node[0], edge_next[0]);
                node.next[k] = edge_next[k];
            } else {
                walk_edge_list(fst, self.edges[mut], d, |eidx, curedge| {
                    if curedge.next[k] == e {
                        curedge.next[k] = edge_next[k];
                        false
                    } else { true }
                });
            }
        }
        self.remove_edge_adjust_indices(e);
    }

    fn remove_edge_adjust_indices(&mut self, e: EdgeIndex) -> Option<()>
    {
        // swap_remove the edge -- only the removed edge
        // and the edge swapped into place are affected and need updating
        // indices.
        let edge = self.edges.swap_remove(e.0).unwrap();
        let swap = match self.edges.get(e.0) {
            // no elment needed to be swapped.
            None => return Some(edge.data),
            Some(ed) => ed.node,
        };
        let swapped_e = EdgeIndex(self.edges.len());

        // List out from A
        // List in to B
        for &d in DIRECTIONS.iter() {
            let k = d as uint;
            let node = &mut self.nodes[swap[k].0];
            let fst = node.next[k];
            if fst == swapped_e {
                node.next[k] = e;
            } else {
                walk_edge_list(fst, self.edges[mut], d, |eidx, curedge| {
                    if curedge.next[k] == swapped_e {
                        curedge.next[k] = e;
                        false
                    } else { true }
                });
            }
        }
        let edge_data = edge.data;
        Some(edge_data)
    }
}

pub struct Neighbors<'a, N: 'a> {
    graph: &'a OGraph<N>,
    next: EdgeIndex,
}

impl<'a, N> Iterator<NodeIndex> for Neighbors<'a, N>
{
    fn next(&mut self) -> Option<NodeIndex>
    {
        match self.graph.edges.get(self.next.0) {
            None => None,
            Some(edge) => {
                self.next = edge.next[0];
                Some(edge.node[1])
            }
        }
    }
}

#[bench]
fn bench_inser(b: &mut test::Bencher) {
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    for x in range(1, 125) {
        let n = og.add_node(x);
        og.add_edge(fst, n);
    }
    b.iter(|| {
        og.add_node(1)
    })
}

#[bench]
fn bench_remove(b: &mut test::Bencher) {
    // removal is very slow in a big graph.
    // and this one doesn't even have many nodes.
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    let mut prev = fst;
    for x in range(1, 1250) {
        let n = og.add_node(x);
        og.add_edge(prev, n);
        prev = n;
    }
    //println!("{}", og);
    b.iter(|| {
        for _ in range(0, 100i) {
            og.remove_node(fst);
        }
    })
}
<|endoftext|>


<commit_before>extern crate num;
use self::num::integer::Integer;
use std::rand::{task_rng, Rng};
use cpu::{Cpu, join_bytes, separate_byte};
use std::mem;

pub fn to_opcode(v: i8) -> Opcode {
    unsafe { mem::transmute(v) }
}

pub enum Opcode {
    Nop = 0,
	Cls,
	Vblnk,
	Bgc,
	Spr,
	Drw,
	Drw2,
	Rnd,
	Flip,
	Snd0,
	Snd1,
	Snd2,
	Snd3,
	Snp,
	Sng,
	Jmp = 0x10,
	Jmc,
	Jx,
	Jme,
	Call,
	Ret,
	Jmp2,
	Cx,
	Call2,
	Ldi = 0x20,
	Ldi2,
	Ldm,
	Ldm2,
	Mov,
	Stm = 0x30,
	Stm2,
	Addi = 0x40,
	Add,
	Add2,
	Subi = 0x50,
	Sub,
	Sub2,
	Cmpi,
	Cmp,
	Andi = 0x60,
	And,
	Tsti,
	Tst,
	Ori = 0x70,
	Or,
	Or2,
	Xori = 0x80,
	Xor,
	Xor2,
	Muli = 0x90,
	Mul,
	Mul2,
	Divi = 0xA0,
	Div,
	Div2,
	Modi,
	Mod,
	Mod2,
	Remi,
	Rem,
	Rem2,
	Shl = 0xB0,
	Shr,
	Sar,
	Shl2,
	Shr2,
	Sar2,
	Push = 0xC0,
	Pop,
	Pushall,
	Popall,
	Pushf,
	Popf,
	Pal = 0xD0,
	Pal2,
	Noti = 0xE0,
	Not,
	Not2,
	Negi,
	Neg,
	Neg2,
}

impl Opcode {
	pub fn execute(&self, cpu: &mut Cpu, byte1: i8, byte2: i8, byte3: i8) {
	    match *self {
		    Nop => nop(),
			Cls => cls(cpu),
			Vblnk => vblnk(cpu),
			Bgc => bgc(cpu, byte2),
			Spr => spr(cpu, byte2 as u8, byte3 as u8),
			Drw => nop(),
			Drw2 => nop(),
			Rnd => rnd(cpu, byte1, join_bytes(byte2, byte3)),
			Flip => flip(cpu, byte3),
			Snd0 => nop(),
			Snd1 => nop(),
			Snd2 => nop(),
			Snd3 => nop(),
			Snp => nop(),
			Sng => nop(),
			Jmp => jmp(cpu, join_bytes(byte2, byte3)),
			Jmc => jmc(cpu, join_bytes(byte2, byte3)),
			Jx => jx(cpu, byte1, join_bytes(byte2, byte3)),
			Jme => jme(cpu, separate_byte(byte1), join_bytes(byte2, byte3)),
			Call => call(cpu, join_bytes(byte2, byte3)),
			Ret => ret(cpu),
			Jmp2 => nop(),
			Cx => nop(),
			Call2 => nop(),
			Ldi => nop(),
			Ldi2 => nop(),
			Ldm => nop(),
			Ldm2 => nop(),
			Mov => nop(),
			Stm => nop(),
			Stm2 => nop(),
			Addi => nop(),
			Add => nop(),
			Add2 => nop(),
			Subi => nop(),
			Sub => nop(),
			Sub2 => nop(),
			Cmpi => nop(),
			Cmp => nop(),
			Andi => nop(),
			And => nop(),
			Tsti => nop(),
			Tst => nop(),
			Ori => nop(),
			Or => nop(),
			Or2 => nop(),
			Xori => nop(),
			Xor => nop(),
			Xor2 => nop(),
			Muli => nop(),
			Mul => nop(),
			Mul2 => nop(),
			Divi => nop(),
			Div => nop(),
			Div2 => nop(),
			Modi => nop(),
			Mod => nop(),
			Mod2 => nop(),
			Remi => nop(),
			Rem => nop(),
			Rem2 => nop(),
			Shl => nop(),
			Shr => nop(),
			Sar => nop(),
			Shl2 => nop(),
			Shr2 => nop(),
			Sar2 => nop(),
			Push => nop(),
			Pop => nop(),
			Pushall => nop(),
			Popall => nop(),
			Pushf => nop(),
			Popf => nop(),
			Pal => nop(),
			Pal2 => nop(),
			Noti => nop(),
			Not => nop(),
			Not2 => nop(),
			Negi => nop(),
			Neg => nop(),
			Neg2 => nop(),
		}
	}
}

fn nop() -> () {
	()
}

fn cls(cpu: &mut Cpu) -> () {
	cpu.clear_fg_bg();
}

fn vblnk(cpu: &mut Cpu)-> () {
	if !cpu.vblank {
		cpu.pc = cpu.pc - 4
	}
}

fn bgc(cpu: &mut Cpu, byte: i8) -> () {
	cpu.set_bg(byte as u8);
}

fn spr(cpu: &mut Cpu, ll: u8, hh: u8) -> () {
	cpu.set_spr_wh(ll, hh);
}

fn flip(cpu: &mut Cpu, byte3: i8) -> () {
	cpu.flip(byte3 > 1, byte3.is_odd())
}

fn rnd(cpu: &mut Cpu, rx: i8, max_rand: i16) -> () {
	cpu.set_rx(rx, task_rng().gen_range(0, max_rand as u16) as i16);
}

fn jmp(cpu: &mut Cpu, new_dir: i16) -> () {
	cpu.pc = new_dir;
}

fn jmc(cpu: &mut Cpu, new_dir: i16) -> () {
	if cpu.has_carry() {
		jmp(cpu, new_dir);
	}
}

fn jx(cpu: &mut Cpu, flag_index: i8, new_dir: i16) -> () {
	if cpu.check_flags(flag_index) {
		jmp(cpu, new_dir);
	}
}

fn jme(cpu: &mut Cpu, tuple: (i8, i8), new_dir: i16) -> () {
	let (x, y) = tuple;
	if cpu.get_rx(x) == cpu.get_rx(y) {
		jmp(cpu, new_dir);
	}
}

fn call(cpu: &mut Cpu, new_dir: i16) -> () {
	let pc = cpu.pc;
	cpu.push_stack(pc);
	cpu.pc = new_dir;
}

fn ret(cpu: &mut Cpu) -> () {
	let pc = cpu.pop_stack();
	cpu.pc = pc;
}<commit_msg>Finished jump opcodes<commit_after>extern crate num;
use self::num::integer::Integer;
use std::rand::{task_rng, Rng};
use cpu::{Cpu, join_bytes, separate_byte};
use std::mem;

pub fn to_opcode(v: i8) -> Opcode {
    unsafe { mem::transmute(v) }
}

pub enum Opcode {
    Nop = 0,
	Cls,
	Vblnk,
	Bgc,
	Spr,
	Drw,
	Drw2,
	Rnd,
	Flip,
	Snd0,
	Snd1,
	Snd2,
	Snd3,
	Snp,
	Sng,
	Jmp = 0x10,
	Jmc,
	Jx,
	Jme,
	Call,
	Ret,
	Jmp2,
	Cx,
	Call2,
	Ldi = 0x20,
	Ldi2,
	Ldm,
	Ldm2,
	Mov,
	Stm = 0x30,
	Stm2,
	Addi = 0x40,
	Add,
	Add2,
	Subi = 0x50,
	Sub,
	Sub2,
	Cmpi,
	Cmp,
	Andi = 0x60,
	And,
	Tsti,
	Tst,
	Ori = 0x70,
	Or,
	Or2,
	Xori = 0x80,
	Xor,
	Xor2,
	Muli = 0x90,
	Mul,
	Mul2,
	Divi = 0xA0,
	Div,
	Div2,
	Modi,
	Mod,
	Mod2,
	Remi,
	Rem,
	Rem2,
	Shl = 0xB0,
	Shr,
	Sar,
	Shl2,
	Shr2,
	Sar2,
	Push = 0xC0,
	Pop,
	Pushall,
	Popall,
	Pushf,
	Popf,
	Pal = 0xD0,
	Pal2,
	Noti = 0xE0,
	Not,
	Not2,
	Negi,
	Neg,
	Neg2,
}

impl Opcode {
	pub fn execute(&self, cpu: &mut Cpu, byte1: i8, byte2: i8, byte3: i8) {
	    match *self {
		    Nop => nop(),
			Cls => cls(cpu),
			Vblnk => vblnk(cpu),
			Bgc => bgc(cpu, byte2),
			Spr => spr(cpu, byte2 as u8, byte3 as u8),
			Drw => nop(),
			Drw2 => nop(),
			Rnd => rnd(cpu, byte1, join_bytes(byte2, byte3)),
			Flip => flip(cpu, byte3),
			Snd0 => nop(),
			Snd1 => nop(),
			Snd2 => nop(),
			Snd3 => nop(),
			Snp => nop(),
			Sng => nop(),
			Jmp => jmp(cpu, join_bytes(byte2, byte3)),
			Jmc => jmc(cpu, join_bytes(byte2, byte3)),
			Jx => jx(cpu, byte1, join_bytes(byte2, byte3)),
			Jme => jme(cpu, separate_byte(byte1), join_bytes(byte2, byte3)),
			Call => call(cpu, join_bytes(byte2, byte3)),
			Ret => ret(cpu),
			Jmp2 => {
				let rx = cpu.get_rx(byte1);
				jmp(cpu, rx)
			},
			Cx => cx(cpu, byte1, join_bytes(byte2, byte3)),
			Call2 => {
				let rx = cpu.get_rx(byte1);
				call(cpu, rx)
			},
			Ldi => nop(),
			Ldi2 => nop(),
			Ldm => nop(),
			Ldm2 => nop(),
			Mov => nop(),
			Stm => nop(),
			Stm2 => nop(),
			Addi => nop(),
			Add => nop(),
			Add2 => nop(),
			Subi => nop(),
			Sub => nop(),
			Sub2 => nop(),
			Cmpi => nop(),
			Cmp => nop(),
			Andi => nop(),
			And => nop(),
			Tsti => nop(),
			Tst => nop(),
			Ori => nop(),
			Or => nop(),
			Or2 => nop(),
			Xori => nop(),
			Xor => nop(),
			Xor2 => nop(),
			Muli => nop(),
			Mul => nop(),
			Mul2 => nop(),
			Divi => nop(),
			Div => nop(),
			Div2 => nop(),
			Modi => nop(),
			Mod => nop(),
			Mod2 => nop(),
			Remi => nop(),
			Rem => nop(),
			Rem2 => nop(),
			Shl => nop(),
			Shr => nop(),
			Sar => nop(),
			Shl2 => nop(),
			Shr2 => nop(),
			Sar2 => nop(),
			Push => nop(),
			Pop => nop(),
			Pushall => nop(),
			Popall => nop(),
			Pushf => nop(),
			Popf => nop(),
			Pal => nop(),
			Pal2 => nop(),
			Noti => nop(),
			Not => nop(),
			Not2 => nop(),
			Negi => nop(),
			Neg => nop(),
			Neg2 => nop(),
		}
	}
}

fn nop() -> () {
	()
}

fn cls(cpu: &mut Cpu) -> () {
	cpu.clear_fg_bg();
}

fn vblnk(cpu: &mut Cpu)-> () {
	if !cpu.vblank {
		cpu.pc = cpu.pc - 4
	}
}

fn bgc(cpu: &mut Cpu, byte: i8) -> () {
	cpu.set_bg(byte as u8);
}

fn spr(cpu: &mut Cpu, ll: u8, hh: u8) -> () {
	cpu.set_spr_wh(ll, hh);
}

fn flip(cpu: &mut Cpu, byte3: i8) -> () {
	cpu.flip(byte3 > 1, byte3.is_odd())
}

fn rnd(cpu: &mut Cpu, rx: i8, max_rand: i16) -> () {
	cpu.set_rx(rx, task_rng().gen_range(0, max_rand as u16) as i16);
}

fn jmp(cpu: &mut Cpu, new_dir: i16) -> () {
	cpu.pc = new_dir;
}

fn jmc(cpu: &mut Cpu, new_dir: i16) -> () {
	if cpu.has_carry() {
		jmp(cpu, new_dir);
	}
}

fn jx(cpu: &mut Cpu, flag_index: i8, new_dir: i16) -> () {
	if cpu.check_flags(flag_index) {
		jmp(cpu, new_dir);
	}
}

fn jme(cpu: &mut Cpu, tuple: (i8, i8), new_dir: i16) -> () {
	let (x, y) = tuple;
	if cpu.get_rx(x) == cpu.get_rx(y) {
		jmp(cpu, new_dir);
	}
}

fn call(cpu: &mut Cpu, new_dir: i16) -> () {
	let pc = cpu.pc;
	cpu.push_stack(pc);
	cpu.pc = new_dir;
}

fn ret(cpu: &mut Cpu) -> () {
	let pc = cpu.pop_stack();
	cpu.pc = pc;
}

fn cx(cpu: &mut Cpu, flag_index: i8, new_dir: i16) -> () {
	if cpu.check_flags(flag_index) {
		call(cpu, new_dir);
	}
}<|endoftext|>


<commit_before>//! Utilities for reading and writing

use std::io;

use byteorder::{Error, Result};

/// Reads enough data to fill the buffer, or errors.
///
/// This is taken from the byteorder souce, where it is a private method.
pub fn read_full<R: io::Read + ?Sized>(reader: &mut R, buf: &mut [u8]) -> Result<()> {
    let mut nread = 0usize;
    while nread < buf.len() {
        match reader.read(&mut buf[nread..]) {
            Ok(0) => return Err(Error::UnexpectedEOF),
            Ok(n) => nread += n,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(From::from(e)),
        }
    }
    Ok(())
}

/// Writes a certain number of null bytes to a buffer.
///
/// This is basically `write_all` that returns the number of bytes written.
pub fn write_zeros<W: io::Write>(writer: &mut W, n: usize) -> io::Result<usize> {
    let buf = vec![0; n];
    try!(writer.write_all(&buf[..]));
    Ok(n)
}
<commit_msg>Remove las::io as unused<commit_after><|endoftext|>


<commit_before>use std::str::from_utf8;
use nom::{line_ending, newline, not_line_ending, GetInput, IResult};

named!(quoted,
       delimited!(tag!("\""), take_until!("\""), tag!("\"")));

named!(quote_and_done,
       alt!(terminated!(quoted, eof!()) | terminated!(quoted, line_ending)));

named!(property, take_until!(" = "));

named!(bare_key, preceded!(not!(tag!("\"")), not_line_ending));

named!(key_value<&[u8], Line>,
       do_parse!(key: property >>
                 tag!(" = ") >>
                 value: alt!(
                     quote_and_done |
                     bare_key
                 ) >>
                 (KeyValue(to_string(key), to_string(value)))));

named!(comment<&[u8], Line>,
       do_parse!(
           content: preceded!(tag!("#"), not_line_ending) >>
           (Comment(to_string(content)))));

named!(line<&[u8], Line>, alt!(
        ws!(key_value) |
        ws!(comment)));

named!(parse<&[u8], Vec<Line> >, many0!(line));

#[derive(Debug, PartialEq)]
pub enum Line {
    KeyValue(String, String),
    Comment(String),
}
use self::Line::*;

#[derive(Debug, PartialEq)]
pub enum ParseError {
    Syntax,
}
use self::ParseError::*;

impl From<::nom::IError> for ParseError {
    fn from(error: ::nom::IError) -> ParseError {
        match error {
            ::nom::IError::Incomplete(_) => Syntax,
            _ => panic!("Error evaluating parser"),
        }
    }
}

fn to_string(bytes: &[u8]) -> String {
    from_utf8(bytes)
        .expect("Managed to parse but failed to convert non-utf8")
        .to_string()
}

pub fn get_lines(s: &str) -> Result<Vec<Line>, ParseError> {
    let parsed = parse(s.as_bytes());
    match parsed.remaining_input() {
        Some(bytes) if (bytes.len() != 0) => return Err(Syntax),
        _ => {}
    }
    parsed.to_full_result().map_err(::std::convert::From::from)
}

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

    fn assert_syntax_error(contents: &str) {
        assert_eq!(get_lines(contents).unwrap_err(),
               ParseError::Syntax);
    }

    fn get_lines_vec(contents: &str) -> Vec<Line> {
        get_lines(contents).unwrap()
    }

    fn key_value(key: &str, value: &str) -> super::Line {
        super::Line::KeyValue(key.to_string(), value.to_string())
    }

    #[test]
    fn root_level_empty() {
        assert_eq!(get_lines_vec(""), vec![]);
    }

    #[test]
    fn root_level_single_property() {
        let result = vec![
            key_value("hostname", "dynamo"),
        ];
        assert_eq!(get_lines("hostname = \"dynamo\"").unwrap(), result);
    }

    #[test]
    fn root_level_three_properties() {
        let result = vec![
            key_value("hostname", "dynamo"),
            key_value("port", "5153"),
            key_value("path", "/foo/bar"),
        ];
        assert_eq!(
            get_lines("hostname = \"dynamo\"\nport = \"5153\"\npath = \"/foo/bar\"").unwrap(),
            result);
    }

    #[test]
    fn just_a_word() {
        assert_syntax_error("hostname");
    }

    #[test]
    fn word_and_equals() {
        assert_syntax_error("hostname = ");
    }

    #[test]
    #[ignore]
    fn root_level_property_with_many_dots_together() {
        assert_syntax_error("host..name = \"dynamo\"");
    }

    #[test]
    fn quoted_word_with_stuff_after() {
        assert_syntax_error("hostname = \"dynamo\"great");
        assert_syntax_error("hostname = \"dynamo\" great");
    }

    #[test]
    fn root_level_property_with_dots() {
        let result = vec![
            key_value("host.name", "dynamo"),
        ];
        assert_eq!(get_lines("host.name = \"dynamo\"").unwrap(), result);
    }

    #[test]
    fn root_level_property_with_underscores() {
        let result = vec![
            key_value("host_name", "dynamo"),
        ];
        assert_eq!(get_lines("host_name = \"dynamo\"").unwrap(), result);
    }

    #[test]
    fn bare_words() {
        let result = vec![
            key_value("hostname", "dynamo"),
        ];
        assert_eq!(get_lines("hostname = dynamo").unwrap(), result);
    }

    #[test]
    fn multiline_bare_words() {
        let result = vec![
            key_value("hostname", "dynamo"),
            key_value("port", "5153"),
            key_value("path", "/foo/bar"),
        ];
        assert_eq!(
            get_lines("hostname = dynamo\nport = 5153\npath = /foo/bar").unwrap(),
            result);
    }

    #[test]
    fn bare_words_until_end_of_line() {
        let result = vec![
            key_value("hostname", "dynamo is bae"),
        ];
        assert_eq!(get_lines("hostname = dynamo is bae").unwrap(), result);
    }

    #[test]
    fn comments() {
        let result = vec![
            Line::Comment(" Just a comment.".to_string()),
        ];
        assert_eq!(get_lines("# Just a comment.").unwrap(), result);
    }

    #[test]
    fn comment_after_bare_words() {
        let result = vec![
            key_value("hostname", "dynamo # This isn't actually a comment."),
        ];
        assert_eq!(
            get_lines("hostname = dynamo # This isn't actually a comment.").unwrap(),
            result);
    }

    #[test]
    fn comment_after_quote() {
        assert_syntax_error("hostname = \"dynamo\" # This isn't actually a comment.");
    }

    #[test]
    #[ignore]
    fn simple_multiline() {
        // let result = vec![
        //     Namespace("host".to_string(), vec![
        //         key_value("name", "dynamo"),
        //     ]),
        // ];
        // assert_eq!(get_lines("host\n    name = dynamo").unwrap(), result);
    }
}
<commit_msg>cleanup parser a bit<commit_after>use std::str::from_utf8;
use nom::{line_ending, not_line_ending, GetInput};

named!(quoted,
       delimited!(tag!("\""), take_until!("\""), tag!("\"")));

named!(quote_and_done,
       terminated!(quoted, alt!(eof!() | line_ending)));

named!(property, take_until!(" = "));

named!(bare_value, preceded!(not!(tag!("\"")), not_line_ending));

named!(key_value<&[u8], Line>,
       do_parse!(key: property >>
                 tag!(" = ") >>
                 value: alt!(
                     quote_and_done |
                     bare_value
                 ) >>
                 (KeyValue(to_string(key), to_string(value)))));

named!(comment<&[u8], Line>,
       do_parse!(
           content: preceded!(tag!("#"), not_line_ending) >>
           (Comment(to_string(content)))));

named!(line<&[u8], Line>, alt!(
        ws!(key_value) |
        ws!(comment)));

named!(parse<&[u8], Vec<Line> >, many0!(line));

#[derive(Debug, PartialEq)]
pub enum Line {
    KeyValue(String, String),
    Comment(String),
}
use self::Line::*;

#[derive(Debug, PartialEq)]
pub enum ParseError {
    Syntax,
}
use self::ParseError::*;

impl From<::nom::IError> for ParseError {
    fn from(error: ::nom::IError) -> ParseError {
        match error {
            ::nom::IError::Incomplete(_) => Syntax,
            _ => panic!("Error evaluating parser"),
        }
    }
}

fn to_string(bytes: &[u8]) -> String {
    from_utf8(bytes)
        .expect("Managed to parse but failed to convert non-utf8")
        .to_string()
}

pub fn get_lines(s: &str) -> Result<Vec<Line>, ParseError> {
    let parsed = parse(s.as_bytes());
    match parsed.remaining_input() {
        Some(bytes) if (bytes.len() != 0) => return Err(Syntax),
        _ => {}
    }
    parsed.to_full_result().map_err(::std::convert::From::from)
}

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

    fn assert_syntax_error(contents: &str) {
        assert_eq!(get_lines(contents).unwrap_err(),
               ParseError::Syntax);
    }

    fn get_lines_vec(contents: &str) -> Vec<Line> {
        get_lines(contents).unwrap()
    }

    fn key_value(key: &str, value: &str) -> super::Line {
        super::Line::KeyValue(key.to_string(), value.to_string())
    }

    #[test]
    fn root_level_empty() {
        assert_eq!(get_lines_vec(""), vec![]);
    }

    #[test]
    fn root_level_single_property() {
        let result = vec![
            key_value("hostname", "dynamo"),
        ];
        assert_eq!(get_lines("hostname = \"dynamo\"").unwrap(), result);
    }

    #[test]
    fn root_level_three_properties() {
        let result = vec![
            key_value("hostname", "dynamo"),
            key_value("port", "5153"),
            key_value("path", "/foo/bar"),
        ];
        assert_eq!(
            get_lines("hostname = \"dynamo\"\nport = \"5153\"\npath = \"/foo/bar\"").unwrap(),
            result);
    }

    #[test]
    fn just_a_word() {
        assert_syntax_error("hostname");
    }

    #[test]
    fn word_and_equals() {
        assert_syntax_error("hostname = ");
    }

    #[test]
    #[ignore]
    fn root_level_property_with_many_dots_together() {
        assert_syntax_error("host..name = \"dynamo\"");
    }

    #[test]
    fn quoted_word_with_stuff_after() {
        assert_syntax_error("hostname = \"dynamo\"great");
        assert_syntax_error("hostname = \"dynamo\" great");
    }

    #[test]
    fn root_level_property_with_dots() {
        let result = vec![
            key_value("host.name", "dynamo"),
        ];
        assert_eq!(get_lines("host.name = \"dynamo\"").unwrap(), result);
    }

    #[test]
    fn root_level_property_with_underscores() {
        let result = vec![
            key_value("host_name", "dynamo"),
        ];
        assert_eq!(get_lines("host_name = \"dynamo\"").unwrap(), result);
    }

    #[test]
    fn bare_words() {
        let result = vec![
            key_value("hostname", "dynamo"),
        ];
        assert_eq!(get_lines("hostname = dynamo").unwrap(), result);
    }

    #[test]
    fn multiline_bare_words() {
        let result = vec![
            key_value("hostname", "dynamo"),
            key_value("port", "5153"),
            key_value("path", "/foo/bar"),
        ];
        assert_eq!(
            get_lines("hostname = dynamo\nport = 5153\npath = /foo/bar").unwrap(),
            result);
    }

    #[test]
    fn bare_words_until_end_of_line() {
        let result = vec![
            key_value("hostname", "dynamo is bae"),
        ];
        assert_eq!(get_lines("hostname = dynamo is bae").unwrap(), result);
    }

    #[test]
    fn comments() {
        let result = vec![
            Line::Comment(" Just a comment.".to_string()),
        ];
        assert_eq!(get_lines("# Just a comment.").unwrap(), result);
    }

    #[test]
    fn comment_after_bare_words() {
        let result = vec![
            key_value("hostname", "dynamo # This isn't actually a comment."),
        ];
        assert_eq!(
            get_lines("hostname = dynamo # This isn't actually a comment.").unwrap(),
            result);
    }

    #[test]
    fn comment_after_quote() {
        assert_syntax_error("hostname = \"dynamo\" # This isn't actually a comment.");
    }

    #[test]
    #[ignore]
    fn simple_multiline() {
        // let result = vec![
        //     Namespace("host".to_string(), vec![
        //         key_value("name", "dynamo"),
        //     ]),
        // ];
        // assert_eq!(get_lines("host\n    name = dynamo").unwrap(), result);
    }
}
<|endoftext|>


<commit_before>/// Parser `parser::parse` for brainfuck.

use ast::Program;
use Instruction::*;
use loop_helper::LoopHelper;

/// Parses (and lexes) the input `text` and returns an ast.
/// Whitespace in `text` ist skipped.
/// Whitespace is anything else, then the 8 legal brainfuck
/// symbols: `+`, `-`, `<`, `>`, `[`, `]`, `.` and `,`.
///
/// # Panics
///
/// Panics on parsing errors: unbalanced `[` and `]`.
pub fn parse(text: String) -> Program {
    let mut loop_helper = LoopHelper::new();
    // "lex" token stream - aka skip whitespace
    let token = text.chars()
        .filter(move |c| {
                    *c == '>' || *c == '<' || *c == '+' || *c == '-' || *c == '.' ||
                    *c == ',' || *c == '[' || *c == ']'
                });
    let mut instructions = token
        .enumerate()
        .map(|(idx, c)| match c {
                 '<' => MoveLeft(1),
                 '>' => MoveRight(1),
                 '+' => Inc(1),
                 '-' => Dec(1),
                 '.' => Output,
                 ',' => Input,
                 '[' => loop_helper.loop_entry(idx),
                 ']' => loop_helper.loop_exit(idx),
                 c => panic!("Unknown instruction {:?} at pc={:}", c, idx),
             })
        .collect::<Vec<_>>();
    loop_helper.relocate(&mut instructions);
    Program { instructions: instructions }
}
<commit_msg>fixed parser documentation<commit_after>//! Parser `parser::parse` for brainfuck.

use ast::Program;
use Instruction::*;
use loop_helper::LoopHelper;

/// Parses (and lexes) the input `text` and returns an ast.
/// Whitespace in `text` ist skipped.
/// Whitespace is anything else, then the 8 legal brainfuck
/// symbols: `+`, `-`, `<`, `>`, `[`, `]`, `.` and `,`.
///
/// # Panics
///
/// Panics on parsing errors: unbalanced `[` and `]`.
pub fn parse(text: String) -> Program {
    let mut loop_helper = LoopHelper::new();
    // "lex" token stream - aka skip whitespace
    let token = text.chars()
        .filter(move |c| {
                    *c == '>' || *c == '<' || *c == '+' || *c == '-' || *c == '.' ||
                    *c == ',' || *c == '[' || *c == ']'
                });
    let mut instructions = token
        .enumerate()
        .map(|(idx, c)| match c {
                 '<' => MoveLeft(1),
                 '>' => MoveRight(1),
                 '+' => Inc(1),
                 '-' => Dec(1),
                 '.' => Output,
                 ',' => Input,
                 '[' => loop_helper.loop_entry(idx),
                 ']' => loop_helper.loop_exit(idx),
                 c => panic!("Unknown instruction {:?} at pc={:}", c, idx),
             })
        .collect::<Vec<_>>();
    loop_helper.relocate(&mut instructions);
    Program { instructions: instructions }
}
<|endoftext|>