<commit_before>extern crate libc;

pub mod anoncreds;
pub mod ledger;
pub mod wallet;

use self::libc::{c_char};

#[repr(i32)]
pub enum ErrorCode {
    Success = 0,

    // Common errors
    // Called passed invalid session handle
    CommonInvalidSession = 100,

    // Caller passed invalid value as param 1 (null, invalid json and etc..)
    CommonInvalidParam1,

    // Caller passed invalid value as param 2 (null, invalid json and etc..)
    CommonInvalidParam2,

    // Caller passed invalid value as param 3 (null, invalid json and etc..)
    CommonInvalidParam3,

    // Caller passed invalid value as param 4 (null, invalid json and etc..)
    CommonInvalidParam4,

    // Caller passed invalid value as param 5 (null, invalid json and etc..)
    CommonInvalidParam5,

    // Invalid library state was detected in runtime. It signals library bug
    CommonInvalidState,

    // Indicates that api was called before init_library call
    CommonUninitialized,

    // Wallet errors
    // Unknown type of wallet was passed on open_session
    WalletUnknownType = 200,

    // Attempt to register already existing wallet type
    WalletTypeAlreadyRegistered,

    // Requested entity id isn't present in wallet
    WalletNotFound,

    // Wallet files referenced in open_session have invalid data format
    WalletInvalidDataFormat,

    // IO error during access wallet backend
    WalletIOError,

    // Ledger errors
    // Pool ledger files referenced in open_session have invalid data format
    LedgerPoolInvalidDataFormat = 300,

    // IO error during access pool ledger files
    LedgerPoolIOError,

    // No concensus during ledger operation
    LedgerNoConsensus,

    // Attempt to send unknown or incomplete transaction message
    LedgerInvalidTransaction,

    // Attempt to send transaction without the necessary privileges
    LedgerSecurityError,

    // IO error during sending of ledger transactions or catchup process
    LedgerIOError,

    // Crypto errors
    // Invalid structure of any crypto promitives (keys, signatures, seeds and etc...)
    CryptoInvalidStructure = 400,

    // Unknown crypto type was requested for signing/verifiyng or encoding/decoding
    CryptoUnknownType,

    // Revocation registry is full and creation of new registry is necessary
    CryptoRevocationRegistryFull
}

/// Initializes the library by providing global configuration.
///
/// Perform the following initialization actions:
/// - Refreshing of local pool ledger copy (catch up)
/// - Establishing of connection with Nodes
///
///
/// #Params
/// ledger_config: (optional) Ledger configuration json. Example:
/// {
///     "genesis_txn": string, (optional; a path to genesis transaction file. If NULL, then a default one will be used.)
///     // TODO: Provide description of additional params like timeouts
/// }
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn init_library(command_handle: i32
                           ledger_config: *const c_char,
                           cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

/// Creates a new session. A session is associated with a wallet.
/// The call is synchronous.
///
/// Note that there can be only one session for each wallet if wallet implementation doesn't provide
/// concurrent access.
///
/// #Params
/// wallet_config: Wallet configuration json. Example:
/// {
///     "type": string, (optional; if not provided then the default wallet type will be used.
///             Custom wallet type can be created with register_wallet_type call)
///     "freshnessTime": int, (optional; if not provided then 60*24 value will be used.
///             Amount of minutes to consider wallet value as fresh.
///     "config": json, (optional; if not provided default configuration will be used. Configuration
///             is specific for concrete wallet type)
/// }
///
/// #Returns
/// session handle
///
/// #Errors
/// CommonInvalidParam1
/// CommonInvalidParam2
/// WalletUnknownType
/// WalletInvalidDataFormat
/// WalletIOError
/// LedgerPoolInvalidDataFormat
/// LedgerPoolIOError
/// LedgerIOError
///
#[no_mangle]
pub extern fn open_session(wallet_name: *const c_char,
                           wallet_config: *const c_char, session_handler: *const * mut i32) -> ErrorCode {
    unimplemented!();
}

/// Closes a session and frees allocated resources. The call is synchronous.
///
/// #Params
/// session_handle: session handler (created by open_session).
///
/// #Returns
/// error code
///
/// #Errors
#[no_mangle]
pub extern fn close_session(session_handle: i32) -> ErrorCode {
    unimplemented!();
}

/// Registers custom wallet implementation.
/// It allows library user to provide custom wallet implementation that can be referenced in
/// open_session call.
///
/// #Params
/// type_name: Wallet type name.
/// init: init operation handler
/// set: set operation handler
/// get: get operation handler
/// free: free operation handler
///
/// #Returns
/// error code
///
/// #Errors
/// CommonInvalidParam1
/// CommonInvalidParam2
/// CommonInvalidParam3
/// CommonInvalidParam4
/// CommonInvalidParam5
/// WalletTypeAlreadyRegistered
#[no_mangle]
pub extern fn register_wallet_type(type_name: *const c_char,
                                   init: extern fn(config: *const c_char, wallet_handle: *const *mut i32) -> ErrorCode,
                                   set: extern fn(wallet_handle: i32, key: *const c_char, sub_key: *const c_char, value: *const c_char) -> ErrorCode,
                                   get: extern fn(wallet_handle: i32, key: *const c_char, sub_key: *const c_char, value_ptr: *const *mut c_char) -> ErrorCode,
                                   free: extern fn(wallet_handle: i32, str: *const c_char) -> ErrorCode) -> ErrorCode {
    unimplemented!();
}

/// Refreshes a local copy of a pool ledger and updates pool nodes connections.
///
/// #Params
/// None. Ledger configuration must be provided with init_library call.
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn refresh_pool_ledger(command_handle: i32,
                                  cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

/// Creates a new secure wallet with the given unique name.
///
/// #Params
/// wallet_name
/// wallet_config
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn create_wallet(command_handle: i32,
                            wallet_name: *const c_char,
                            wallet_config: *const c_char,
                            cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

/// Removes a secure wallet with the given unique name.
///
/// #Params
/// wallet_name
/// wallet_config
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn delete_wallet(command_handle: i32,
                            wallet_name: *const c_char,
                            cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>small changes<commit_after>extern crate libc;

pub mod anoncreds;
pub mod ledger;
pub mod wallet;

use self::libc::{c_char};

#[repr(i32)]
pub enum ErrorCode {
    Success = 0,

    // Common errors
    // Called passed invalid session handle
    CommonInvalidSession = 100,

    // Caller passed invalid value as param 1 (null, invalid json and etc..)
    CommonInvalidParam1,

    // Caller passed invalid value as param 2 (null, invalid json and etc..)
    CommonInvalidParam2,

    // Caller passed invalid value as param 3 (null, invalid json and etc..)
    CommonInvalidParam3,

    // Caller passed invalid value as param 4 (null, invalid json and etc..)
    CommonInvalidParam4,

    // Caller passed invalid value as param 5 (null, invalid json and etc..)
    CommonInvalidParam5,

    // Invalid library state was detected in runtime. It signals library bug
    CommonInvalidState,

    // Indicates that api was called before init_library call
    CommonUninitialized,

    // Wallet errors
    // Unknown type of wallet was passed on open_session
    WalletUnknownType = 200,

    // Attempt to register already existing wallet type
    WalletTypeAlreadyRegistered,

    // Requested entity id isn't present in wallet
    WalletNotFound,

    // Wallet files referenced in open_session have invalid data format
    WalletInvalidDataFormat,

    // IO error during access wallet backend
    WalletIOError,

    // Ledger errors
    // Pool ledger files referenced in open_session have invalid data format
    LedgerPoolInvalidDataFormat = 300,

    // IO error during access pool ledger files
    LedgerPoolIOError,

    // No concensus during ledger operation
    LedgerNoConsensus,

    // Attempt to send unknown or incomplete transaction message
    LedgerInvalidTransaction,

    // Attempt to send transaction without the necessary privileges
    LedgerSecurityError,

    // IO error during sending of ledger transactions or catchup process
    LedgerIOError,

    // Crypto errors
    // Invalid structure of any crypto promitives (keys, signatures, seeds and etc...)
    CryptoInvalidStructure = 400,

    // Unknown crypto type was requested for signing/verifiyng or encoding/decoding
    CryptoUnknownType,

    // Revocation registry is full and creation of new registry is necessary
    CryptoRevocationRegistryFull
}

/// Initializes the library by providing global configuration.
///
/// Perform the following initialization actions:
/// - Refreshing of local pool ledger copy (catch up)
/// - Establishing of connection with Nodes
///
///
/// #Params
/// ledger_config: (optional) Ledger configuration json. Example:
/// {
///     "genesis_txn": string, (optional; a path to genesis transaction file. If NULL, then a default one will be used.)
///     // TODO: Provide description of additional params like timeouts
/// }
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn init_library(command_handle: i32,
                           ledger_config: *const c_char,
                           cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

/// Creates a new session. A session is associated with a wallet.
/// The call is synchronous.
///
/// Note that there can be only one session for each wallet if wallet implementation doesn't provide
/// concurrent access.
///
/// #Params
/// wallet_config: Wallet configuration json. Example:
/// {
///     "type": string, (optional; if not provided then the default wallet type will be used.
///             Custom wallet type can be created with register_wallet_type call)
///     "freshnessTime": int, (optional; if not provided then 60*24 value will be used.
///             Amount of minutes to consider wallet value as fresh.
///     "config": json, (optional; if not provided default configuration will be used. Configuration
///             is specific for concrete wallet type)
/// }
///
/// #Returns
/// session handle
///
/// #Errors
/// CommonInvalidParam1
/// CommonInvalidParam2
/// WalletUnknownType
/// WalletInvalidDataFormat
/// WalletIOError
/// LedgerPoolInvalidDataFormat
/// LedgerPoolIOError
/// LedgerIOError
///
#[no_mangle]
pub extern fn open_session(wallet_name: *const c_char,
                           wallet_config: *const c_char, session_handler: *const * mut i32) -> ErrorCode {
    unimplemented!();
}

/// Closes a session and frees allocated resources. The call is synchronous.
///
/// #Params
/// session_handle: session handler (created by open_session).
///
/// #Returns
/// error code
///
/// #Errors
#[no_mangle]
pub extern fn close_session(session_handle: i32) -> ErrorCode {
    unimplemented!();
}

/// Registers custom wallet implementation.
/// It allows library user to provide custom wallet implementation that can be referenced in
/// open_session call.
///
/// #Params
/// type_name: Wallet type name.
/// init: init operation handler
/// set: set operation handler
/// get: get operation handler
/// free: free operation handler
///
/// #Returns
/// error code
///
/// #Errors
/// CommonInvalidParam1
/// CommonInvalidParam2
/// CommonInvalidParam3
/// CommonInvalidParam4
/// CommonInvalidParam5
/// WalletTypeAlreadyRegistered
#[no_mangle]
pub extern fn register_wallet_type(type_name: *const c_char,
                                   init: extern fn(config: *const c_char, wallet_handle: *const *mut i32) -> ErrorCode,
                                   set: extern fn(wallet_handle: i32, key: *const c_char, sub_key: *const c_char, value: *const c_char) -> ErrorCode,
                                   get: extern fn(wallet_handle: i32, key: *const c_char, sub_key: *const c_char, value_ptr: *const *mut c_char) -> ErrorCode,
                                   free: extern fn(wallet_handle: i32, str: *const c_char) -> ErrorCode) -> ErrorCode {
    unimplemented!();
}

/// Refreshes a local copy of a pool ledger and updates pool nodes connections.
///
/// #Params
/// None. Ledger configuration must be provided with init_library call.
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn refresh_pool_ledger(command_handle: i32,
                                  cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

/// Creates a new secure wallet with the given unique name.
///
/// #Params
/// wallet_name
/// wallet_config
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn create_wallet(command_handle: i32,
                            wallet_name: *const c_char,
                            wallet_config: *const c_char,
                            cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

/// Removes a secure wallet with the given unique name.
///
/// #Params
/// wallet_name
/// wallet_config
///
/// #Returns
/// Error code
///
/// #Errors
/// Common*
/// Ledger*
#[no_mangle]
pub extern fn delete_wallet(command_handle: i32,
                            wallet_name: *const c_char,
                            cb: extern fn(xcommand_handle: i32, err: ErrorCode)) -> ErrorCode {
    unimplemented!();
}

#[cfg(test)]
mod tests {
    use super::*;
}
<|endoftext|>


<commit_before>use nom::{multispace, eol};

named!(pub comment <()>, chain!(
  tag!("--") ~
  take_till!(is_eol) ~
  eol,
  || ()
));

named!(pub skip_other <()>, do_parse!(
  opt!(multispace) >>
  opt!(comment) >>
  opt!(multispace) >>
  opt!(chain!(
     complete!(peek!(tag!("--"))) ~
     complete!(skip_other),
     || ()
  )) >>
  ()
));

pub fn is_eol(byte: u8) -> bool {
  let chr = byte as char;
  chr == '\n' || chr == '\r'
}

#[test]
fn test_comment() {
  assert_eq!(
    "".as_bytes(),
    comment("--\n".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    comment("--foo bar asdf\n".as_bytes()).unwrap().0
  );
  assert_eq!(
    "  ".as_bytes(),
    comment("--\n  ".as_bytes()).unwrap().0
  );
  assert_eq!(
    "foobar".as_bytes(),
    comment("--asdf\nfoobar".as_bytes()).unwrap().0
  );
  assert!(comment("foo --\n".as_bytes()).is_err());
  assert!(comment(" bar --\n".as_bytes()).is_err());
  assert!(comment(" -asdf-\n".as_bytes()).is_err());
  assert!(comment("-".as_bytes()).is_incomplete());
  assert!(comment("--".as_bytes()).is_incomplete());
}

#[test]
fn test_skip_other() {
  assert_eq!(
    "".as_bytes(),
    skip_other("  --\n".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    skip_other("--\n  ".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    skip_other("--\n \n ".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    skip_other("--\n -- adf\n ".as_bytes()).unwrap().0
  );
  assert!(skip_other(" -".as_bytes()).is_incomplete());
  assert!(skip_other(" --".as_bytes()).is_incomplete());
  assert!(skip_other(" --\n -".as_bytes()).is_done());
}
<commit_msg>Fix skip_other test<commit_after>use nom::{multispace, eol};

named!(pub comment <()>, chain!(
  tag!("--") ~
  take_till!(is_eol) ~
  eol,
  || ()
));

named!(pub skip_other <()>, do_parse!(
  opt!(multispace) >>
  opt!(comment) >>
  opt!(multispace) >>
  opt!(chain!(
     complete!(peek!(tag!("--"))) ~
     complete!(skip_other),
     || ()
  )) >>
  ()
));

pub fn is_eol(byte: u8) -> bool {
  let chr = byte as char;
  chr == '\n' || chr == '\r'
}

#[test]
fn test_comment() {
  assert_eq!(
    "".as_bytes(),
    comment("--\n".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    comment("--foo bar asdf\n".as_bytes()).unwrap().0
  );
  assert_eq!(
    "  ".as_bytes(),
    comment("--\n  ".as_bytes()).unwrap().0
  );
  assert_eq!(
    "foobar".as_bytes(),
    comment("--asdf\nfoobar".as_bytes()).unwrap().0
  );
  assert!(comment("foo --\n".as_bytes()).is_err());
  assert!(comment(" bar --\n".as_bytes()).is_err());
  assert!(comment(" -asdf-\n".as_bytes()).is_err());
  assert!(comment("-".as_bytes()).is_incomplete());
  assert!(comment("--".as_bytes()).is_incomplete());
}

#[test]
fn test_skip_other() {
  assert_eq!(
    "".as_bytes(),
    skip_other("  --\n\n".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    skip_other("--\n  ".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    skip_other("--\n \n ".as_bytes()).unwrap().0
  );
  assert_eq!(
    "".as_bytes(),
    skip_other("--\n -- adf\n ".as_bytes()).unwrap().0
  );
  assert!(skip_other(" -".as_bytes()).is_incomplete());
  assert!(skip_other(" --".as_bytes()).is_incomplete());
  assert!(skip_other(" --\n -".as_bytes()).is_done());
}
<|endoftext|>


<commit_before>use ansi_term::Colour::Yellow;
use std::io::{stdin, stdout, Write};
use clap::{Arg, App, SubCommand, ArgMatches};

use app::machine::Machine;

pub mod logger;
pub mod machine;
pub mod vagrant;

pub fn init_cli<'a>() -> ArgMatches<'a> {
    App::new("vagment")
        .author(crate_authors!())
        .version(crate_version!())
        .arg(Arg::with_name("VAGRANT_COMMAND"))
        .arg(Arg::with_name("MACHINE_NUMBER"))
        .subcommand(SubCommand::with_name("dump")
            .arg(Arg::with_name("MACHINE_NUMBER"))
            .about("Dump Vagrant file")
        )
        .subcommand(SubCommand::with_name("list")
            .about("List available machines")
        )
        .subcommand(SubCommand::with_name("refresh")
            .about("Clear vagrant cache")
        )
        .get_matches()
}

pub fn dump_config(machine: &Machine) -> Result<String, String> {
    vagrant::dump(machine.get_path())
}

pub fn validate_number(machines: &Vec<Machine>, num: u16) -> Result<u16, String> {

    let mut number: u16 = num;

    if number == 0 {
        if machines.len() > 1 {
            let input = ask_machine_number(&machines);
            number = input.parse().unwrap_or(0);
        } else {
            number = 1;
        }
    }

    if number == 0 {
        return Err("Please enter a valid number".to_string());
    }

    Ok(number)
}

pub fn retrieve_machine_by_number(machines: &Vec<Machine>, number: u16) -> Result<&Machine, String> {

    if number == 0 {
        return Err("Please enter a valid number".to_string());
    }

    let index = (number - 1) as usize;

    if machines.get(index).is_none() {
        return Err("Please enter a valid number".to_string());
    }

    Ok(machines.get(index).unwrap())
}

pub fn process_command(machine: &Machine, command: &str) -> Result<String, String> {

    let commands = list_commands!();

    if !commands.contains(&command) {
        return Err(
            format!("`{}` is not a valid command! Available commands are {}", command, commands.join(", "))
        );
    }

    logger::info(
        format!("Executing command vagrant {} in {}", Yellow.paint(command), Yellow.paint(machine.get_path()))
    );

    vagrant::execute(command, machine.get_path())
}

pub fn print_list(machines: &Vec<Machine>) {

    let output = format!("{0: ^10} | {1: ^10} | {2: ^10} | {3: ^10} | {4: ^10}", "Id", "Name", "Provider", "State", "Path");

    print!("\n");
    println!("{}", Yellow.paint(output));
    for machine in machines {
        machine.to_output();
    }
    print!("\n");
}

fn ask_machine_number(machines: &Vec<Machine>) -> String {
    print_list(machines);
    print!("{}", Yellow.paint("Please enter a machine number\n-> "));

    let _ = stdout().flush();

    let mut input = String::new();
    match stdin().read_line(&mut input) {
        Ok(bytes) => bytes,
        Err(error) => panic!("Could nout read input: {}", error)
    };
    input.trim().to_string()
}
<commit_msg>Fix typo<commit_after>use ansi_term::Colour::Yellow;
use std::io::{stdin, stdout, Write};
use clap::{Arg, App, SubCommand, ArgMatches};

use app::machine::Machine;

pub mod logger;
pub mod machine;
pub mod vagrant;

pub fn init_cli<'a>() -> ArgMatches<'a> {
    App::new("vagment")
        .author(crate_authors!())
        .version(crate_version!())
        .arg(Arg::with_name("VAGRANT_COMMAND"))
        .arg(Arg::with_name("MACHINE_NUMBER"))
        .subcommand(SubCommand::with_name("dump")
            .arg(Arg::with_name("MACHINE_NUMBER"))
            .about("Dump Vagrant file")
        )
        .subcommand(SubCommand::with_name("list")
            .about("List available machines")
        )
        .subcommand(SubCommand::with_name("refresh")
            .about("Clear vagrant cache")
        )
        .get_matches()
}

pub fn dump_config(machine: &Machine) -> Result<String, String> {
    vagrant::dump(machine.get_path())
}

pub fn validate_number(machines: &Vec<Machine>, num: u16) -> Result<u16, String> {

    let mut number: u16 = num;

    if number == 0 {
        if machines.len() > 1 {
            let input = ask_machine_number(&machines);
            number = input.parse().unwrap_or(0);
        } else {
            number = 1;
        }
    }

    if number == 0 {
        return Err("Please enter a valid number".to_string());
    }

    Ok(number)
}

pub fn retrieve_machine_by_number(machines: &Vec<Machine>, number: u16) -> Result<&Machine, String> {

    if number == 0 {
        return Err("Please enter a valid number".to_string());
    }

    let index = (number - 1) as usize;

    if machines.get(index).is_none() {
        return Err("Please enter a valid number".to_string());
    }

    Ok(machines.get(index).unwrap())
}

pub fn process_command(machine: &Machine, command: &str) -> Result<String, String> {

    let commands = list_commands!();

    if !commands.contains(&command) {
        return Err(
            format!("`{}` is not a valid command! Available commands are {}", command, commands.join(", "))
        );
    }

    logger::info(
        format!("Executing command vagrant {} in {}", Yellow.paint(command), Yellow.paint(machine.get_path()))
    );

    vagrant::execute(command, machine.get_path())
}

pub fn print_list(machines: &Vec<Machine>) {

    let output = format!("{0: ^10} | {1: ^10} | {2: ^10} | {3: ^10} | {4: ^10}", "Id", "Name", "Provider", "State", "Path");

    print!("\n");
    println!("{}", Yellow.paint(output));
    for machine in machines {
        machine.to_output();
    }
    print!("\n");
}

fn ask_machine_number(machines: &Vec<Machine>) -> String {
    print_list(machines);
    print!("{}", Yellow.paint("Please enter a machine number\n-> "));

    let _ = stdout().flush();

    let mut input = String::new();
    match stdin().read_line(&mut input) {
        Ok(bytes) => bytes,
        Err(error) => panic!("Could not read input: {}", error)
    };
    input.trim().to_string()
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::str::FromStr;

use bodyparser;
use bldr_core::build_config::{BLDR_CFG, BuildCfg};
use github_api_client::GitHubClient;
use hab_core::package::Plan;
use http_gateway::http::controller::*;
use iron::status;
use persistent;
use protocol::scheduler::{Group, GroupCreate};
use router::Router;

use error::Error;
use headers::*;
use types::*;

pub enum GitHubEvent {
    Push,
    Ping,
}

impl FromStr for GitHubEvent {
    type Err = Error;

    fn from_str(event: &str) -> Result<Self, Self::Err> {
        match event {
            "ping" => Ok(GitHubEvent::Ping),
            "push" => Ok(GitHubEvent::Push),
            _ => Err(Error::UnknownGitHubEvent(event.to_string())),
        }
    }
}

enum HandleResult<T> {
    Ok(T),
    Err(Response),
}

pub fn handle_event(req: &mut Request) -> IronResult<Response> {
    let event = match req.headers.get::<XGitHubEvent>() {
        Some(&XGitHubEvent(ref event)) => {
            match GitHubEvent::from_str(event) {
                Ok(event) => event,
                Err(err) => return Ok(Response::with((status::BadRequest, err.to_string()))),
            }
        }
        _ => return Ok(Response::with(status::BadRequest)),
    };
    // JW TODO: Authenticate hook
    match event {
        GitHubEvent::Ping => Ok(Response::with(status::Ok)),
        GitHubEvent::Push => handle_push(req),
    }
}

pub fn repo_file_content(req: &mut Request) -> IronResult<Response> {
    let github = req.get::<persistent::Read<GitHubCli>>().unwrap();
    let session = req.extensions.get::<Authenticated>().unwrap();
    let params = req.extensions.get::<Router>().unwrap();
    let path = match params.find("path") {
        Some(path) => path,
        None => return Ok(Response::with(status::BadRequest)),
    };
    let install_id = match params.find("install_id") {
        Some(install_id) => {
            match install_id.parse::<u32>() {
                Ok(install_id) => install_id,
                Err(_) => return Ok(Response::with(status::BadRequest)),
            }
        }
        None => return Ok(Response::with(status::BadRequest)),
    };
    let token = {
        match github.app_installation_token(install_id) {
            Ok(token) => token,
            Err(err) => {
                return Ok(Response::with((status::BadGateway, err.to_string())));
            }
        }
    };
    let repo = {
        let repo = match params.find("repo") {
            Some(repo) => repo,
            None => return Ok(Response::with(status::BadRequest)),
        };
        let repos = match github.repositories(session.get_token(), install_id) {
            Ok(repos) => repos,
            Err(err) => return Ok(Response::with((status::BadGateway, err.to_string()))),
        };
        match repos.into_iter().find(|r| r.name == repo) {
            Some(repo) => repo,
            None => return Ok(Response::with(status::NotFound)),
        }
    };
    match github.contents(&token, repo.id, path) {
        Ok(search) => Ok(render_json(status::Ok, &search)),
        Err(err) => Ok(Response::with((status::BadGateway, err.to_string()))),
    }
}

pub fn search_code(req: &mut Request) -> IronResult<Response> {
    let github = req.get::<persistent::Read<GitHubCli>>().unwrap();
    let params = req.extensions.get::<Router>().unwrap();
    let query = match req.url.query() {
        Some(query) => query,
        None => return Ok(Response::with(status::BadRequest)),
    };
    let install_id = match params.find("install_id") {
        Some(install_id) => {
            match install_id.parse::<u32>() {
                Ok(install_id) => install_id,
                Err(_) => return Ok(Response::with(status::BadRequest)),
            }
        }
        None => return Ok(Response::with(status::BadRequest)),
    };
    let token = {
        match github.app_installation_token(install_id) {
            Ok(token) => token,
            Err(err) => {
                return Ok(Response::with((status::BadGateway, err.to_string())));
            }
        }
    };
    match github.search_code(&token, query) {
        Ok(search) => Ok(render_json(status::Ok, &search)),
        Err(err) => Ok(Response::with((status::BadGateway, err.to_string()))),
    }
}

fn handle_push(req: &mut Request) -> IronResult<Response> {
    let hook = match req.get::<bodyparser::Struct<GitHubWebhookPush>>() {
        Ok(Some(hook)) => hook,
        Ok(None) => return Ok(Response::with(status::UnprocessableEntity)),
        Err(err) => {
            return Ok(Response::with(
                (status::UnprocessableEntity, err.to_string()),
            ));
        }
    };
    if hook.commits.is_empty() {
        return Ok(Response::with((status::Ok)));
    }
    let github = req.get::<persistent::Read<GitHubCli>>().unwrap();
    let token = match github.app_installation_token(hook.installation.id) {
        Ok(token) => token,
        Err(err) => {
            return Ok(Response::with((status::BadGateway, err.to_string())));
        }
    };
    let mut query = format!("q={}+in:path+repo:{}", "plan.sh", hook.repository.full_name);
    let plans = match github.search_code(&token, &query) {
        Ok(search) => search.items,
        Err(err) => return Ok(Response::with((status::BadGateway, err.to_string()))),
    };
    if plans.is_empty() {
        return Ok(Response::with(status::Ok));
    }
    query = format!("q={}+in:path+repo:{}", BLDR_CFG, hook.repository.full_name);
    let config = match github.search_code(&token, &query) {
        Ok(search) => {
            match search
                .items
                .into_iter()
                .filter(|i| i.path == BLDR_CFG)
                .collect::<Vec<SearchItem>>()
                .pop() {
                Some(item) => {
                    match read_bldr_config(&*github, &token, &hook, &item.path) {
                        HandleResult::Ok(cfg) => Some(cfg),
                        HandleResult::Err(response) => return Ok(response),
                    }
                }
                None => None,
            }
        }
        Err(err) => return Ok(Response::with((status::BadGateway, err.to_string()))),
    };
    debug!("Config, {:?}", config);
    let mut plans = match read_plans(&github, &token, &hook, plans) {
        HandleResult::Ok(plans) => plans,
        HandleResult::Err(err) => return Ok(err),
    };
    debug!("Plans, {:?}", plans);
    if let Some(cfg) = config {
        plans.retain(|plan| match cfg.get(&plan.name) {
            Some(project) => hook.changed().iter().any(|f| project.triggered_by(f)),
            None => false,
        })
    }
    build_plans(req, plans)
}

fn build_plans(req: &mut Request, plans: Vec<Plan>) -> IronResult<Response> {
    // JW TODO: Validate that this repository is where these plans belong. You could theoretically
    // create a plan in a different repo and force a build of another piece of software without
    // this check.
    let mut request = GroupCreate::new();
    for plan in plans.into_iter() {
        debug!("Scheduling, {:?}", plan);
        request.set_origin(plan.origin);
        request.set_package(plan.name);
        // JW TODO: We need to be able to determine which platform this build is for based on
        // the directory structure the plan is found in or metadata inside the plan. We will need
        // to have this done before we support building additional targets with Builder.
        request.set_target("x86_64-linux".to_string());
        match route_message::<GroupCreate, Group>(req, &request) {
            Ok(group) => debug!("Group created, {:?}", group),
            Err(err) => debug!("Failed to create group, {:?}", err),
        }
    }
    Ok(Response::with(status::Ok))
}

fn read_bldr_config(
    github: &GitHubClient,
    token: &str,
    hook: &GitHubWebhookPush,
    path: &str,
) -> HandleResult<BuildCfg> {
    match github.contents(token, hook.repository.id, path) {
        Ok(contents) => {
            match contents.decode() {
                Ok(ref bytes) => {
                    match BuildCfg::from_slice(bytes) {
                        Ok(cfg) => HandleResult::Ok(cfg),
                        Err(err) => HandleResult::Err(Response::with(
                            (status::UnprocessableEntity, err.to_string()),
                        )),
                    }
                }
                Err(err) => {
                    HandleResult::Err(Response::with(
                        (status::UnprocessableEntity, err.to_string()),
                    ))
                }
            }
        }
        Err(err) => HandleResult::Err(Response::with((status::BadGateway, err.to_string()))),
    }
}

fn read_plans(
    github: &GitHubClient,
    token: &str,
    hook: &GitHubWebhookPush,
    plans: Vec<SearchItem>,
) -> HandleResult<Vec<Plan>> {
    let mut parsed = Vec::with_capacity(plans.len());
    for plan in plans {
        match github.contents(token, hook.repository.id, &plan.path) {
            Ok(contents) => {
                match contents.decode() {
                    Ok(bytes) => {
                        match Plan::from_bytes(bytes.as_slice()) {
                            Ok(plan) => parsed.push(plan),
                            Err(err) => debug!("{}, {}", plan.path, err),
                        }
                    }
                    Err(err) => {
                        return HandleResult::Err(Response::with((
                            status::UnprocessableEntity,
                            format!("{}, {}", plan.path, err),
                        )))
                    }
                }
            }
            Err(err) => {
                return HandleResult::Err(Response::with(
                    (status::BadGateway, format!("{}, {}", plan.path, err)),
                ))
            }
        }
    }
    HandleResult::Ok(parsed)
}
<commit_msg>Validate that the repository URL from GH matches what's in our project<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::str::FromStr;

use bodyparser;
use bldr_core::build_config::{BLDR_CFG, BuildCfg};
use github_api_client::GitHubClient;
use hab_core::package::Plan;
use http_gateway::http::controller::*;
use iron::status;
use persistent;
use protocol::originsrv::{OriginProject, OriginProjectGet};
use protocol::scheduler::{Group, GroupCreate};
use router::Router;

use error::Error;
use headers::*;
use types::*;

pub enum GitHubEvent {
    Push,
    Ping,
}

impl FromStr for GitHubEvent {
    type Err = Error;

    fn from_str(event: &str) -> Result<Self, Self::Err> {
        match event {
            "ping" => Ok(GitHubEvent::Ping),
            "push" => Ok(GitHubEvent::Push),
            _ => Err(Error::UnknownGitHubEvent(event.to_string())),
        }
    }
}

enum HandleResult<T> {
    Ok(T),
    Err(Response),
}

pub fn handle_event(req: &mut Request) -> IronResult<Response> {
    let event = match req.headers.get::<XGitHubEvent>() {
        Some(&XGitHubEvent(ref event)) => {
            match GitHubEvent::from_str(event) {
                Ok(event) => event,
                Err(err) => return Ok(Response::with((status::BadRequest, err.to_string()))),
            }
        }
        _ => return Ok(Response::with(status::BadRequest)),
    };
    // JW TODO: Authenticate hook
    match event {
        GitHubEvent::Ping => Ok(Response::with(status::Ok)),
        GitHubEvent::Push => handle_push(req),
    }
}

pub fn repo_file_content(req: &mut Request) -> IronResult<Response> {
    let github = req.get::<persistent::Read<GitHubCli>>().unwrap();
    let session = req.extensions.get::<Authenticated>().unwrap();
    let params = req.extensions.get::<Router>().unwrap();
    let path = match params.find("path") {
        Some(path) => path,
        None => return Ok(Response::with(status::BadRequest)),
    };
    let install_id = match params.find("install_id") {
        Some(install_id) => {
            match install_id.parse::<u32>() {
                Ok(install_id) => install_id,
                Err(_) => return Ok(Response::with(status::BadRequest)),
            }
        }
        None => return Ok(Response::with(status::BadRequest)),
    };
    let token = {
        match github.app_installation_token(install_id) {
            Ok(token) => token,
            Err(err) => {
                return Ok(Response::with((status::BadGateway, err.to_string())));
            }
        }
    };
    let repo = {
        let repo = match params.find("repo") {
            Some(repo) => repo,
            None => return Ok(Response::with(status::BadRequest)),
        };
        let repos = match github.repositories(session.get_token(), install_id) {
            Ok(repos) => repos,
            Err(err) => return Ok(Response::with((status::BadGateway, err.to_string()))),
        };
        match repos.into_iter().find(|r| r.name == repo) {
            Some(repo) => repo,
            None => return Ok(Response::with(status::NotFound)),
        }
    };
    match github.contents(&token, repo.id, path) {
        Ok(search) => Ok(render_json(status::Ok, &search)),
        Err(err) => Ok(Response::with((status::BadGateway, err.to_string()))),
    }
}

pub fn search_code(req: &mut Request) -> IronResult<Response> {
    let github = req.get::<persistent::Read<GitHubCli>>().unwrap();
    let params = req.extensions.get::<Router>().unwrap();
    let query = match req.url.query() {
        Some(query) => query,
        None => return Ok(Response::with(status::BadRequest)),
    };
    let install_id = match params.find("install_id") {
        Some(install_id) => {
            match install_id.parse::<u32>() {
                Ok(install_id) => install_id,
                Err(_) => return Ok(Response::with(status::BadRequest)),
            }
        }
        None => return Ok(Response::with(status::BadRequest)),
    };
    let token = {
        match github.app_installation_token(install_id) {
            Ok(token) => token,
            Err(err) => {
                return Ok(Response::with((status::BadGateway, err.to_string())));
            }
        }
    };
    match github.search_code(&token, query) {
        Ok(search) => Ok(render_json(status::Ok, &search)),
        Err(err) => Ok(Response::with((status::BadGateway, err.to_string()))),
    }
}

fn handle_push(req: &mut Request) -> IronResult<Response> {
    let hook = match req.get::<bodyparser::Struct<GitHubWebhookPush>>() {
        Ok(Some(hook)) => hook,
        Ok(None) => return Ok(Response::with(status::UnprocessableEntity)),
        Err(err) => {
            return Ok(Response::with(
                (status::UnprocessableEntity, err.to_string()),
            ));
        }
    };

    if hook.commits.is_empty() {
        return Ok(Response::with(status::Ok));
    }

    let github = req.get::<persistent::Read<GitHubCli>>().unwrap();
    let token = match github.app_installation_token(hook.installation.id) {
        Ok(token) => token,
        Err(err) => {
            return Ok(Response::with((status::BadGateway, err.to_string())));
        }
    };

    let mut query = format!("q={}+in:path+repo:{}", "plan.sh", hook.repository.full_name);
    let plans = match github.search_code(&token, &query) {
        Ok(search) => search.items,
        Err(err) => return Ok(Response::with((status::BadGateway, err.to_string()))),
    };

    if plans.is_empty() {
        return Ok(Response::with(status::Ok));
    }

    query = format!("q={}+in:path+repo:{}", BLDR_CFG, hook.repository.full_name);

    let config = match github.search_code(&token, &query) {
        Ok(search) => {
            match search
                .items
                .into_iter()
                .filter(|i| i.path == BLDR_CFG)
                .collect::<Vec<SearchItem>>()
                .pop() {
                Some(item) => {
                    match read_bldr_config(&*github, &token, &hook, &item.path) {
                        HandleResult::Ok(cfg) => Some(cfg),
                        HandleResult::Err(response) => return Ok(response),
                    }
                }
                None => None,
            }
        }
        Err(err) => return Ok(Response::with((status::BadGateway, err.to_string()))),
    };

    debug!("Config, {:?}", config);

    let mut plans = match read_plans(&github, &token, &hook, plans) {
        HandleResult::Ok(plans) => plans,
        HandleResult::Err(err) => return Ok(err),
    };

    debug!("Plans, {:?}", plans);

    if let Some(cfg) = config {
        plans.retain(|plan| match cfg.get(&plan.name) {
            Some(project) => hook.changed().iter().any(|f| project.triggered_by(f)),
            None => false,
        })
    }

    build_plans(req, &hook.repository.clone_url, plans)
}

fn build_plans(req: &mut Request, repo_url: &str, plans: Vec<Plan>) -> IronResult<Response> {
    let mut request = GroupCreate::new();

    for plan in plans.into_iter() {
        let mut project_get = OriginProjectGet::new();
        project_get.set_name(format!("{}/{}", &plan.origin, &plan.name));

        match route_message::<OriginProjectGet, OriginProject>(req, &project_get) {
            Ok(project) => {
                if repo_url != project.get_vcs_data() {
                    error!(
                        "Incoming repository URL ({}) doesn't match what's in the project vcs data ({}). Aborting.",
                        repo_url,
                        project.get_vcs_data()
                    );
                    continue;
                }
            }
            Err(err) => {
                error!("Failed to fetch project. err = {:?}", &err);
                continue;
            }
        }

        debug!("Scheduling, {:?}", plan);
        request.set_origin(plan.origin);
        request.set_package(plan.name);
        // JW TODO: We need to be able to determine which platform this build is for based on
        // the directory structure the plan is found in or metadata inside the plan. We will need
        // to have this done before we support building additional targets with Builder.
        request.set_target("x86_64-linux".to_string());
        match route_message::<GroupCreate, Group>(req, &request) {
            Ok(group) => debug!("Group created, {:?}", group),
            Err(err) => debug!("Failed to create group, {:?}", err),
        }
    }

    Ok(Response::with(status::Ok))
}

fn read_bldr_config(
    github: &GitHubClient,
    token: &str,
    hook: &GitHubWebhookPush,
    path: &str,
) -> HandleResult<BuildCfg> {
    match github.contents(token, hook.repository.id, path) {
        Ok(contents) => {
            match contents.decode() {
                Ok(ref bytes) => {
                    match BuildCfg::from_slice(bytes) {
                        Ok(cfg) => HandleResult::Ok(cfg),
                        Err(err) => HandleResult::Err(Response::with(
                            (status::UnprocessableEntity, err.to_string()),
                        )),
                    }
                }
                Err(err) => {
                    HandleResult::Err(Response::with(
                        (status::UnprocessableEntity, err.to_string()),
                    ))
                }
            }
        }
        Err(err) => HandleResult::Err(Response::with((status::BadGateway, err.to_string()))),
    }
}

fn read_plans(
    github: &GitHubClient,
    token: &str,
    hook: &GitHubWebhookPush,
    plans: Vec<SearchItem>,
) -> HandleResult<Vec<Plan>> {
    let mut parsed = Vec::with_capacity(plans.len());
    for plan in plans {
        match github.contents(token, hook.repository.id, &plan.path) {
            Ok(contents) => {
                match contents.decode() {
                    Ok(bytes) => {
                        match Plan::from_bytes(bytes.as_slice()) {
                            Ok(plan) => parsed.push(plan),
                            Err(err) => debug!("{}, {}", plan.path, err),
                        }
                    }
                    Err(err) => {
                        return HandleResult::Err(Response::with((
                            status::UnprocessableEntity,
                            format!("{}, {}", plan.path, err),
                        )))
                    }
                }
            }
            Err(err) => {
                return HandleResult::Err(Response::with(
                    (status::BadGateway, format!("{}, {}", plan.path, err)),
                ))
            }
        }
    }
    HandleResult::Ok(parsed)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::compartments::enter_realm;
use crate::dom::audionode::MAX_CHANNEL_COUNT;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AudioBufferBinding::{
    self, AudioBufferMethods, AudioBufferOptions,
};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::window::Window;
use crate::script_runtime::JSContext as SafeJSContext;
use dom_struct::dom_struct;
use js::jsapi::JS_GetArrayBufferViewBuffer;
use js::jsapi::{Heap, JSContext, JSObject};
use js::rust::wrappers::DetachArrayBuffer;
use js::rust::CustomAutoRooterGuard;
use js::typedarray::{CreateWith, Float32Array};
use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
use std::cell::Ref;
use std::cmp::min;
use std::ptr::{self, NonNull};

// Spec mandates at least [8000, 96000], we use [8000, 192000] to match Firefox
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer
pub const MIN_SAMPLE_RATE: f32 = 8000.;
pub const MAX_SAMPLE_RATE: f32 = 192000.;

type JSAudioChannel = Heap<*mut JSObject>;

/// The AudioBuffer keeps its data either in js_channels
/// or in shared_channels if js_channels buffers are detached.
///
/// js_channels buffers are (re)attached right before calling GetChannelData
/// and remain attached until its contents are needed by some other API
/// implementation. Follow https://webaudio.github.io/web-audio-api/#acquire-the-content
/// to know in which situations js_channels buffers must be detached.
///
#[dom_struct]
pub struct AudioBuffer {
    reflector_: Reflector,
    /// Float32Arrays returned by calls to GetChannelData.
    #[ignore_malloc_size_of = "mozjs"]
    js_channels: DomRefCell<Vec<JSAudioChannel>>,
    /// Aggregates the data from js_channels.
    /// This is Some<T> iff the buffers in js_channels are detached.
    #[ignore_malloc_size_of = "servo_media"]
    shared_channels: DomRefCell<Option<ServoMediaAudioBuffer>>,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-samplerate
    sample_rate: f32,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-length
    length: u32,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-duration
    duration: f64,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-numberofchannels
    number_of_channels: u32,
}

impl AudioBuffer {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    pub fn new_inherited(number_of_channels: u32, length: u32, sample_rate: f32) -> AudioBuffer {
        let vec = (0..number_of_channels).map(|_| Heap::default()).collect();
        AudioBuffer {
            reflector_: Reflector::new(),
            js_channels: DomRefCell::new(vec),
            shared_channels: DomRefCell::new(None),
            sample_rate,
            length,
            duration: length as f64 / sample_rate as f64,
            number_of_channels,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(
        global: &Window,
        number_of_channels: u32,
        length: u32,
        sample_rate: f32,
        initial_data: Option<&[Vec<f32>]>,
    ) -> DomRoot<AudioBuffer> {
        let buffer = AudioBuffer::new_inherited(number_of_channels, length, sample_rate);
        let buffer = reflect_dom_object(Box::new(buffer), global, AudioBufferBinding::Wrap);
        buffer.set_initial_data(initial_data);
        buffer
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-audiobuffer
    pub fn Constructor(
        window: &Window,
        options: &AudioBufferOptions,
    ) -> Fallible<DomRoot<AudioBuffer>> {
        if options.length <= 0 ||
            options.numberOfChannels <= 0 ||
            options.numberOfChannels > MAX_CHANNEL_COUNT ||
            *options.sampleRate < MIN_SAMPLE_RATE ||
            *options.sampleRate > MAX_SAMPLE_RATE
        {
            return Err(Error::NotSupported);
        }
        Ok(AudioBuffer::new(
            window,
            options.numberOfChannels,
            options.length,
            *options.sampleRate,
            None,
        ))
    }

    // Initialize the underlying channels data with initial data provided by
    // the user or silence otherwise.
    fn set_initial_data(&self, initial_data: Option<&[Vec<f32>]>) {
        let mut channels =
            ServoMediaAudioBuffer::new(self.number_of_channels as u8, self.length as usize);
        for channel in 0..self.number_of_channels {
            channels.buffers[channel as usize] = match initial_data {
                Some(data) => data[channel as usize].clone(),
                None => vec![0.; self.length as usize],
            };
        }
        *self.shared_channels.borrow_mut() = Some(channels);
    }

    #[allow(unsafe_code)]
    unsafe fn restore_js_channel_data(&self, cx: *mut JSContext) -> bool {
        let _ac = enter_realm(&*self);
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            if !channel.get().is_null() {
                // Already have data in JS array.
                continue;
            }

            rooted!(in (cx) let mut array = ptr::null_mut::<JSObject>());
            if let Some(ref shared_channels) = *self.shared_channels.borrow() {
                // Step 4. of
                // https://webaudio.github.io/web-audio-api/#acquire-the-content
                // "Attach ArrayBuffers containing copies of the data to the AudioBuffer,
                // to be returned by the next call to getChannelData()".
                if Float32Array::create(
                    cx,
                    CreateWith::Slice(&shared_channels.buffers[i]),
                    array.handle_mut(),
                )
                .is_err()
                {
                    return false;
                }
            }
            channel.set(array.get());
        }

        *self.shared_channels.borrow_mut() = None;

        true
    }

    // https://webaudio.github.io/web-audio-api/#acquire-the-content
    #[allow(unsafe_code)]
    fn acquire_contents(&self) -> Option<ServoMediaAudioBuffer> {
        let mut result =
            ServoMediaAudioBuffer::new(self.number_of_channels as u8, self.length as usize);
        let cx = self.global().get_cx();
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            // Step 1.
            if channel.get().is_null() {
                return None;
            }

            // Step 2.
            let channel_data = unsafe {
                typedarray!(in(*cx) let array: Float32Array = channel.get());
                if let Ok(array) = array {
                    let data = array.to_vec();
                    let mut is_shared = false;
                    rooted!(in (*cx) let view_buffer =
                        JS_GetArrayBufferViewBuffer(*cx, channel.handle(), &mut is_shared));
                    // This buffer is always created unshared
                    debug_assert!(!is_shared);
                    let _ = DetachArrayBuffer(*cx, view_buffer.handle());
                    data
                } else {
                    return None;
                }
            };

            channel.set(ptr::null_mut());

            // Step 3.
            result.buffers[i] = channel_data;
        }

        Some(result)
    }

    pub fn get_channels(&self) -> Ref<Option<ServoMediaAudioBuffer>> {
        if self.shared_channels.borrow().is_none() {
            let channels = self.acquire_contents();
            if channels.is_some() {
                *self.shared_channels.borrow_mut() = channels;
            }
        }
        return self.shared_channels.borrow();
    }
}

impl AudioBufferMethods for AudioBuffer {
    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-samplerate
    fn SampleRate(&self) -> Finite<f32> {
        Finite::wrap(self.sample_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-length
    fn Length(&self) -> u32 {
        self.length
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-duration
    fn Duration(&self) -> Finite<f64> {
        Finite::wrap(self.duration)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-numberofchannels
    fn NumberOfChannels(&self) -> u32 {
        self.number_of_channels
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-getchanneldata
    #[allow(unsafe_code)]
    fn GetChannelData(&self, cx: SafeJSContext, channel: u32) -> Fallible<NonNull<JSObject>> {
        if channel >= self.number_of_channels {
            return Err(Error::IndexSize);
        }

        unsafe {
            if !self.restore_js_channel_data(*cx) {
                return Err(Error::JSFailed);
            }

            Ok(NonNull::new_unchecked(
                self.js_channels.borrow()[channel as usize].get(),
            ))
        }
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copyfromchannel
    #[allow(unsafe_code)]
    fn CopyFromChannel(
        &self,
        mut destination: CustomAutoRooterGuard<Float32Array>,
        channel_number: u32,
        start_in_channel: u32,
    ) -> Fallible<()> {
        if destination.is_shared() {
            return Err(Error::Type("Cannot copy to shared buffer".to_owned()));
        }

        if channel_number >= self.number_of_channels || start_in_channel >= self.length {
            return Err(Error::IndexSize);
        }

        let bytes_to_copy = min(self.length - start_in_channel, destination.len() as u32) as usize;
        let cx = self.global().get_cx();
        let channel_number = channel_number as usize;
        let offset = start_in_channel as usize;
        let mut dest = Vec::with_capacity(destination.len());

        // We either copy form js_channels or shared_channels.
        let js_channel = self.js_channels.borrow()[channel_number].get();
        if !js_channel.is_null() {
            typedarray!(in(*cx) let array: Float32Array = js_channel);
            if let Ok(array) = array {
                let data = unsafe { array.as_slice() };
                dest.extend_from_slice(&data[offset..offset + bytes_to_copy]);
            }
        } else if let Some(ref shared_channels) = *self.shared_channels.borrow() {
            if let Some(shared_channel) = shared_channels.buffers.get(channel_number) {
                dest.extend_from_slice(&shared_channel.as_slice()[offset..offset + bytes_to_copy]);
            }
        }

        unsafe {
            destination.update(&dest);
        }

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copytochannel
    #[allow(unsafe_code)]
    fn CopyToChannel(
        &self,
        source: CustomAutoRooterGuard<Float32Array>,
        channel_number: u32,
        start_in_channel: u32,
    ) -> Fallible<()> {
        if source.is_shared() {
            return Err(Error::Type("Cannot copy from shared buffer".to_owned()));
        }

        if channel_number >= self.number_of_channels || start_in_channel > (source.len() as u32) {
            return Err(Error::IndexSize);
        }

        let cx = self.global().get_cx();
        if unsafe { !self.restore_js_channel_data(*cx) } {
            return Err(Error::JSFailed);
        }

        let js_channel = self.js_channels.borrow()[channel_number as usize].get();
        if js_channel.is_null() {
            // The array buffer was detached.
            return Err(Error::IndexSize);
        }

        typedarray!(in(*cx) let js_channel: Float32Array = js_channel);
        if let Ok(mut js_channel) = js_channel {
            let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
            let js_channel_data = unsafe { js_channel.as_mut_slice() };
            let (_, js_channel_data) = js_channel_data.split_at_mut(start_in_channel as usize);
            unsafe {
                js_channel_data[0..bytes_to_copy]
                    .copy_from_slice(&source.as_slice()[0..bytes_to_copy])
            };
        } else {
            return Err(Error::IndexSize);
        }

        Ok(())
    }
}
<commit_msg>Remove some usage of unsafe code in AudioBuffer<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::compartments::enter_realm;
use crate::dom::audionode::MAX_CHANNEL_COUNT;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AudioBufferBinding::{
    self, AudioBufferMethods, AudioBufferOptions,
};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::window::Window;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
use js::jsapi::JS_GetArrayBufferViewBuffer;
use js::jsapi::{Heap, JSObject};
use js::rust::wrappers::DetachArrayBuffer;
use js::rust::CustomAutoRooterGuard;
use js::typedarray::{CreateWith, Float32Array};
use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;
use std::cell::Ref;
use std::cmp::min;
use std::ptr::{self, NonNull};

// Spec mandates at least [8000, 96000], we use [8000, 192000] to match Firefox
// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createbuffer
pub const MIN_SAMPLE_RATE: f32 = 8000.;
pub const MAX_SAMPLE_RATE: f32 = 192000.;

type JSAudioChannel = Heap<*mut JSObject>;

/// The AudioBuffer keeps its data either in js_channels
/// or in shared_channels if js_channels buffers are detached.
///
/// js_channels buffers are (re)attached right before calling GetChannelData
/// and remain attached until its contents are needed by some other API
/// implementation. Follow https://webaudio.github.io/web-audio-api/#acquire-the-content
/// to know in which situations js_channels buffers must be detached.
///
#[dom_struct]
pub struct AudioBuffer {
    reflector_: Reflector,
    /// Float32Arrays returned by calls to GetChannelData.
    #[ignore_malloc_size_of = "mozjs"]
    js_channels: DomRefCell<Vec<JSAudioChannel>>,
    /// Aggregates the data from js_channels.
    /// This is Some<T> iff the buffers in js_channels are detached.
    #[ignore_malloc_size_of = "servo_media"]
    shared_channels: DomRefCell<Option<ServoMediaAudioBuffer>>,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-samplerate
    sample_rate: f32,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-length
    length: u32,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-duration
    duration: f64,
    /// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-numberofchannels
    number_of_channels: u32,
}

impl AudioBuffer {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    pub fn new_inherited(number_of_channels: u32, length: u32, sample_rate: f32) -> AudioBuffer {
        let vec = (0..number_of_channels).map(|_| Heap::default()).collect();
        AudioBuffer {
            reflector_: Reflector::new(),
            js_channels: DomRefCell::new(vec),
            shared_channels: DomRefCell::new(None),
            sample_rate,
            length,
            duration: length as f64 / sample_rate as f64,
            number_of_channels,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(
        global: &Window,
        number_of_channels: u32,
        length: u32,
        sample_rate: f32,
        initial_data: Option<&[Vec<f32>]>,
    ) -> DomRoot<AudioBuffer> {
        let buffer = AudioBuffer::new_inherited(number_of_channels, length, sample_rate);
        let buffer = reflect_dom_object(Box::new(buffer), global, AudioBufferBinding::Wrap);
        buffer.set_initial_data(initial_data);
        buffer
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-audiobuffer
    pub fn Constructor(
        window: &Window,
        options: &AudioBufferOptions,
    ) -> Fallible<DomRoot<AudioBuffer>> {
        if options.length <= 0 ||
            options.numberOfChannels <= 0 ||
            options.numberOfChannels > MAX_CHANNEL_COUNT ||
            *options.sampleRate < MIN_SAMPLE_RATE ||
            *options.sampleRate > MAX_SAMPLE_RATE
        {
            return Err(Error::NotSupported);
        }
        Ok(AudioBuffer::new(
            window,
            options.numberOfChannels,
            options.length,
            *options.sampleRate,
            None,
        ))
    }

    // Initialize the underlying channels data with initial data provided by
    // the user or silence otherwise.
    fn set_initial_data(&self, initial_data: Option<&[Vec<f32>]>) {
        let mut channels =
            ServoMediaAudioBuffer::new(self.number_of_channels as u8, self.length as usize);
        for channel in 0..self.number_of_channels {
            channels.buffers[channel as usize] = match initial_data {
                Some(data) => data[channel as usize].clone(),
                None => vec![0.; self.length as usize],
            };
        }
        *self.shared_channels.borrow_mut() = Some(channels);
    }

    #[allow(unsafe_code)]
    fn restore_js_channel_data(&self, cx: JSContext) -> bool {
        let _ac = enter_realm(&*self);
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            if !channel.get().is_null() {
                // Already have data in JS array.
                continue;
            }

            rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
            if let Some(ref shared_channels) = *self.shared_channels.borrow() {
                // Step 4. of
                // https://webaudio.github.io/web-audio-api/#acquire-the-content
                // "Attach ArrayBuffers containing copies of the data to the AudioBuffer,
                // to be returned by the next call to getChannelData()".
                unsafe {
                    if Float32Array::create(
                        *cx,
                        CreateWith::Slice(&shared_channels.buffers[i]),
                        array.handle_mut(),
                    )
                    .is_err()
                    {
                        return false;
                    }
                }
            }
            channel.set(array.get());
        }

        *self.shared_channels.borrow_mut() = None;

        true
    }

    // https://webaudio.github.io/web-audio-api/#acquire-the-content
    #[allow(unsafe_code)]
    fn acquire_contents(&self) -> Option<ServoMediaAudioBuffer> {
        let mut result =
            ServoMediaAudioBuffer::new(self.number_of_channels as u8, self.length as usize);
        let cx = self.global().get_cx();
        for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
            // Step 1.
            if channel.get().is_null() {
                return None;
            }

            // Step 2.
            let channel_data = unsafe {
                typedarray!(in(*cx) let array: Float32Array = channel.get());
                if let Ok(array) = array {
                    let data = array.to_vec();
                    let mut is_shared = false;
                    rooted!(in (*cx) let view_buffer =
                        JS_GetArrayBufferViewBuffer(*cx, channel.handle(), &mut is_shared));
                    // This buffer is always created unshared
                    debug_assert!(!is_shared);
                    let _ = DetachArrayBuffer(*cx, view_buffer.handle());
                    data
                } else {
                    return None;
                }
            };

            channel.set(ptr::null_mut());

            // Step 3.
            result.buffers[i] = channel_data;
        }

        Some(result)
    }

    pub fn get_channels(&self) -> Ref<Option<ServoMediaAudioBuffer>> {
        if self.shared_channels.borrow().is_none() {
            let channels = self.acquire_contents();
            if channels.is_some() {
                *self.shared_channels.borrow_mut() = channels;
            }
        }
        return self.shared_channels.borrow();
    }
}

impl AudioBufferMethods for AudioBuffer {
    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-samplerate
    fn SampleRate(&self) -> Finite<f32> {
        Finite::wrap(self.sample_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-length
    fn Length(&self) -> u32 {
        self.length
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-duration
    fn Duration(&self) -> Finite<f64> {
        Finite::wrap(self.duration)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-numberofchannels
    fn NumberOfChannels(&self) -> u32 {
        self.number_of_channels
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-getchanneldata
    #[allow(unsafe_code)]
    fn GetChannelData(&self, cx: JSContext, channel: u32) -> Fallible<NonNull<JSObject>> {
        if channel >= self.number_of_channels {
            return Err(Error::IndexSize);
        }

        if !self.restore_js_channel_data(cx) {
            return Err(Error::JSFailed);
        }
        unsafe {
            Ok(NonNull::new_unchecked(
                self.js_channels.borrow()[channel as usize].get(),
            ))
        }
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copyfromchannel
    #[allow(unsafe_code)]
    fn CopyFromChannel(
        &self,
        mut destination: CustomAutoRooterGuard<Float32Array>,
        channel_number: u32,
        start_in_channel: u32,
    ) -> Fallible<()> {
        if destination.is_shared() {
            return Err(Error::Type("Cannot copy to shared buffer".to_owned()));
        }

        if channel_number >= self.number_of_channels || start_in_channel >= self.length {
            return Err(Error::IndexSize);
        }

        let bytes_to_copy = min(self.length - start_in_channel, destination.len() as u32) as usize;
        let cx = self.global().get_cx();
        let channel_number = channel_number as usize;
        let offset = start_in_channel as usize;
        let mut dest = Vec::with_capacity(destination.len());

        // We either copy form js_channels or shared_channels.
        let js_channel = self.js_channels.borrow()[channel_number].get();
        if !js_channel.is_null() {
            typedarray!(in(*cx) let array: Float32Array = js_channel);
            if let Ok(array) = array {
                let data = unsafe { array.as_slice() };
                dest.extend_from_slice(&data[offset..offset + bytes_to_copy]);
            }
        } else if let Some(ref shared_channels) = *self.shared_channels.borrow() {
            if let Some(shared_channel) = shared_channels.buffers.get(channel_number) {
                dest.extend_from_slice(&shared_channel.as_slice()[offset..offset + bytes_to_copy]);
            }
        }

        unsafe {
            destination.update(&dest);
        }

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copytochannel
    #[allow(unsafe_code)]
    fn CopyToChannel(
        &self,
        source: CustomAutoRooterGuard<Float32Array>,
        channel_number: u32,
        start_in_channel: u32,
    ) -> Fallible<()> {
        if source.is_shared() {
            return Err(Error::Type("Cannot copy from shared buffer".to_owned()));
        }

        if channel_number >= self.number_of_channels || start_in_channel > (source.len() as u32) {
            return Err(Error::IndexSize);
        }

        let cx = self.global().get_cx();
        if !self.restore_js_channel_data(cx) {
            return Err(Error::JSFailed);
        }

        let js_channel = self.js_channels.borrow()[channel_number as usize].get();
        if js_channel.is_null() {
            // The array buffer was detached.
            return Err(Error::IndexSize);
        }

        typedarray!(in(*cx) let js_channel: Float32Array = js_channel);
        if let Ok(mut js_channel) = js_channel {
            let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
            let js_channel_data = unsafe { js_channel.as_mut_slice() };
            let (_, js_channel_data) = js_channel_data.split_at_mut(start_in_channel as usize);
            unsafe {
                js_channel_data[0..bytes_to_copy]
                    .copy_from_slice(&source.as_slice()[0..bytes_to_copy])
            };
        } else {
            return Err(Error::IndexSize);
        }

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


<commit_before>use Environment;
use config::Config;
use database::Database;
use listeners::ReceivesEvents;

use xivdb::XivDb;
use xivdb::error::*;

use discord::{Discord, State, Connection};

use serde_json;

use chrono::prelude::*;

use std::fs::{OpenOptions, File};
use std::path::Path;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{channel, Receiver};
use std::thread;

mod creation;

pub use self::creation::create_bot;

pub struct LalafellBot {
  pub environment: Environment,
  pub config: Config,
  pub discord: Discord,
  pub connection: Arc<Mutex<Connection>>,
  // TODO: Use RwLock instead of Mutex for multiple reads
  pub state: RwLock<Option<State>>,
  pub xivdb: XivDb,
  pub database: Mutex<Database>,
  pub listeners: Mutex<Vec<Box<ReceivesEvents + Send>>>
}

impl Drop for LalafellBot {
  fn drop(&mut self) {
    self.save_database(None).unwrap()
  }
}

impl LalafellBot {
  pub fn new(environment: Environment, config: Config) -> Result<LalafellBot> {
    let discord = Discord::from_bot_token(&environment.discord_bot_token).chain_err(|| "could not start discord from bot token")?;
    let mut database = LalafellBot::load_database(&environment.database_location)?;
    database.last_saved = UTC::now().timestamp();

    let (connection, ready) = discord.connect().chain_err(|| "could not connect to discord")?;
    let state = State::new(ready);

    Ok(LalafellBot {
      environment: environment,
      config: config,
      discord: discord,
      connection: Arc::new(Mutex::new(connection)),
      state: RwLock::new(Some(state)),
      xivdb: XivDb::default(),
      database: Mutex::new(database),
      listeners: Mutex::default()
    })
  }

  pub fn load_database(location: &str) -> Result<Database> {
    if !Path::new(location).exists() {
      return Ok(Database::default());
    }
    let f = File::open(location).chain_err(|| "could not open or create database file")?;
    serde_json::from_reader(f).chain_err(|| "could not deserialize database")
  }

  pub fn save_database(&self, location: Option<&str>) -> Result<()> {
    let f = OpenOptions::new()
      .write(true)
      .truncate(true)
      .create(true)
      .open(location.unwrap_or(&self.environment.database_location))
      .chain_err(|| "could not open or create database file")?;
    serde_json::to_writer(f, &self.database).chain_err(|| "could not serialize database")
  }

  pub fn start_loop(&self, loop_cancel: Receiver<()>) -> Result<()> {
    self.connection.lock().unwrap().set_game_name("with other Lalafell.".to_string());
    let (event_channel_tx, event_channel_rx) = channel();
    let thread_connection = self.connection.clone();
    thread::spawn(move || {
      loop {
        if let Err(e) = event_channel_tx.send(thread_connection.lock().unwrap().recv_event()) {
          error!("error sending event: {}", e);
        }
      }
    });
    info!("Starting main loop");
    loop {
      let event = select! {
        _ = loop_cancel.recv() => break,
        event = event_channel_rx.recv() => event
      };
      let event = match event {
        Ok(Ok(e)) => e,
        Ok(Err(e)) => {
          warn!("could not receive event from select: {}", e);
          continue;
        },
        Err(e) => {
          warn!("could not receive discord event: {}", e);
          continue;
        }
      };
      {
        let mut state_option = self.state.write().unwrap();
        let mut state = state_option.as_mut().unwrap();
        state.update(&event);
      }
      {
        let listeners = self.listeners.lock().unwrap();
        for listener in listeners.iter() {
          listener.receive(&event);
        }
      }
    }
    info!("Main loop stopped");
    Ok(())
  }
}
<commit_msg>docs: remove finished todo<commit_after>use Environment;
use config::Config;
use database::Database;
use listeners::ReceivesEvents;

use xivdb::XivDb;
use xivdb::error::*;

use discord::{Discord, State, Connection};

use serde_json;

use chrono::prelude::*;

use std::fs::{OpenOptions, File};
use std::path::Path;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{channel, Receiver};
use std::thread;

mod creation;

pub use self::creation::create_bot;

pub struct LalafellBot {
  pub environment: Environment,
  pub config: Config,
  pub discord: Discord,
  pub connection: Arc<Mutex<Connection>>,
  pub state: RwLock<Option<State>>,
  pub xivdb: XivDb,
  pub database: Mutex<Database>,
  pub listeners: Mutex<Vec<Box<ReceivesEvents + Send>>>
}

impl Drop for LalafellBot {
  fn drop(&mut self) {
    self.save_database(None).unwrap()
  }
}

impl LalafellBot {
  pub fn new(environment: Environment, config: Config) -> Result<LalafellBot> {
    let discord = Discord::from_bot_token(&environment.discord_bot_token).chain_err(|| "could not start discord from bot token")?;
    let mut database = LalafellBot::load_database(&environment.database_location)?;
    database.last_saved = UTC::now().timestamp();

    let (connection, ready) = discord.connect().chain_err(|| "could not connect to discord")?;
    let state = State::new(ready);

    Ok(LalafellBot {
      environment: environment,
      config: config,
      discord: discord,
      connection: Arc::new(Mutex::new(connection)),
      state: RwLock::new(Some(state)),
      xivdb: XivDb::default(),
      database: Mutex::new(database),
      listeners: Mutex::default()
    })
  }

  pub fn load_database(location: &str) -> Result<Database> {
    if !Path::new(location).exists() {
      return Ok(Database::default());
    }
    let f = File::open(location).chain_err(|| "could not open or create database file")?;
    serde_json::from_reader(f).chain_err(|| "could not deserialize database")
  }

  pub fn save_database(&self, location: Option<&str>) -> Result<()> {
    let f = OpenOptions::new()
      .write(true)
      .truncate(true)
      .create(true)
      .open(location.unwrap_or(&self.environment.database_location))
      .chain_err(|| "could not open or create database file")?;
    serde_json::to_writer(f, &self.database).chain_err(|| "could not serialize database")
  }

  pub fn start_loop(&self, loop_cancel: Receiver<()>) -> Result<()> {
    self.connection.lock().unwrap().set_game_name("with other Lalafell.".to_string());
    let (event_channel_tx, event_channel_rx) = channel();
    let thread_connection = self.connection.clone();
    thread::spawn(move || {
      loop {
        if let Err(e) = event_channel_tx.send(thread_connection.lock().unwrap().recv_event()) {
          error!("error sending event: {}", e);
        }
      }
    });
    info!("Starting main loop");
    loop {
      let event = select! {
        _ = loop_cancel.recv() => break,
        event = event_channel_rx.recv() => event
      };
      let event = match event {
        Ok(Ok(e)) => e,
        Ok(Err(e)) => {
          warn!("could not receive event from select: {}", e);
          continue;
        },
        Err(e) => {
          warn!("could not receive discord event: {}", e);
          continue;
        }
      };
      {
        let mut state_option = self.state.write().unwrap();
        let mut state = state_option.as_mut().unwrap();
        state.update(&event);
      }
      {
        let listeners = self.listeners.lock().unwrap();
        for listener in listeners.iter() {
          listener.receive(&event);
        }
      }
    }
    info!("Main loop stopped");
    Ok(())
  }
}
<|endoftext|>


<commit_before>use libc::{c_char, c_uint};
use ffi::prelude::{LLVMBuilderRef, LLVMValueRef};
use ffi::{core, LLVMBuilder, LLVMRealPredicate, LLVMIntPredicate};
use cbox::{CSemiBox, DisposeRef};
use std::marker::PhantomData;
use std::mem;
use block::BasicBlock;
use context::Context;
use types::Type;
use value::{Function, Value, Predicate};

static NULL_NAME:[c_char; 1] = [0];

/// This provides a uniform API for creating instructions and inserting them into a basic block.
pub struct Builder(PhantomData<[u8]>);
native_ref!(&Builder = LLVMBuilderRef);
macro_rules! bin_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            unsafe { core::$func(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()) }.into()
        }
    );
    ($name:ident, $ifunc:ident, $ffunc:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            let ty = left.get_type();
            unsafe {
                (if ty.is_integer() {
                    core::$ifunc
                } else {
                    core::$ffunc
                })(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()).into()
            }
        }
    );
);
macro_rules! un_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, value: &Value) -> &Value {
            unsafe { core::$func(self.into(), value.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
        }
    );
);
impl Builder {
    /// Create a new builder in the context given.
    pub fn new(context: &Context) -> CSemiBox<Builder> {
        CSemiBox::new(unsafe { core::LLVMCreateBuilderInContext(context.into()) }.into())
    }
    /// Position the builder at the end of `block`.
    pub fn position_at_end(&self, block: &BasicBlock) {
        unsafe { core::LLVMPositionBuilderAtEnd(self.into(), block.into()) }
    }
    /// Build an instruction that returns from the function with void.
    pub fn build_ret_void(&self) -> &Value {
        unsafe { core::LLVMBuildRetVoid(self.into()) }.into()
    }
    /// Build an instruction that returns from the function with `value`.
    pub fn build_ret(&self, value: &Value) -> &Value {
        unsafe { core::LLVMBuildRet(self.into(), value.into()) }.into()
    }
    /// Build an instruction that allocates an array with the element type `elem` and the size `size`.
    ///
    /// The size of this array will be the size of `elem` times `size`.
    pub fn build_array_alloca(&self, elem: &Type, size: &Value) -> &Value {
        unsafe { core::LLVMBuildArrayAlloca(self.into(), elem.into(), size.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that allocates a pointer to fit the size of `ty` then returns this pointer.
    pub fn build_alloca(&self, ty: &Type) -> &Value {
        unsafe { core::LLVMBuildAlloca(self.into(), ty.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that frees the `val`, which _MUST_ be a pointer that was returned
    /// from `build_alloca`.
    pub fn build_free(&self, val: &Value) -> &Value {
        unsafe { core::LLVMBuildFree(self.into(), val.into()) }.into()
    }
    /// Build an instruction that store the value `val` in the pointer `ptr`.
    pub fn build_store(&self, val: &Value, ptr: &Value) -> &Value {
        unsafe { core::LLVMBuildStore(self.into(), val.into(), ptr.into()) }.into()
    }
    /// Build an instruction that branches to the block `dest`.
    pub fn build_br(&self, dest: &BasicBlock) -> &Value {
        unsafe { core::LLVMBuildBr(self.into(), dest.into()).into() }
    }
    /// Build an instruction that branches to `if_block` if `cond` evaluates to true, and `else_block` otherwise.
    pub fn build_cond_br(&self, cond: &Value, if_block: &BasicBlock, else_block: Option<&BasicBlock>) -> &Value {
        unsafe { core::LLVMBuildCondBr(self.into(), cond.into(), if_block.into(), mem::transmute(else_block)).into() }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 0);
            call.into()
        }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_tail_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 1);
            call.into()
        }
    }
    /// Build an instruction that yields to `true_val` if `cond` is equal to `1`, and `false_val` otherwise.
    pub fn build_select(&self, cond: &Value, true_val: &Value, false_val: &Value) -> &Value {
        unsafe { core::LLVMBuildSelect(self.into(), cond.into(), true_val.into(), false_val.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that casts a value into a certain type.
    pub fn build_bit_cast(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildBitCast(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction to bitcast in integer into a pointer.
    pub fn build_int_to_ptr(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildIntToPtr(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that zero extends its operand to the type `dest`.
    pub fn build_zext(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildZExtOrBitCast(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that truncates the high-order bits of value to fit into a certain type.
    pub fn build_trunc(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildTrunc(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that inserts a value into an aggregate data value.
    pub fn build_insert_value(&self, agg: &Value, elem: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildInsertValue(self.into(), agg.into(), elem.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that extracts a value from an aggregate data value.
    pub fn build_extract_value(&self, agg: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildExtractValue(self.into(), agg.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that computes the address of a subelement of an aggregate data structure.
    ///
    /// Basically type-safe pointer arithmetic.
    pub fn build_gep(&self, pointer: &Value, indices: &[&Value]) -> &Value {
        unsafe { core::LLVMBuildInBoundsGEP(self.into(), pointer.into(), indices.as_ptr() as *mut LLVMValueRef, indices.len() as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that runs whichever block matches the value, or `default` if none of them matched it.
    pub fn build_switch(&self, value: &Value, default: &BasicBlock, cases: &[(&Value, &BasicBlock)]) -> &Value {
        unsafe {
            let switch = core::LLVMBuildSwitch(self.into(), value.into(), default.into(), cases.len() as c_uint);
            for case in cases {
                core::LLVMAddCase(switch, case.0.into(), case.1.into());
            }
            switch.into()
        }
    }
    un_op!{build_load, LLVMBuildLoad}
    un_op!{build_neg, LLVMBuildNeg}
    un_op!{build_not, LLVMBuildNot}
    bin_op!{build_add, LLVMBuildAdd, LLVMBuildFAdd}
    bin_op!{build_sub, LLVMBuildSub, LLVMBuildFSub}
    bin_op!{build_mul, LLVMBuildMul, LLVMBuildFMul}
    bin_op!{build_div, LLVMBuildSDiv, LLVMBuildFDiv}
    bin_op!{build_shl, LLVMBuildShl}
    bin_op!{build_ashr, LLVMBuildAShr}
    bin_op!{build_and, LLVMBuildAnd}
    bin_op!{build_or, LLVMBuildOr}
    /// Build an instruction to compare the values `a` and `b` with the predicate / comparative operator `pred`.
    pub fn build_cmp(&self, a: &Value, b: &Value, pred: Predicate) -> &Value {
        let (at, bt) = (a.get_type(), b.get_type());
        assert_eq!(at, bt);
        if at.is_integer() {
            let pred = match pred {
                Predicate::Equal => LLVMIntPredicate::LLVMIntEQ,
                Predicate::NotEqual => LLVMIntPredicate::LLVMIntNE,
                Predicate::GreaterThan => LLVMIntPredicate::LLVMIntSGT,
                Predicate::GreaterThanOrEqual => LLVMIntPredicate::LLVMIntSGE,
                Predicate::LessThan => LLVMIntPredicate::LLVMIntSLT,
                Predicate::LessThanOrEqual => LLVMIntPredicate::LLVMIntSLE
            };
            unsafe { core::LLVMBuildICmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else if at.is_float() {
            let pred = match pred {
                Predicate::Equal => LLVMRealPredicate::LLVMRealOEQ,
                Predicate::NotEqual => LLVMRealPredicate::LLVMRealONE,
                Predicate::GreaterThan => LLVMRealPredicate::LLVMRealOGT,
                Predicate::GreaterThanOrEqual => LLVMRealPredicate::LLVMRealOGE,
                Predicate::LessThan => LLVMRealPredicate::LLVMRealOLT,
                Predicate::LessThanOrEqual => LLVMRealPredicate::LLVMRealOLE
            };
            unsafe { core::LLVMBuildFCmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else {
            panic!("expected numzextbers, got {:?}", at)
        }
    }
}

impl DisposeRef for Builder {
    type RefTo = LLVMBuilder;
    #[inline(always)]
    unsafe fn dispose(ptr: LLVMBuilderRef) {
        core::LLVMDisposeBuilder(ptr)
    }
}
<commit_msg>Use macro for `Builder`'s `DisposeRef` impl<commit_after>use libc::{c_char, c_uint};
use ffi::prelude::{LLVMBuilderRef, LLVMValueRef};
use ffi::{core, LLVMBuilder, LLVMRealPredicate, LLVMIntPredicate};
use cbox::CSemiBox;
use std::marker::PhantomData;
use std::mem;
use block::BasicBlock;
use context::Context;
use types::Type;
use value::{Function, Value, Predicate};

static NULL_NAME:[c_char; 1] = [0];

/// This provides a uniform API for creating instructions and inserting them into a basic block.
pub struct Builder(PhantomData<[u8]>);
native_ref!(&Builder = LLVMBuilderRef);
dispose!{Builder, LLVMBuilder, core::LLVMDisposeBuilder}
macro_rules! bin_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            unsafe { core::$func(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()) }.into()
        }
    );
    ($name:ident, $ifunc:ident, $ffunc:ident) => (
        pub fn $name(&self, left: &Value, right: &Value) -> &Value {
            let ty = left.get_type();
            unsafe {
                (if ty.is_integer() {
                    core::$ifunc
                } else {
                    core::$ffunc
                })(self.into(), left.into(), right.into(), NULL_NAME.as_ptr()).into()
            }
        }
    );
);
macro_rules! un_op(
    ($name:ident, $func:ident) => (
        pub fn $name(&self, value: &Value) -> &Value {
            unsafe { core::$func(self.into(), value.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
        }
    );
);
impl Builder {
    /// Create a new builder in the context given.
    pub fn new(context: &Context) -> CSemiBox<Builder> {
        CSemiBox::new(unsafe { core::LLVMCreateBuilderInContext(context.into()) }.into())
    }
    /// Position the builder at the end of `block`.
    pub fn position_at_end(&self, block: &BasicBlock) {
        unsafe { core::LLVMPositionBuilderAtEnd(self.into(), block.into()) }
    }
    /// Build an instruction that returns from the function with void.
    pub fn build_ret_void(&self) -> &Value {
        unsafe { core::LLVMBuildRetVoid(self.into()) }.into()
    }
    /// Build an instruction that returns from the function with `value`.
    pub fn build_ret(&self, value: &Value) -> &Value {
        unsafe { core::LLVMBuildRet(self.into(), value.into()) }.into()
    }
    /// Build an instruction that allocates an array with the element type `elem` and the size `size`.
    ///
    /// The size of this array will be the size of `elem` times `size`.
    pub fn build_array_alloca(&self, elem: &Type, size: &Value) -> &Value {
        unsafe { core::LLVMBuildArrayAlloca(self.into(), elem.into(), size.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that allocates a pointer to fit the size of `ty` then returns this pointer.
    pub fn build_alloca(&self, ty: &Type) -> &Value {
        unsafe { core::LLVMBuildAlloca(self.into(), ty.into(), NULL_NAME.as_ptr() as *const c_char) }.into()
    }
    /// Build an instruction that frees the `val`, which _MUST_ be a pointer that was returned
    /// from `build_alloca`.
    pub fn build_free(&self, val: &Value) -> &Value {
        unsafe { core::LLVMBuildFree(self.into(), val.into()) }.into()
    }
    /// Build an instruction that store the value `val` in the pointer `ptr`.
    pub fn build_store(&self, val: &Value, ptr: &Value) -> &Value {
        unsafe { core::LLVMBuildStore(self.into(), val.into(), ptr.into()) }.into()
    }
    /// Build an instruction that branches to the block `dest`.
    pub fn build_br(&self, dest: &BasicBlock) -> &Value {
        unsafe { core::LLVMBuildBr(self.into(), dest.into()).into() }
    }
    /// Build an instruction that branches to `if_block` if `cond` evaluates to true, and `else_block` otherwise.
    pub fn build_cond_br(&self, cond: &Value, if_block: &BasicBlock, else_block: Option<&BasicBlock>) -> &Value {
        unsafe { core::LLVMBuildCondBr(self.into(), cond.into(), if_block.into(), mem::transmute(else_block)).into() }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 0);
            call.into()
        }
    }
    /// Build an instruction that calls the function `func` with the arguments `args`.
    ///
    /// This will return the return value of the function.
    pub fn build_tail_call(&self, func: &Function, args: &[&Value]) -> &Value {
        unsafe {
            let call = core::LLVMBuildCall(self.into(), func.into(), args.as_ptr() as *mut LLVMValueRef, args.len() as c_uint, NULL_NAME.as_ptr());
            core::LLVMSetTailCall(call, 1);
            call.into()
        }
    }
    /// Build an instruction that yields to `true_val` if `cond` is equal to `1`, and `false_val` otherwise.
    pub fn build_select(&self, cond: &Value, true_val: &Value, false_val: &Value) -> &Value {
        unsafe { core::LLVMBuildSelect(self.into(), cond.into(), true_val.into(), false_val.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that casts a value into a certain type.
    pub fn build_bit_cast(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildBitCast(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction to bitcast in integer into a pointer.
    pub fn build_int_to_ptr(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildIntToPtr(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that zero extends its operand to the type `dest`.
    pub fn build_zext(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildZExtOrBitCast(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that truncates the high-order bits of value to fit into a certain type.
    pub fn build_trunc(&self, value: &Value, dest: &Type) -> &Value {
        unsafe { core::LLVMBuildTrunc(self.into(), value.into(), dest.into(), NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that inserts a value into an aggregate data value.
    pub fn build_insert_value(&self, agg: &Value, elem: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildInsertValue(self.into(), agg.into(), elem.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that extracts a value from an aggregate data value.
    pub fn build_extract_value(&self, agg: &Value, index: usize) -> &Value {
        unsafe { core::LLVMBuildExtractValue(self.into(), agg.into(), index as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that computes the address of a subelement of an aggregate data structure.
    ///
    /// Basically type-safe pointer arithmetic.
    pub fn build_gep(&self, pointer: &Value, indices: &[&Value]) -> &Value {
        unsafe { core::LLVMBuildInBoundsGEP(self.into(), pointer.into(), indices.as_ptr() as *mut LLVMValueRef, indices.len() as c_uint, NULL_NAME.as_ptr()).into() }
    }
    /// Build an instruction that runs whichever block matches the value, or `default` if none of them matched it.
    pub fn build_switch(&self, value: &Value, default: &BasicBlock, cases: &[(&Value, &BasicBlock)]) -> &Value {
        unsafe {
            let switch = core::LLVMBuildSwitch(self.into(), value.into(), default.into(), cases.len() as c_uint);
            for case in cases {
                core::LLVMAddCase(switch, case.0.into(), case.1.into());
            }
            switch.into()
        }
    }
    un_op!{build_load, LLVMBuildLoad}
    un_op!{build_neg, LLVMBuildNeg}
    un_op!{build_not, LLVMBuildNot}
    bin_op!{build_add, LLVMBuildAdd, LLVMBuildFAdd}
    bin_op!{build_sub, LLVMBuildSub, LLVMBuildFSub}
    bin_op!{build_mul, LLVMBuildMul, LLVMBuildFMul}
    bin_op!{build_div, LLVMBuildSDiv, LLVMBuildFDiv}
    bin_op!{build_shl, LLVMBuildShl}
    bin_op!{build_ashr, LLVMBuildAShr}
    bin_op!{build_and, LLVMBuildAnd}
    bin_op!{build_or, LLVMBuildOr}
    /// Build an instruction to compare the values `a` and `b` with the predicate / comparative operator `pred`.
    pub fn build_cmp(&self, a: &Value, b: &Value, pred: Predicate) -> &Value {
        let (at, bt) = (a.get_type(), b.get_type());
        assert_eq!(at, bt);
        if at.is_integer() {
            let pred = match pred {
                Predicate::Equal => LLVMIntPredicate::LLVMIntEQ,
                Predicate::NotEqual => LLVMIntPredicate::LLVMIntNE,
                Predicate::GreaterThan => LLVMIntPredicate::LLVMIntSGT,
                Predicate::GreaterThanOrEqual => LLVMIntPredicate::LLVMIntSGE,
                Predicate::LessThan => LLVMIntPredicate::LLVMIntSLT,
                Predicate::LessThanOrEqual => LLVMIntPredicate::LLVMIntSLE
            };
            unsafe { core::LLVMBuildICmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else if at.is_float() {
            let pred = match pred {
                Predicate::Equal => LLVMRealPredicate::LLVMRealOEQ,
                Predicate::NotEqual => LLVMRealPredicate::LLVMRealONE,
                Predicate::GreaterThan => LLVMRealPredicate::LLVMRealOGT,
                Predicate::GreaterThanOrEqual => LLVMRealPredicate::LLVMRealOGE,
                Predicate::LessThan => LLVMRealPredicate::LLVMRealOLT,
                Predicate::LessThanOrEqual => LLVMRealPredicate::LLVMRealOLE
            };
            unsafe { core::LLVMBuildFCmp(self.into(), pred, a.into(), b.into(), NULL_NAME.as_ptr()) }.into()
        } else {
            panic!("expected numzextbers, got {:?}", at)
        }
    }
}<|endoftext|>