<commit_before>//! The public FFI functions for initializing, adding sinks to, and applying a logger.

use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::ptr;
use std::str::from_utf8;

use fern::Dispatch;
use libc::{c_char, c_int};
use log::{error, LevelFilter as LogLevelFilter};

use crate::error::set_error_msg;
use crate::log::level_filter::LevelFilter;
use crate::log::logger::{add_sink, apply_logger, set_logger};
use crate::log::sink::Sink;
use crate::log::status::Status;
use crate::util::string::to_c;
use crate::log::inmem_buffer::fetch_buffer_contents;

/// Convenience function to direct all logging to stdout.
#[no_mangle]
pub extern "C" fn log_to_stdout(level_filter: LevelFilter) -> c_int {
    logger_init();

    let spec = match CString::new("stdout") {
        Ok(spec) => spec,
        Err(e) => {
            set_error_msg(e.to_string());
            return Status::CantConstructSink as c_int;
        }
    };

    let status = logger_attach_sink(spec.as_ptr(), level_filter);
    if status != 0 {
        return status;
    }

    let status = logger_apply();
    if status != 0 {
        return status;
    }

    Status::Success as c_int
}

/// Convenience function to direct all logging to stderr.
#[no_mangle]
pub extern "C" fn log_to_stderr(level_filter: LevelFilter) -> c_int {
    logger_init();

    let spec = match CString::new("stderr") {
        Ok(spec) => spec,
        Err(e) => {
            set_error_msg(e.to_string());
            return Status::CantConstructSink as c_int;
        }
    };

    let status = logger_attach_sink(spec.as_ptr(), level_filter);
    if status != 0 {
        return status;
    }

    let status = logger_apply();
    if status != 0 {
        return status;
    }

    Status::Success as c_int
}

/// Convenience function to direct all logging to a file.
#[no_mangle]
pub extern "C" fn log_to_file(
    file_name: *const c_char,
    level_filter: LevelFilter,
) -> c_int {
    logger_init();

    let spec = {
        if file_name.is_null() {
            return Status::CantConstructSink as c_int;
        }

        let file_name =
            match unsafe { CStr::from_ptr(file_name) }.to_str() {
                Ok(file_name) => file_name,
                Err(e) => {
                    set_error_msg(e.to_string());
                    return Status::CantConstructSink as c_int;
                }
            };

        let spec = format!("file {}", file_name);

        match CString::new(spec) {
            Ok(spec) => spec,
            Err(e) => {
                set_error_msg(e.to_string());
                return Status::CantConstructSink as c_int;
            }
        }
    };

    let status = logger_attach_sink(spec.as_ptr(), level_filter);
    if status != 0 {
        return status;
    }

    let status = logger_apply();
    if status != 0 {
        return status;
    }

    Status::Success as c_int
}

/// Convenience function to direct all logging to a thread local memory buffer.
#[no_mangle]
pub extern "C" fn log_to_buffer(level_filter: LevelFilter) -> c_int {
  logger_init();

  let spec = match CString::new("buffer") {
    Ok(spec) => spec,
    Err(e) => {
      set_error_msg(e.to_string());
      return Status::CantConstructSink as c_int;
    }
  };

  let status = logger_attach_sink(spec.as_ptr(), level_filter);
  if status != 0 {
    return status;
  }

  let status = logger_apply();
  if status != 0 {
    return status;
  }

  Status::Success as c_int
}

// C API uses something like the pledge API to select write locations, including:
//
// * stdout (`logger_attach_sink("stdout", LevelFilter_Info)`)
// * stderr (`logger_attach_sink("stderr", LevelFilter_Debug)`)
// * file w/ file path (`logger_attach_sink("file /some/file/path", LevelFilter_Trace)`)
//
// The general flow is:
//
// 1. Call `logger_init` to create a `Dispatch` struct.
// 2. Call `logger_attach_sink` to add an additional sink, using bitflags to set the metadata.
// 3. Call `logger_apply` to finalize the logger and enable logging to the configured sinks.
//
// Once `logger_apply` has been called, any additional calls to `logger_attach_sink` will fail
// with an error indicating the logger has been applied already.
//
// ```
// logger_init();
//
// int result = logger_attach_sink("stderr", FilterLevel_Debug);
// /* handle the error */
//
// int result = logger_attach_sink("file /some/file/path", FilterLevel_Info);
// /* handle the error */
//
// int result = logger_apply();
// /* handle the error */
// ```

/// Initialize the thread-local logger with no sinks.
///
/// This initialized logger does nothing until `logger_apply` has been called.
///
/// # Usage
///
/// ```c
/// logger_init();
/// ```
///
/// # Safety
///
/// This function is always safe to call.
#[no_mangle]
pub extern "C" fn logger_init() {
    set_logger(Dispatch::new());
}

/// Attach an additional sink to the thread-local logger.
///
/// This logger does nothing until `logger_apply` has been called.
///
/// Three types of sinks can be specified:
///
/// - stdout (`logger_attach_sink("stdout", LevelFilter_Info)`)
/// - stderr (`logger_attach_sink("stderr", LevelFilter_Debug)`)
/// - file w/ file path (`logger_attach_sink("file /some/file/path", LevelFilter_Trace)`)
/// - buffer (`logger_attach_sink("buffer", LevelFilter_Debug)`)
///
/// # Usage
///
/// ```c
/// int result = logger_attach_sink("file /some/file/path", LogLevel_Filter);
/// ```
///
/// # Error Handling
///
/// The return error codes are as follows:
///
/// - `-1`: Can't set logger (applying the logger failed, perhaps because one is applied already).
/// - `-2`: No logger has been initialized (call `logger_init` before any other log function).
/// - `-3`: The sink specifier was not UTF-8 encoded.
/// - `-4`: The sink type specified is not a known type (known types: "stdout", "stderr", or "file /some/path").
/// - `-5`: No file path was specified in a file-type sink specification.
/// - `-6`: Opening a sink to the specified file path failed (check permissions).
///
/// # Safety
///
/// This function checks the validity of the passed-in sink specifier, and errors
/// out if the specifier isn't valid UTF-8.
#[allow(clippy::missing_safety_doc)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn logger_attach_sink(
    sink_specifier: *const c_char,
    level_filter: LevelFilter,
) -> c_int {
    // Get the specifier from the raw C string.
    let sink_specifier = unsafe { CStr::from_ptr(sink_specifier) };
    let sink_specifier = match sink_specifier.to_str() {
        Ok(sink_specifier) => sink_specifier,
        // TODO: Permit non-UTF8 strings, as some filesystems may have non-UTF8
        //       paths to which the user wants to direct the logging output.
        Err(_) => return Status::SpecifierNotUtf8 as c_int,
    };

    // Attempt to construct a sink from the specifier.
    let sink = match Sink::try_from(sink_specifier) {
        Ok(sink) => sink,
        Err(err) => return Status::from(err) as c_int,
    };

    // Convert from our `#[repr(C)]` LevelFilter to the one from the `log` crate.
    let level_filter: LogLevelFilter = level_filter.into();

    // Construct a dispatcher from the sink and level filter.
    let dispatch = Into::<Dispatch>::into(sink)
        .level(level_filter)
        .format(|out, message, record| {
            out.finish(format_args!(
                "[{}][{}] {}",
                record.level(),
                record.target(),
                message
            ))
        });

    // Take the existing logger, if there is one, add a new sink to it, and put it back.
    let status = match add_sink(dispatch) {
        Ok(_) => Status::Success,
        Err(err) => Status::from(err),
    };

    status as c_int
}

/// Apply the thread-local logger to the program.
///
/// Any attempts to modify the logger after the call to `logger_apply` will fail.
#[no_mangle]
pub extern "C" fn logger_apply() -> c_int {
    let status = match apply_logger() {
        Ok(_) => Status::Success,
        Err(err) => Status::from(err),
    };

    status as c_int
}

/// Fetch the in-memory logger buffer contents. This will only have any contents if the `buffer`
/// sink has been configured to log to. The contents will be allocated on the heap and will need
/// to be freed with `string_delete`.
///
/// Returns a NULL pointer if the buffer can't be fetched. This can occur is there is not
/// sufficient memory to make a copy of the contents or the buffer contains non-UTF-8 characters.
#[no_mangle]
pub extern "C" fn fetch_memory_buffer() -> *const c_char {
   match from_utf8(&fetch_buffer_contents()) {
     Ok(contents) => match to_c(contents) {
       Ok(c_str) => c_str,
       Err(err) => {
         error!("Failed to copy in-memory log buffer - {}", err);
         ptr::null()
       }
     }
     Err(err) => {
       error!("Failed to convert in-memory log buffer to UTF-8 = {}", err);
       ptr::null()
     }
   }
}
<commit_msg>chore: fix clippy voilation<commit_after>//! The public FFI functions for initializing, adding sinks to, and applying a logger.

use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::ptr;
use std::str::from_utf8;

use fern::Dispatch;
use libc::{c_char, c_int};
use log::{error, LevelFilter as LogLevelFilter};

use crate::error::set_error_msg;
use crate::log::level_filter::LevelFilter;
use crate::log::logger::{add_sink, apply_logger, set_logger};
use crate::log::sink::Sink;
use crate::log::status::Status;
use crate::util::string::to_c;
use crate::log::inmem_buffer::fetch_buffer_contents;

/// Convenience function to direct all logging to stdout.
#[no_mangle]
pub extern "C" fn log_to_stdout(level_filter: LevelFilter) -> c_int {
    logger_init();

    let spec = match CString::new("stdout") {
        Ok(spec) => spec,
        Err(e) => {
            set_error_msg(e.to_string());
            return Status::CantConstructSink as c_int;
        }
    };

    let status = logger_attach_sink(spec.as_ptr(), level_filter);
    if status != 0 {
        return status;
    }

    let status = logger_apply();
    if status != 0 {
        return status;
    }

    Status::Success as c_int
}

/// Convenience function to direct all logging to stderr.
#[no_mangle]
pub extern "C" fn log_to_stderr(level_filter: LevelFilter) -> c_int {
    logger_init();

    let spec = match CString::new("stderr") {
        Ok(spec) => spec,
        Err(e) => {
            set_error_msg(e.to_string());
            return Status::CantConstructSink as c_int;
        }
    };

    let status = logger_attach_sink(spec.as_ptr(), level_filter);
    if status != 0 {
        return status;
    }

    let status = logger_apply();
    if status != 0 {
        return status;
    }

    Status::Success as c_int
}

/// Convenience function to direct all logging to a file.
#[no_mangle]
pub unsafe extern "C" fn log_to_file(
    file_name: *const c_char,
    level_filter: LevelFilter,
) -> c_int {
    logger_init();

    let spec = {
        if file_name.is_null() {
            return Status::CantConstructSink as c_int;
        }

        let file_name =
            match CStr::from_ptr(file_name).to_str() {
                Ok(file_name) => file_name,
                Err(e) => {
                    set_error_msg(e.to_string());
                    return Status::CantConstructSink as c_int;
                }
            };

        let spec = format!("file {}", file_name);

        match CString::new(spec) {
            Ok(spec) => spec,
            Err(e) => {
                set_error_msg(e.to_string());
                return Status::CantConstructSink as c_int;
            }
        }
    };

    let status = logger_attach_sink(spec.as_ptr(), level_filter);
    if status != 0 {
        return status;
    }

    let status = logger_apply();
    if status != 0 {
        return status;
    }

    Status::Success as c_int
}

/// Convenience function to direct all logging to a thread local memory buffer.
#[no_mangle]
pub extern "C" fn log_to_buffer(level_filter: LevelFilter) -> c_int {
  logger_init();

  let spec = match CString::new("buffer") {
    Ok(spec) => spec,
    Err(e) => {
      set_error_msg(e.to_string());
      return Status::CantConstructSink as c_int;
    }
  };

  let status = logger_attach_sink(spec.as_ptr(), level_filter);
  if status != 0 {
    return status;
  }

  let status = logger_apply();
  if status != 0 {
    return status;
  }

  Status::Success as c_int
}

// C API uses something like the pledge API to select write locations, including:
//
// * stdout (`logger_attach_sink("stdout", LevelFilter_Info)`)
// * stderr (`logger_attach_sink("stderr", LevelFilter_Debug)`)
// * file w/ file path (`logger_attach_sink("file /some/file/path", LevelFilter_Trace)`)
//
// The general flow is:
//
// 1. Call `logger_init` to create a `Dispatch` struct.
// 2. Call `logger_attach_sink` to add an additional sink, using bitflags to set the metadata.
// 3. Call `logger_apply` to finalize the logger and enable logging to the configured sinks.
//
// Once `logger_apply` has been called, any additional calls to `logger_attach_sink` will fail
// with an error indicating the logger has been applied already.
//
// ```
// logger_init();
//
// int result = logger_attach_sink("stderr", FilterLevel_Debug);
// /* handle the error */
//
// int result = logger_attach_sink("file /some/file/path", FilterLevel_Info);
// /* handle the error */
//
// int result = logger_apply();
// /* handle the error */
// ```

/// Initialize the thread-local logger with no sinks.
///
/// This initialized logger does nothing until `logger_apply` has been called.
///
/// # Usage
///
/// ```c
/// logger_init();
/// ```
///
/// # Safety
///
/// This function is always safe to call.
#[no_mangle]
pub extern "C" fn logger_init() {
    set_logger(Dispatch::new());
}

/// Attach an additional sink to the thread-local logger.
///
/// This logger does nothing until `logger_apply` has been called.
///
/// Three types of sinks can be specified:
///
/// - stdout (`logger_attach_sink("stdout", LevelFilter_Info)`)
/// - stderr (`logger_attach_sink("stderr", LevelFilter_Debug)`)
/// - file w/ file path (`logger_attach_sink("file /some/file/path", LevelFilter_Trace)`)
/// - buffer (`logger_attach_sink("buffer", LevelFilter_Debug)`)
///
/// # Usage
///
/// ```c
/// int result = logger_attach_sink("file /some/file/path", LogLevel_Filter);
/// ```
///
/// # Error Handling
///
/// The return error codes are as follows:
///
/// - `-1`: Can't set logger (applying the logger failed, perhaps because one is applied already).
/// - `-2`: No logger has been initialized (call `logger_init` before any other log function).
/// - `-3`: The sink specifier was not UTF-8 encoded.
/// - `-4`: The sink type specified is not a known type (known types: "stdout", "stderr", or "file /some/path").
/// - `-5`: No file path was specified in a file-type sink specification.
/// - `-6`: Opening a sink to the specified file path failed (check permissions).
///
/// # Safety
///
/// This function checks the validity of the passed-in sink specifier, and errors
/// out if the specifier isn't valid UTF-8.
#[allow(clippy::missing_safety_doc)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn logger_attach_sink(
    sink_specifier: *const c_char,
    level_filter: LevelFilter,
) -> c_int {
    // Get the specifier from the raw C string.
    let sink_specifier = unsafe { CStr::from_ptr(sink_specifier) };
    let sink_specifier = match sink_specifier.to_str() {
        Ok(sink_specifier) => sink_specifier,
        // TODO: Permit non-UTF8 strings, as some filesystems may have non-UTF8
        //       paths to which the user wants to direct the logging output.
        Err(_) => return Status::SpecifierNotUtf8 as c_int,
    };

    // Attempt to construct a sink from the specifier.
    let sink = match Sink::try_from(sink_specifier) {
        Ok(sink) => sink,
        Err(err) => return Status::from(err) as c_int,
    };

    // Convert from our `#[repr(C)]` LevelFilter to the one from the `log` crate.
    let level_filter: LogLevelFilter = level_filter.into();

    // Construct a dispatcher from the sink and level filter.
    let dispatch = Into::<Dispatch>::into(sink)
        .level(level_filter)
        .format(|out, message, record| {
            out.finish(format_args!(
                "[{}][{}] {}",
                record.level(),
                record.target(),
                message
            ))
        });

    // Take the existing logger, if there is one, add a new sink to it, and put it back.
    let status = match add_sink(dispatch) {
        Ok(_) => Status::Success,
        Err(err) => Status::from(err),
    };

    status as c_int
}

/// Apply the thread-local logger to the program.
///
/// Any attempts to modify the logger after the call to `logger_apply` will fail.
#[no_mangle]
pub extern "C" fn logger_apply() -> c_int {
    let status = match apply_logger() {
        Ok(_) => Status::Success,
        Err(err) => Status::from(err),
    };

    status as c_int
}

/// Fetch the in-memory logger buffer contents. This will only have any contents if the `buffer`
/// sink has been configured to log to. The contents will be allocated on the heap and will need
/// to be freed with `string_delete`.
///
/// Returns a NULL pointer if the buffer can't be fetched. This can occur is there is not
/// sufficient memory to make a copy of the contents or the buffer contains non-UTF-8 characters.
#[no_mangle]
pub extern "C" fn fetch_memory_buffer() -> *const c_char {
   match from_utf8(&fetch_buffer_contents()) {
     Ok(contents) => match to_c(contents) {
       Ok(c_str) => c_str,
       Err(err) => {
         error!("Failed to copy in-memory log buffer - {}", err);
         ptr::null()
       }
     }
     Err(err) => {
       error!("Failed to convert in-memory log buffer to UTF-8 = {}", err);
       ptr::null()
     }
   }
}
<|endoftext|>


<commit_before>#[lang="panic_fmt"]
#[no_mangle]
pub extern "C" fn rust_begin_unwind(args: ::core::fmt::Arguments, file: &str, line: usize) -> !
{
	// 'args' will print to the formatted string passed to panic!
	// log!("file='{}', line={} :: {}", file, line, args);
	loop {}
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
pub enum _Unwind_Reason_Code
{
	_URC_NO_REASON = 0,
	_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
	_URC_FATAL_PHASE2_ERROR = 2,
	_URC_FATAL_PHASE1_ERROR = 3,
	_URC_NORMAL_STOP = 4,
	_URC_END_OF_STACK = 5,
	_URC_HANDLER_FOUND = 6,
	_URC_INSTALL_CONTEXT = 7,
	_URC_CONTINUE_UNWIND = 8,
}

#[allow(non_camel_case_types)]
#[derive(Clone,Copy)]
pub struct _Unwind_Context;

#[allow(non_camel_case_types)]
pub type _Unwind_Action = u32;
static _UA_SEARCH_PHASE: _Unwind_Action = 1;

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
pub struct _Unwind_Exception
{
	exception_class: u64,
	exception_cleanup: fn(_Unwind_Reason_Code,*const _Unwind_Exception),
	private: [u64; 2],
}

#[lang="eh_personality"]
#[no_mangle]
pub fn rust_eh_personality(
	_version: isize, _actions: _Unwind_Action, _exception_class: u64,
	_exception_object: &_Unwind_Exception, _context: &_Unwind_Context
	) -> _Unwind_Reason_Code
{
	loop{}
}

#[no_mangle]
#[allow(non_snake_case)]
pub fn _Unwind_Resume()
{
	loop{}
}
<commit_msg>Unwind to print error<commit_after>
#[lang="panic_fmt"]
#[no_mangle]
pub extern "C" fn rust_begin_unwind(args: ::core::fmt::Arguments, file: &str, line: usize) -> !
{
    // 'args' will print to the formatted string passed to panic!
    use core::fmt::Write;
    let _ = write!(&mut ::PrintWriter::new(), "file='{}', line={} :: {}", file, line, args);
    loop {}
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
pub enum _Unwind_Reason_Code
{
	_URC_NO_REASON = 0,
	_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
	_URC_FATAL_PHASE2_ERROR = 2,
	_URC_FATAL_PHASE1_ERROR = 3,
	_URC_NORMAL_STOP = 4,
	_URC_END_OF_STACK = 5,
	_URC_HANDLER_FOUND = 6,
	_URC_INSTALL_CONTEXT = 7,
	_URC_CONTINUE_UNWIND = 8,
}

#[allow(non_camel_case_types)]
#[derive(Clone,Copy)]
pub struct _Unwind_Context;

#[allow(non_camel_case_types)]
pub type _Unwind_Action = u32;
static _UA_SEARCH_PHASE: _Unwind_Action = 1;

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone,Copy)]
pub struct _Unwind_Exception
{
	exception_class: u64,
	exception_cleanup: fn(_Unwind_Reason_Code,*const _Unwind_Exception),
	private: [u64; 2],
}

#[lang="eh_personality"]
#[no_mangle]
pub fn rust_eh_personality(
	_version: isize, _actions: _Unwind_Action, _exception_class: u64,
	_exception_object: &_Unwind_Exception, _context: &_Unwind_Context
	) -> _Unwind_Reason_Code
{
	loop{}
}

#[no_mangle]
#[allow(non_snake_case)]
pub fn _Unwind_Resume()
{
	loop{}
}
<|endoftext|>


<commit_before>extern crate config;

use self::config::{Config, File};

// Creates the default configuration for an application with data,
// read from file.
pub fn get_config(file_path: &str) -> Box<Config> {
    let mut conf = Box::new(Config::default());

    if file_path != "" {
        conf.merge(File::with_name(file_path))
            .map_err(|err|
                println!("Error during reading file: {}. \
                          Changes won't applied.", err)
            )
            .and_then(|new_config| Ok(new_config))
            .is_ok();
    }

    conf
}

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

    #[test]
    fn test_get_config_returns_a_new_config_by_default() {
        let conf = get_config(&"");
        assert_eq!(format!("{}", conf.cache), "nil");
    }

    #[test]
    fn test_get_config_returns_a_new_config_with_values_from_file() {
        let conf = get_config(&"./tests/files/valid_file.yaml");

        let data = conf.cache.into_table();
        assert_eq!(data.is_ok(), true);

        let table = data.unwrap();
        assert_eq!(table.contains_key("foo"), true);

        let foo_array = table["foo"].clone().into_array().unwrap();
        assert_eq!(foo_array.len(), 1);
        assert_eq!(foo_array[0].clone().into_str().unwrap(), "bar");
    }
}
<commit_msg>Added documentation for config.rs<commit_after>//! Wrappers for a work with application configuration
//!

extern crate config;

use self::config::{Config, File};

/// Returns a configuration for the application with data that was
/// read from a file. When specified an empty string, returns a
/// default configuration.
pub fn get_config(file_path: &str) -> Box<Config> {
    let mut conf = Box::new(Config::default());

    if file_path != "" {
        conf.merge(File::with_name(file_path))
            .map_err(|err|
                println!("Error during reading file: {}. \
                          Changes won't applied.", err)
            )
            .and_then(|new_config| Ok(new_config))
            .is_ok();
    }

    conf
}

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

    #[test]
    fn test_get_config_returns_a_new_config_by_default() {
        let conf = get_config(&"");
        assert_eq!(format!("{}", conf.cache), "nil");
    }

    #[test]
    fn test_get_config_returns_a_new_config_with_values_from_file() {
        let conf = get_config(&"./tests/files/valid_file.yaml");

        let data = conf.cache.into_table();
        assert_eq!(data.is_ok(), true);

        let table = data.unwrap();
        assert_eq!(table.contains_key("foo"), true);

        let foo_array = table["foo"].clone().into_array().unwrap();
        assert_eq!(foo_array.len(), 1);
        assert_eq!(foo_array[0].clone().into_str().unwrap(), "bar");
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
extern crate assert_cli;
extern crate boxcars;
extern crate failure;
extern crate globset;
extern crate rayon;
extern crate serde_json;
#[macro_use]
extern crate structopt;

use failure::{err_msg, Error, ResultExt};
use structopt::StructOpt;
use std::fs::{File, OpenOptions};
use std::io::{self, BufWriter};
use std::io::prelude::*;
use rayon::prelude::*;
use boxcars::{CrcCheck, NetworkParse, ParserBuilder, Replay};
use std::path::Path;
use globset::Glob;

#[derive(StructOpt, Debug, Clone, PartialEq)]
#[structopt(name = "rrrocket",
            about = "Parses Rocket League replay files and outputs JSON with decoded information")]
struct Opt {
    #[structopt(short = "c", long = "crc-check", help = "forces a crc check for corruption even when replay was successfully parsed")]
    crc: bool,

    #[structopt(short = "n", long = "network-parse", help = "parses the network data of a replay instead of skipping it")]
    body: bool,

    #[structopt(short = "m", long = "multiple", help = "parse multiple replays, instead of writing JSON to stdout, write to a sibling JSON file")]
    multiple: bool,

    #[structopt(long = "dry-run", help = "parses but does not write JSON output")]
    dry_run: bool,

    #[structopt(help = "Rocket League replay files")] input: Vec<String>,
}

fn read_file(input: &str) -> Result<Vec<u8>, Error> {
    let mut f = File::open(input)
        .with_context(|_e| format!("Could not open rocket league file: {}", input))?;
    let mut buffer = vec![];
    f.read_to_end(&mut buffer)
        .with_context(|_e| format!("Could not read rocket league file: {}", input))?;
    Ok(buffer)
}

fn parse_replay<'a>(opt: &Opt, data: &'a [u8]) -> Result<Replay<'a>, Error> {
    ParserBuilder::new(&data[..])
        .with_crc_check(if opt.crc {
            CrcCheck::Always
        } else {
            CrcCheck::OnError
        })
        .with_network_parse(if opt.body {
            NetworkParse::Always
        } else {
            NetworkParse::Never
        })
        .parse()
}

/// Each file argument that we get could be a directory so we need to expand that directory and
/// find all the *.replay files. File arguments turn into single element vectors.
fn expand_paths(files: &[String]) -> Result<Vec<Vec<String>>, Error> {
    let glob = Glob::new("*.replay")?.compile_matcher();

    files
        .iter()
        .map(|x| {
            let p = Path::new(x);
            if p.is_dir() {
                // If the commandline argument is a directory we look for all files that match
                // *.replay. A file that does not match the pattern because of an error reading the
                // directory / file will not be filtered and will cause the error to bubble up. In
                // the future, we could get smart and ignore directories / files we don't have
                // permission that wouldn't match the pattern anyways
                let files: Result<Vec<_>, _> = p.read_dir()?
                    .filter_map(|entry| {
                        match entry {
                            Ok(y) => {
                                if glob.is_match(y.path()) {
                                    // Force UTF-8. There is a special place in the fourth circle
                                    // of inferno for people who rename their rocket league replays
                                    // to not contain UTF-8. We won't panic, but will cause an
                                    // error when the file is attempted to be read.
                                    Some(Ok(y.path().to_string_lossy().into_owned()))
                                } else {
                                    None
                                }
                            }
                            Err(e) => Some(Err(e)),
                        }
                    })
                    .collect();
                Ok(files?)
            } else {
                Ok(vec![x.clone()])
            }
        })
        .collect()
}

fn parse_multiple_replays(opt: &Opt) -> Result<(), Error> {
    let res: Result<Vec<()>, Error> = expand_paths(&opt.input)?
        .into_iter()
        .flat_map(|x| x)
        .collect::<Vec<_>>()
        .par_iter()
        .map(|file| {
            let data = read_file(file)?;
            let replay = parse_replay(opt, &data[..])
                .with_context(|_e| format!("Could not parse: {}", file))?;

            if !opt.dry_run {
                let outfile = format!("{}.json", file);
                let fout = OpenOptions::new()
                    .write(true)
                    .create(true)
                    .truncate(true)
                    .open(&outfile)
                    .with_context(|_e| format!("Could not open json output file: {}", outfile))?;

                let mut writer = BufWriter::new(fout);
                serde_json::to_writer(&mut writer, &replay)
                    .with_context(|_e| format!("Could not serialize replay {}", file))?;
            }
            Ok(())
        })
        .collect();
    res?;
    Ok(())
}

fn run() -> Result<(), Error> {
    let opt = Opt::from_args();
    if opt.multiple {
        parse_multiple_replays(&opt)
    } else if opt.input.len() != 1 {
        Err(err_msg(
            "Expected one input file if --multiple is not specified",
        ))
    } else {
        let file = &opt.input[0];
        let data = read_file(file)?;
        let replay = parse_replay(&opt, &data[..]).context("Could not parse replay")?;
        if !opt.dry_run {
            serde_json::to_writer(&mut io::stdout(), &replay)
                .context("Could not serialize replay")?;
        }
        Ok(())
    }
}

fn main() {
    if let Err(ref e) = run() {
        let mut stderr = io::stderr();
        for fail in e.causes() {
            let _ = writeln!(stderr, "{}", fail);
        }

        ::std::process::exit(1);
    }
}

#[cfg(test)]
mod tests {
    use assert_cli::Assert;
    use std::io::Write;

    #[test]
    fn test_error_output() {
        let mut w = Vec::new();
        writeln!(
            &mut w,
            "Could not open rocket league file: assets/fuzz-string-too-long.replay"
        ).unwrap();

        Assert::cargo_binary("rrrocket")
            .with_args(&[
                "-n",
                "-c",
                "--dry-run",
                "assets/fuzz-string-too-long.replay",
            ])
            .fails_with(1)
            .stderr()
            .contains(String::from_utf8(w).unwrap())
            .unwrap();
    }

    #[test]
    fn test_error_output2() {
        let mut w = Vec::new();
        writeln!(
            &mut w,
            "Could not parse: ../assets/fuzz-string-too-long.replay"
        ).unwrap();
        writeln!(
            &mut w,
            "Crc mismatch. Expected 3765941959 but received 1825689991"
        ).unwrap();

        Assert::cargo_binary("rrrocket")
            .with_args(&[
                "-n",
                "-c",
                "--dry-run",
                "-m",
                "../assets/fuzz-string-too-long.replay",
            ])
            .fails_with(1)
            .stderr()
            .contains(String::from_utf8(w).unwrap())
            .unwrap();
    }
}
<commit_msg>Rustfmt rrrocket<commit_after>#[cfg(test)]
extern crate assert_cli;
extern crate boxcars;
extern crate failure;
extern crate globset;
extern crate rayon;
extern crate serde_json;
#[macro_use]
extern crate structopt;

use boxcars::{CrcCheck, NetworkParse, ParserBuilder, Replay};
use failure::{err_msg, Error, ResultExt};
use globset::Glob;
use rayon::prelude::*;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::path::Path;
use structopt::StructOpt;

#[derive(StructOpt, Debug, Clone, PartialEq)]
#[structopt(name = "rrrocket",
            about = "Parses Rocket League replay files and outputs JSON with decoded information")]
struct Opt {
    #[structopt(short = "c", long = "crc-check",
                help = "forces a crc check for corruption even when replay was successfully parsed")]
    crc: bool,

    #[structopt(short = "n", long = "network-parse",
                help = "parses the network data of a replay instead of skipping it")]
    body: bool,

    #[structopt(short = "m", long = "multiple",
                help = "parse multiple replays, instead of writing JSON to stdout, write to a sibling JSON file")]
    multiple: bool,

    #[structopt(long = "dry-run", help = "parses but does not write JSON output")]
    dry_run: bool,

    #[structopt(help = "Rocket League replay files")]
    input: Vec<String>,
}

fn read_file(input: &str) -> Result<Vec<u8>, Error> {
    let mut f = File::open(input)
        .with_context(|_e| format!("Could not open rocket league file: {}", input))?;
    let mut buffer = vec![];
    f.read_to_end(&mut buffer)
        .with_context(|_e| format!("Could not read rocket league file: {}", input))?;
    Ok(buffer)
}

fn parse_replay<'a>(opt: &Opt, data: &'a [u8]) -> Result<Replay<'a>, Error> {
    ParserBuilder::new(&data[..])
        .with_crc_check(if opt.crc {
            CrcCheck::Always
        } else {
            CrcCheck::OnError
        })
        .with_network_parse(if opt.body {
            NetworkParse::Always
        } else {
            NetworkParse::Never
        })
        .parse()
}

/// Each file argument that we get could be a directory so we need to expand that directory and
/// find all the *.replay files. File arguments turn into single element vectors.
fn expand_paths(files: &[String]) -> Result<Vec<Vec<String>>, Error> {
    let glob = Glob::new("*.replay")?.compile_matcher();

    files
        .iter()
        .map(|x| {
            let p = Path::new(x);
            if p.is_dir() {
                // If the commandline argument is a directory we look for all files that match
                // *.replay. A file that does not match the pattern because of an error reading the
                // directory / file will not be filtered and will cause the error to bubble up. In
                // the future, we could get smart and ignore directories / files we don't have
                // permission that wouldn't match the pattern anyways
                let files: Result<Vec<_>, _> = p.read_dir()?
                    .filter_map(|entry| {
                        match entry {
                            Ok(y) => {
                                if glob.is_match(y.path()) {
                                    // Force UTF-8. There is a special place in the fourth circle
                                    // of inferno for people who rename their rocket league replays
                                    // to not contain UTF-8. We won't panic, but will cause an
                                    // error when the file is attempted to be read.
                                    Some(Ok(y.path().to_string_lossy().into_owned()))
                                } else {
                                    None
                                }
                            }
                            Err(e) => Some(Err(e)),
                        }
                    })
                    .collect();
                Ok(files?)
            } else {
                Ok(vec![x.clone()])
            }
        })
        .collect()
}

fn parse_multiple_replays(opt: &Opt) -> Result<(), Error> {
    let res: Result<Vec<()>, Error> = expand_paths(&opt.input)?
        .into_iter()
        .flat_map(|x| x)
        .collect::<Vec<_>>()
        .par_iter()
        .map(|file| {
            let data = read_file(file)?;
            let replay = parse_replay(opt, &data[..])
                .with_context(|_e| format!("Could not parse: {}", file))?;

            if !opt.dry_run {
                let outfile = format!("{}.json", file);
                let fout = OpenOptions::new()
                    .write(true)
                    .create(true)
                    .truncate(true)
                    .open(&outfile)
                    .with_context(|_e| format!("Could not open json output file: {}", outfile))?;

                let mut writer = BufWriter::new(fout);
                serde_json::to_writer(&mut writer, &replay)
                    .with_context(|_e| format!("Could not serialize replay {}", file))?;
            }
            Ok(())
        })
        .collect();
    res?;
    Ok(())
}

fn run() -> Result<(), Error> {
    let opt = Opt::from_args();
    if opt.multiple {
        parse_multiple_replays(&opt)
    } else if opt.input.len() != 1 {
        Err(err_msg(
            "Expected one input file if --multiple is not specified",
        ))
    } else {
        let file = &opt.input[0];
        let data = read_file(file)?;
        let replay = parse_replay(&opt, &data[..]).context("Could not parse replay")?;
        if !opt.dry_run {
            serde_json::to_writer(&mut io::stdout(), &replay)
                .context("Could not serialize replay")?;
        }
        Ok(())
    }
}

fn main() {
    if let Err(ref e) = run() {
        let mut stderr = io::stderr();
        for fail in e.causes() {
            let _ = writeln!(stderr, "{}", fail);
        }

        ::std::process::exit(1);
    }
}

#[cfg(test)]
mod tests {
    use assert_cli::Assert;
    use std::io::Write;

    #[test]
    fn test_error_output() {
        let mut w = Vec::new();
        writeln!(
            &mut w,
            "Could not open rocket league file: assets/fuzz-string-too-long.replay"
        ).unwrap();

        Assert::cargo_binary("rrrocket")
            .with_args(&[
                "-n",
                "-c",
                "--dry-run",
                "assets/fuzz-string-too-long.replay",
            ])
            .fails_with(1)
            .stderr()
            .contains(String::from_utf8(w).unwrap())
            .unwrap();
    }

    #[test]
    fn test_error_output2() {
        let mut w = Vec::new();
        writeln!(
            &mut w,
            "Could not parse: ../assets/fuzz-string-too-long.replay"
        ).unwrap();
        writeln!(
            &mut w,
            "Crc mismatch. Expected 3765941959 but received 1825689991"
        ).unwrap();

        Assert::cargo_binary("rrrocket")
            .with_args(&[
                "-n",
                "-c",
                "--dry-run",
                "-m",
                "../assets/fuzz-string-too-long.replay",
            ])
            .fails_with(1)
            .stderr()
            .contains(String::from_utf8(w).unwrap())
            .unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::types::{
    flags::*,
    generation::*,
    parsing::{traits::ParsableInput, *},
    *,
};
use nom::{
    combinator::{flat_map, map, map_opt},
    error::context,
};
use std::{convert::TryFrom, error, fmt, io::Write};

include!(concat!(env!("OUT_DIR"), "/protocol.rs"));

/// Type alias for AMQP BasicProperties
pub type BasicProperties = basic::AMQPProperties;

/// An AMQP Error
#[derive(Clone, Debug, PartialEq)]
pub struct AMQPError {
    kind: AMQPErrorKind,
    message: ShortString,
}

impl AMQPError {
    /// Create a new error
    pub fn new(kind: AMQPErrorKind, message: ShortString) -> Self {
        Self { kind, message }
    }

    /// Get the error corresponding to an id
    pub fn from_id(id: ShortUInt, message: ShortString) -> Option<Self> {
        AMQPErrorKind::from_id(id).map(|kind| Self { kind, message })
    }

    /// Get the kind of error
    pub fn kind(&self) -> &AMQPErrorKind {
        &self.kind
    }
}

impl TryFrom<channel::Close> for AMQPError {
    type Error = String;

    fn try_from(method: channel::Close) -> Result<Self, Self::Error> {
        Self::from_id(method.reply_code, method.reply_text.clone()).ok_or_else(|| format!("Couldn't convert method to error: {:?}", method))
    }
}

impl TryFrom<connection::Close> for AMQPError {
    type Error = String;

    fn try_from(method: connection::Close) -> Result<Self, Self::Error> {
        Self::from_id(method.reply_code, method.reply_text.clone()).ok_or_else(|| format!("Couldn't convert method to error: {:?}", method))
    }
}

impl fmt::Display for AMQPError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}: {}", self.kind, self.message)
    }
}

impl error::Error for AMQPError {}

/// The kind of AMQP Error
#[derive(Clone, Debug, PartialEq)]
pub enum AMQPErrorKind {
    /// A soft AMQP error
    Soft(AMQPSoftError),
    /// A hard AMQP error
    Hard(AMQPHardError),
}

impl AMQPErrorKind {
    /// Get the id of the error
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPErrorKind::Soft(ref s) => s.get_id(),
            AMQPErrorKind::Hard(ref h) => h.get_id(),
        }
    }

    /// Get the error kind corresponding to an id
    pub fn from_id(id: ShortUInt) -> Option<Self> {
        AMQPSoftError::from_id(id).map(AMQPErrorKind::Soft).or_else(|| AMQPHardError::from_id(id).map(AMQPErrorKind::Hard))
    }
}

impl fmt::Display for AMQPErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AMQPErrorKind::Soft(err) => write!(f, "AMQP soft error: {}", err),
            AMQPErrorKind::Hard(err) => write!(f, "AMQP hard error: {}", err),
        }
    }
}

impl From<AMQPSoftError> for AMQPErrorKind {
    fn from(error: AMQPSoftError) -> Self {
        Self::Soft(error)
    }
}

impl From<AMQPHardError> for AMQPErrorKind {
    fn from(error: AMQPHardError) -> Self {
        Self::Hard(error)
    }
}

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

    #[test]
    fn test_description() {
        assert_eq!(
            format!(
                "{} - {}.{}.{}",
                metadata::NAME,
                metadata::MAJOR_VERSION,
                metadata::MINOR_VERSION,
                metadata::REVISION
            ),
            "AMQP - 0.9.1"
        );
    }
}
<commit_msg>add AMQPError dector<commit_after>use crate::types::{
    flags::*,
    generation::*,
    parsing::{traits::ParsableInput, *},
    *,
};
use nom::{
    combinator::{flat_map, map, map_opt},
    error::context,
};
use std::{convert::TryFrom, error, fmt, io::Write};

include!(concat!(env!("OUT_DIR"), "/protocol.rs"));

/// Type alias for AMQP BasicProperties
pub type BasicProperties = basic::AMQPProperties;

/// An AMQP Error
#[derive(Clone, Debug, PartialEq)]
pub struct AMQPError {
    kind: AMQPErrorKind,
    message: ShortString,
}

impl AMQPError {
    /// Create a new error
    pub fn new(kind: AMQPErrorKind, message: ShortString) -> Self {
        Self { kind, message }
    }

    /// Get the error corresponding to an id
    pub fn from_id(id: ShortUInt, message: ShortString) -> Option<Self> {
        AMQPErrorKind::from_id(id).map(|kind| Self { kind, message })
    }

    /// Get the kind of error
    pub fn kind(&self) -> &AMQPErrorKind {
        &self.kind
    }

    /// Get the id of the error
    pub fn get_id(&self) -> ShortUInt {
        self.kind.get_id()
    }

    /// Get the message of the error
    pub fn get_message(&self) -> &ShortString {
        &self.message
    }
}

impl TryFrom<channel::Close> for AMQPError {
    type Error = String;

    fn try_from(method: channel::Close) -> Result<Self, Self::Error> {
        Self::from_id(method.reply_code, method.reply_text.clone())
            .ok_or_else(|| format!("Couldn't convert method to error: {:?}", method))
    }
}

impl TryFrom<connection::Close> for AMQPError {
    type Error = String;

    fn try_from(method: connection::Close) -> Result<Self, Self::Error> {
        Self::from_id(method.reply_code, method.reply_text.clone())
            .ok_or_else(|| format!("Couldn't convert method to error: {:?}", method))
    }
}

impl fmt::Display for AMQPError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}: {}", self.kind, self.message)
    }
}

impl error::Error for AMQPError {}

/// The kind of AMQP Error
#[derive(Clone, Debug, PartialEq)]
pub enum AMQPErrorKind {
    /// A soft AMQP error
    Soft(AMQPSoftError),
    /// A hard AMQP error
    Hard(AMQPHardError),
}

impl AMQPErrorKind {
    /// Get the id of the error
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPErrorKind::Soft(ref s) => s.get_id(),
            AMQPErrorKind::Hard(ref h) => h.get_id(),
        }
    }

    /// Get the error kind corresponding to an id
    pub fn from_id(id: ShortUInt) -> Option<Self> {
        AMQPSoftError::from_id(id)
            .map(AMQPErrorKind::Soft)
            .or_else(|| AMQPHardError::from_id(id).map(AMQPErrorKind::Hard))
    }
}

impl fmt::Display for AMQPErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AMQPErrorKind::Soft(err) => write!(f, "AMQP soft error: {}", err),
            AMQPErrorKind::Hard(err) => write!(f, "AMQP hard error: {}", err),
        }
    }
}

impl From<AMQPSoftError> for AMQPErrorKind {
    fn from(error: AMQPSoftError) -> Self {
        Self::Soft(error)
    }
}

impl From<AMQPHardError> for AMQPErrorKind {
    fn from(error: AMQPHardError) -> Self {
        Self::Hard(error)
    }
}

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

    #[test]
    fn test_description() {
        assert_eq!(
            format!(
                "{} - {}.{}.{}",
                metadata::NAME,
                metadata::MAJOR_VERSION,
                metadata::MINOR_VERSION,
                metadata::REVISION
            ),
            "AMQP - 0.9.1"
        );
    }
}
<|endoftext|>


<commit_before>use std::env;
use version_check as rustc;

const MSRV: &str = "1.32.0";
const NO_STD_MSRV: &str = "1.36.0";

macro_rules! cfg_emit {
    ($s:ident) => {
        println!(concat!("cargo:rustc-cfg=", stringify!($s)));
    };
}

macro_rules! warning {
    ($($s:tt)*) => {
        println!("cargo:warning={}", format_args!($($s)*));
    };
}

fn main() {
    println!("cargo:rerun-if-env-changed=COMPILING_UNDER_CARGO_WEB");

    // Are we compiling with `cargo web`?
    if env::var("COMPILING_UNDER_CARGO_WEB") == Ok("1".into()) {
        cfg_emit!(__time_02_cargo_web);
    }

    // Warn if the version is below MSRV.
    if !rustc::is_min_version(MSRV).unwrap_or(false) {
        warning!(
            "The time crate has a minimum supported rust version of {}.",
            MSRV
        );
    }

    // Warn if the version is below `#![no_std]` MSRV.
    if !rustc::is_min_version(NO_STD_MSRV).unwrap_or(false) {
        #[cfg(not(feature = "std"))]
        warning!(
            "Using the time crate without the standard library enabled requires a global \
             allocator. This was stabilized in Rust {}. You can either upgrade or enable the \
             standard library.",
            NO_STD_MSRV
        );
    }

    // Warn if the `__doc` feature is used on stable or beta.
    if !rustc::Channel::read().map_or(false, |channel| channel.supports_features()) {
        #[cfg(__time_02_docs)]
        warning!(
            "`--cfg __time_02_docs` requires a nightly compiler, and is intended for internal \
             usage only."
        );
    }

    // ==== features that affect runtime directly ====

    // `#[non_exhaustive]` was stabilized in 1.40.0.
    if rustc::is_min_version("1.40.0").unwrap_or(false) {
        cfg_emit!(__time_02_supports_non_exhaustive);
    }

    // `Instant::checked_add` and `Instant::checked_sub` were added in 1.34.0.
    // `NonZeroI*` was stabilized in 1.34.0.
    if rustc::is_min_version("1.34.0").unwrap_or(false) {
        cfg_emit!(__time_02_instant_checked_ops);
        cfg_emit!(__time_02_nonzero_signed);
    }

    // `use <trait> as _;` was stabilized in 1.33.0.
    if rustc::is_min_version("1.33.0").unwrap_or(false) {
        cfg_emit!(__time_02_use_trait_as_underscore);
    }
}
<commit_msg>Better support for nightly<commit_after>use std::env;
use version_check as rustc;

const MSRV: u16 = 32;
const NO_STD_MSRV: u16 = 36;

macro_rules! cfg_emit {
    ($s:ident) => {
        println!(concat!("cargo:rustc-cfg=", stringify!($s)));
    };
}

macro_rules! warning {
    ($($s:tt)*) => {
        println!("cargo:warning={}", format_args!($($s)*));
    };
}

fn main() {
    println!("cargo:rerun-if-env-changed=COMPILING_UNDER_CARGO_WEB");

    // Are we compiling with `cargo web`?
    if env::var("COMPILING_UNDER_CARGO_WEB") == Ok("1".into()) {
        cfg_emit!(__time_02_cargo_web);
    }

    // Treat nightly & dev compilers as the equivalent of the then-beta.
    // As features are never stabilized in patch versions, we can safely ignore it.
    let (effective_compiler_version, channel) = match rustc::triple() {
        Some((version, channel, _)) if channel.is_dev() => (version.to_mmp().1 - 2, channel),
        Some((version, channel, _)) if channel.is_nightly() => (version.to_mmp().1 - 1, channel),
        Some((version, channel, _)) => (version.to_mmp().1, channel),
        None => {
            warning!(
                "Unable to determine rustc version. Assuming rustc 1.{}.0.",
                MSRV
            );
            (
                MSRV,
                rustc::Channel::parse("1.0.0").expect("This is a generic stable version."),
            )
        }
    };

    // Warn if the version is below MSRV.
    if effective_compiler_version < MSRV {
        warning!(
            "The time crate has a minimum supported rust version of {}.",
            MSRV
        );
    }

    // Warn if the version is below `#![no_std]` MSRV.
    if effective_compiler_version < NO_STD_MSRV {
        #[cfg(not(feature = "std"))]
        warning!(
            "Using the time crate without the standard library enabled requires a global \
             allocator. This was stabilized in Rust {}. You can either upgrade or enable the \
             standard library.",
            NO_STD_MSRV
        );
    }

    // Warn if the `__doc` feature is used on stable or beta.
    if !channel.supports_features() {
        #[cfg(__time_02_docs)]
        warning!(
            "`--cfg __time_02_docs` requires a nightly compiler, and is intended for internal \
             usage only."
        );
    }

    // ==== features that affect runtime directly ====

    // `#[non_exhaustive]` was stabilized in 1.40.0.
    if effective_compiler_version >= 40 {
        cfg_emit!(__time_02_supports_non_exhaustive);
    }

    // `Instant::checked_add` and `Instant::checked_sub` were added in 1.34.0.
    // `NonZeroI*` was stabilized in 1.34.0.
    if effective_compiler_version >= 34 {
        cfg_emit!(__time_02_instant_checked_ops);
        cfg_emit!(__time_02_nonzero_signed);
    }

    // `use <trait> as _;` was stabilized in 1.33.0.
    if effective_compiler_version >= 33 {
        cfg_emit!(__time_02_use_trait_as_underscore);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::{PathBuf, Path};

fn main() {
    let target = env::var("TARGET").unwrap();
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let mut lib_dir = manifest_dir.clone();
    let mut dll_dir = manifest_dir.clone();
    if target == "x86_64-pc-windows-msvc" {
        lib_dir.push("msvc");
        lib_dir.push("lib");
        lib_dir.push("64");
        dll_dir.push("msvc");
        dll_dir.push("dll");
        dll_dir.push("64");
    }
    else if target == "x86_64-pc-windows-gnu" {
        lib_dir.push("gnu");
        lib_dir.push("lib");
        lib_dir.push("64");
        dll_dir.push("gnu");
        dll_dir.push("dll");
        dll_dir.push("64");
    }
    else if target == "i686-pc-windows-msvc" {
        lib_dir.push("msvc");
        lib_dir.push("lib");
        lib_dir.push("32");
        dll_dir.push("msvc");
        dll_dir.push("dll");
        dll_dir.push("32");
    }
    else if target == "i686-pc-windows-gnu" {
        lib_dir.push("gnu");
        lib_dir.push("lib");
        lib_dir.push("32");
        dll_dir.push("gnu");
        dll_dir.push("dll");
        dll_dir.push("32");
    }
    println!("cargo:rustc-link-search=static={}", lib_dir.display());
    for entry in std::fs::read_dir(dll_dir).expect("Can't read DLL dir")  {
        let entry_path = entry.expect("Invalid fs entry").path();
        let file_name_result = entry_path.file_name();
        let mut new_file_path = manifest_dir.clone();
        if let Some(file_name) = file_name_result {
            new_file_path.push(file_name.to_str().unwrap());
            std::fs::copy(&entry_path, new_file_path.as_path()).expect("Can't copy from DLL dir");
        }
    }
}
<commit_msg>I broke other platforms with last commit. Fixed.<commit_after>use std::env;
use std::path::{PathBuf, Path};

fn main() {
    let target = env::var("TARGET").unwrap();
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let mut lib_dir = manifest_dir.clone();
    let mut dll_dir = manifest_dir.clone();
    if target == "x86_64-pc-windows-msvc" {
        lib_dir.push("msvc");
        lib_dir.push("lib");
        lib_dir.push("64");
        dll_dir.push("msvc");
        dll_dir.push("dll");
        dll_dir.push("64");
    }
    else if target == "x86_64-pc-windows-gnu" {
        lib_dir.push("gnu");
        lib_dir.push("lib");
        lib_dir.push("64");
        dll_dir.push("gnu");
        dll_dir.push("dll");
        dll_dir.push("64");
    }
    else if target == "i686-pc-windows-msvc" {
        lib_dir.push("msvc");
        lib_dir.push("lib");
        lib_dir.push("32");
        dll_dir.push("msvc");
        dll_dir.push("dll");
        dll_dir.push("32");
    }
    else if target == "i686-pc-windows-gnu" {
        lib_dir.push("gnu");
        lib_dir.push("lib");
        lib_dir.push("32");
        dll_dir.push("gnu");
        dll_dir.push("dll");
        dll_dir.push("32");
    }
    if target.contains("pc-windows") {
        println!("cargo:rustc-link-search=static={}", lib_dir.display());
        for entry in std::fs::read_dir(dll_dir).expect("Can't read DLL dir")  {
            let entry_path = entry.expect("Invalid fs entry").path();
            let file_name_result = entry_path.file_name();
            let mut new_file_path = manifest_dir.clone();
            if let Some(file_name) = file_name_result {
                new_file_path.push(file_name.to_str().unwrap());
                std::fs::copy(&entry_path, new_file_path.as_path()).expect("Can't copy from DLL dir");
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Command;
use std::io::fs::PathExtensions;
use std::io::fs;
use std::os;

fn main() {
  let out_dir = os::getenv("OUT_DIR").unwrap();
  let mut objects = Vec::new();

  let files = fs::readdir(&Path::new("src")).unwrap();
  let mut files = files.iter().filter(|p| p.is_file());

  for file in files {
    if let Some(filename) = file.filename_str() {
      let filepath = format!("src/{}", filename);
      let outpath;

      if let Some(basename) = eat_extension(filename, ".c") {
        outpath = format!("{}/{}.o", out_dir, basename);

        Command::new("cc").args(&[filepath.as_slice(), "-c", "-o"])
                          .arg(outpath.clone())
                          .status().unwrap();
      }
      else if let Some(basename) = eat_extension(filename, ".s") {
        outpath = format!("{}/{}.o", out_dir, basename);

        Command::new("nasm").args(&[filepath.as_slice(), "-felf64", "-o"])
                            .arg(outpath.clone())
                            .status().unwrap();
      }
      else { continue }

      objects.push(outpath);
    }
  }

  Command::new("ar").args(&["crus", "libcontext.a"])
                    .args(objects.as_slice())
                    .cwd(&Path::new(&out_dir))
                    .status().unwrap();

  println!("cargo:rustc-flags=-L {} -l context:static", out_dir);
}

fn eat_extension<'a>(s: &'a str, ext: &str) -> Option<&'a str> {
  if s.ends_with(ext) {
    Some(s.slice_to(s.len() - ext.len()))
  }
  else {
    None
  }
}
<commit_msg>build library code as PIC<commit_after>use std::io::Command;
use std::io::fs::PathExtensions;
use std::io::fs;
use std::os;

fn main() {
  let out_dir = os::getenv("OUT_DIR").unwrap();
  let mut objects = Vec::new();

  let files = fs::readdir(&Path::new("src")).unwrap();
  let mut files = files.iter().filter(|p| p.is_file());

  for file in files {
    if let Some(filename) = file.filename_str() {
      let filepath = format!("src/{}", filename);
      let outpath;

      if let Some(basename) = eat_extension(filename, ".c") {
        outpath = format!("{}/{}.o", out_dir, basename);

        Command::new("cc").args(&[filepath.as_slice(), "-c", "-fPIC", "-o"])
                          .arg(outpath.clone())
                          .status().unwrap();
      }
      else if let Some(basename) = eat_extension(filename, ".s") {
        outpath = format!("{}/{}.o", out_dir, basename);

        Command::new("nasm").args(&[filepath.as_slice(), "-felf64", "-o"])
                            .arg(outpath.clone())
                            .status().unwrap();
      }
      else { continue }

      objects.push(outpath);
    }
  }

  Command::new("ar").args(&["crus", "libcontext.a"])
                    .args(objects.as_slice())
                    .cwd(&Path::new(&out_dir))
                    .status().unwrap();

  println!("cargo:rustc-flags=-L {} -l context:static", out_dir);
}

fn eat_extension<'a>(s: &'a str, ext: &str) -> Option<&'a str> {
  if s.ends_with(ext) {
    Some(s.slice_to(s.len() - ext.len()))
  }
  else {
    None
  }
}
<|endoftext|>


<commit_before>use std::io::Command;
use std::os;

#[allow(unused_must_use)]
fn main() {
    let mut build = os::getcwd().unwrap();
    build.push_many(&["ext", "libqmlrswrapper", "build"]);

    /* Ignore error, the return value is not reliable and we'll catch it when chdir'ing anyway. */
    std::io::fs::mkdir(&build, std::io::USER_RWX);

    os::change_dir(&build).ok().expect("Failed to change into build directory");

    let out = Command::new("cmake").arg("..").output();
    if out.unwrap().status != std::io::process::ProcessExit::ExitStatus(0) {
        panic!("Failed to run cmake");
    }

    let out = Command::new("make").output();
    if out.unwrap().status != std::io::process::ProcessExit::ExitStatus(0) {
        panic!("Failed to run make");
    }

    println!("cargo:rustc-flags=-L ext/libqmlrswrapper/build -l qmlrswrapper -l Qt5Core -l Qt5Quick -l Qt5Gui -l Qt5Qml -l stdc++");
}
<commit_msg>Fix -L switch to rustc<commit_after>use std::io::Command;
use std::os;

#[allow(unused_must_use)]
fn main() {
    let mut build = os::getcwd().unwrap();
    build.push_many(&["ext", "libqmlrswrapper", "build"]);

    /* Ignore error, the return value is not reliable and we'll catch it when chdir'ing anyway. */
    std::io::fs::mkdir(&build, std::io::USER_RWX);

    os::change_dir(&build).ok().expect("Failed to change into build directory");

    let out = Command::new("cmake").arg("..").output();
    if out.unwrap().status != std::io::process::ProcessExit::ExitStatus(0) {
        panic!("Failed to run cmake");
    }

    let out = Command::new("make").output();
    if out.unwrap().status != std::io::process::ProcessExit::ExitStatus(0) {
        panic!("Failed to run make");
    }

    println!("cargo:rustc-flags=-L {} -l qmlrswrapper:static -l Qt5Core -l Qt5Quick -l Qt5Gui -l Qt5Qml -l stdc++", build.display());
}
<|endoftext|>


<commit_before>extern crate cc;
extern crate glob;

#[cfg(not(feature = "orangepi"))]
const TARGET: &'static str = "wiringPi";
#[cfg(feature = "orangepi")]
const TARGET: &'static str = "WiringOP";

fn main() {
    if cfg!(feature = "development") {
        return;
    }

    // only build wiringpi/wiringop for arm/armv7 platforms

    let target = std::env::var("TARGET").unwrap();
    if !(target.starts_with("arm-") || target.starts_with("armv7-")) {
        return;
    }

    cc::Build::new()
        .files(
            glob::glob(&format!("{}/wiringPi/*.c", TARGET))
                .unwrap()
                .filter_map(|x| x.ok()),
        )
        .include(format!("{}/", TARGET))
        .include(format!("{}/wiringPi/", TARGET))
        .static_flag(true)
        .compile("wiringpi");
}
<commit_msg>automatically add development feature on non-arm targets<commit_after>extern crate cc;
extern crate glob;

#[cfg(not(feature = "orangepi"))]
const TARGET: &'static str = "wiringPi";
#[cfg(feature = "orangepi")]
const TARGET: &'static str = "WiringOP";

fn main() {
    if cfg!(feature = "development") {
        return;
    }

    // only build wiringpi/wiringop for arm/armv7 platforms

    let target = std::env::var("TARGET").unwrap();
    if !(target.starts_with("arm-") || target.starts_with("armv7-")) {
        println!("cargo:rustc-cfg=feature=\"development\"");
        return;
    }

    cc::Build::new()
        .files(
            glob::glob(&format!("{}/wiringPi/*.c", TARGET))
                .unwrap()
                .filter_map(|x| x.ok()),
        )
        .include(format!("{}/", TARGET))
        .include(format!("{}/wiringPi/", TARGET))
        .static_flag(true)
        .compile("wiringpi");
}
<|endoftext|>


<commit_before>use std::hash::Hash;
use std::collections::HashMap;

use communication::*;
use communication::pact::Pipeline;

use example_shared::*;
use example_shared::operators::unary::UnaryNotifyExt;

use drain::DrainExt;

pub trait DelayExt<G: GraphBuilder, D: Data> {
    fn delay<F: Fn(&D, &G::Timestamp)->G::Timestamp+'static>(&self, F) -> Self;
}

impl<G: GraphBuilder, D: Data> DelayExt<G, D> for Stream<G, D>
where G::Timestamp: Hash {
    fn delay<F: Fn(&D, &G::Timestamp)->G::Timestamp+'static>(&self, func: F) -> Stream<G, D> {
        let _threshold = 256; // TODO : make more "streaming" by flushing
        let mut elements = HashMap::new();
        self.unary_notify(Pipeline, format!("Delay"), vec![], move |input, output, notificator| {
            while let Some((time, data)) = input.pull() {
                for datum in data.drain_temp() {
                    let mut new_time = func(&datum, &time);
                    if !(new_time >= time) {
                        new_time = time;
                    }

                    elements.entry(new_time.clone())
                            .or_insert_with(|| { notificator.notify_at(&new_time); Vec::new() })
                            .push(datum);
                }
            }
            // for each available notification, send corresponding set
            while let Some((time, _count)) = notificator.next() {
                if let Some(mut data) = elements.remove(&time) {
                    output.give_at(&time, data.drain_temp());
                }
            }
        })
    }
}
<commit_msg>added delay_batch method; delays by time rather than record and is more efficient<commit_after>use std::hash::Hash;
use std::collections::HashMap;

use communication::*;
use communication::pact::Pipeline;

use example_shared::*;
use example_shared::operators::unary::UnaryNotifyExt;

use drain::DrainExt;

pub trait DelayExt<G: GraphBuilder, D: Data> {
    fn delay<F: Fn(&D, &G::Timestamp)->G::Timestamp+'static>(&self, F) -> Self;
    fn delay_batch<F: Fn(&G::Timestamp)->G::Timestamp+'static>(&self, F) -> Self;
}

impl<G: GraphBuilder, D: Data> DelayExt<G, D> for Stream<G, D>
where G::Timestamp: Hash {
    fn delay<F: Fn(&D, &G::Timestamp)->G::Timestamp+'static>(&self, func: F) -> Stream<G, D> {

        let mut elements = HashMap::new();
        self.unary_notify(Pipeline, format!("Delay"), vec![], move |input, output, notificator| {
            while let Some((time, data)) = input.pull() {
                for datum in data.drain_temp() {
                    let mut new_time = func(&datum, &time);
                    if !(new_time >= time) {
                        new_time = time;
                    }

                    elements.entry(new_time.clone())
                            .or_insert_with(|| { notificator.notify_at(&new_time); Vec::new() })
                            .push(datum);
                }
            }
            // for each available notification, send corresponding set
            while let Some((time, _count)) = notificator.next() {
                if let Some(mut data) = elements.remove(&time) {
                    output.give_at(&time, data.drain_temp());
                }
            }
        })
    }

    fn delay_batch<F: Fn(&G::Timestamp)->G::Timestamp+'static>(&self, func: F) -> Stream<G, D> {

        let mut stash = Vec::new();
        let mut elements = HashMap::new();
        self.unary_notify(Pipeline, format!("Delay"), vec![], move |input, output, notificator| {
            while let Some((time, data)) = input.pull() {
                let mut new_time = func(&time);
                if !(new_time >= time) {
                    new_time = time;
                }

                let spare = if let Some(vec) = stash.pop() { vec } else { Vec::with_capacity(256) };
                let data = ::std::mem::replace(data, spare);

                elements.entry(new_time.clone())
                        .or_insert_with(|| { notificator.notify_at(&new_time); Vec::new() })
                        .push(data);
            }

            // for each available notification, send corresponding set
            while let Some((time, _count)) = notificator.next() {
                if let Some(mut datas) = elements.remove(&time) {
                    for mut data in datas.drain_temp() {
                        output.give_vector_at(&time, &mut data);
                        stash.push(data);
                    }
                }
            }
        })
    }
}
<|endoftext|>


<commit_before><commit_msg>Comment update<commit_after><|endoftext|>


<commit_before>//! Defines `Body`: a lowered representation of bodies of functions, statics and
//! consts.
mod lower;
pub mod scope;

use std::{mem, ops::Index, sync::Arc};

use drop_bomb::DropBomb;
use either::Either;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
use ra_arena::{map::ArenaMap, Arena};
use ra_cfg::CfgOptions;
use ra_db::CrateId;
use ra_prof::profile;
use ra_syntax::{ast, AstNode, AstPtr};
use rustc_hash::FxHashMap;

pub(crate) use lower::LowerCtx;

use crate::{
    attr::Attrs,
    db::DefDatabase,
    expr::{Expr, ExprId, Pat, PatId},
    item_scope::BuiltinShadowMode,
    item_scope::ItemScope,
    nameres::CrateDefMap,
    path::{ModPath, Path},
    src::HasSource,
    AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
};

/// A subset of Expander that only deals with cfg attributes. We only need it to
/// avoid cyclic queries in crate def map during enum processing.
pub(crate) struct CfgExpander {
    cfg_options: CfgOptions,
    hygiene: Hygiene,
}

pub(crate) struct Expander {
    cfg_expander: CfgExpander,
    crate_def_map: Arc<CrateDefMap>,
    current_file_id: HirFileId,
    ast_id_map: Arc<AstIdMap>,
    module: ModuleId,
    recursive_limit: usize,
}

impl CfgExpander {
    pub(crate) fn new(
        db: &dyn DefDatabase,
        current_file_id: HirFileId,
        krate: CrateId,
    ) -> CfgExpander {
        let hygiene = Hygiene::new(db.upcast(), current_file_id);
        let cfg_options = db.crate_graph()[krate].cfg_options.clone();
        CfgExpander { cfg_options, hygiene }
    }

    pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs {
        Attrs::new(owner, &self.hygiene)
    }

    pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
        let attrs = self.parse_attrs(owner);
        attrs.is_cfg_enabled(&self.cfg_options)
    }
}

impl Expander {
    pub(crate) fn new(
        db: &dyn DefDatabase,
        current_file_id: HirFileId,
        module: ModuleId,
    ) -> Expander {
        let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
        let crate_def_map = db.crate_def_map(module.krate);
        let ast_id_map = db.ast_id_map(current_file_id);
        Expander {
            cfg_expander,
            crate_def_map,
            current_file_id,
            ast_id_map,
            module,
            recursive_limit: 0,
        }
    }

    pub(crate) fn enter_expand<T: ast::AstNode>(
        &mut self,
        db: &dyn DefDatabase,
        local_scope: Option<&ItemScope>,
        macro_call: ast::MacroCall,
    ) -> Option<(Mark, T)> {
        if self.recursive_limit > 1024 {
            return None;
        }

        let macro_call = InFile::new(self.current_file_id, &macro_call);

        if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
            if let Some(local_scope) = local_scope {
                if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
                    return Some(def);
                }
            }
            self.resolve_path_as_macro(db, &path)
        }) {
            let file_id = call_id.as_file();
            if let Some(node) = db.parse_or_expand(file_id) {
                if let Some(expr) = T::cast(node) {
                    log::debug!("macro expansion {:#?}", expr.syntax());

                    let mark = Mark {
                        file_id: self.current_file_id,
                        ast_id_map: mem::take(&mut self.ast_id_map),
                        bomb: DropBomb::new("expansion mark dropped"),
                    };
                    self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
                    self.current_file_id = file_id;
                    self.ast_id_map = db.ast_id_map(file_id);
                    self.recursive_limit += 1;

                    return Some((mark, expr));
                }
            }
        }

        // FIXME: Instead of just dropping the error from expansion
        // report it
        None
    }

    pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
        self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
        self.current_file_id = mark.file_id;
        self.ast_id_map = mem::take(&mut mark.ast_id_map);
        self.recursive_limit -= 1;
        mark.bomb.defuse();
    }

    pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
        InFile { file_id: self.current_file_id, value }
    }

    pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
        self.cfg_expander.is_cfg_enabled(owner)
    }

    fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
        Path::from_src(path, &self.cfg_expander.hygiene)
    }

    fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
        self.crate_def_map
            .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
            .0
            .take_macros()
    }

    fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
        let file_local_id = self.ast_id_map.ast_id(item);
        AstId::new(self.current_file_id, file_local_id)
    }
}

pub(crate) struct Mark {
    file_id: HirFileId,
    ast_id_map: Arc<AstIdMap>,
    bomb: DropBomb,
}

/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
    pub exprs: Arena<Expr>,
    pub pats: Arena<Pat>,
    /// The patterns for the function's parameters. While the parameter types are
    /// part of the function signature, the patterns are not (they don't change
    /// the external type of the function).
    ///
    /// If this `Body` is for the body of a constant, this will just be
    /// empty.
    pub params: Vec<PatId>,
    /// The `ExprId` of the actual body expression.
    pub body_expr: ExprId,
    pub item_scope: ItemScope,
}

pub type ExprPtr = AstPtr<ast::Expr>;
pub type ExprSource = InFile<ExprPtr>;

pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
pub type PatSource = InFile<PatPtr>;

/// An item body together with the mapping from syntax nodes to HIR expression
/// IDs. This is needed to go from e.g. a position in a file to the HIR
/// expression containing it; but for type inference etc., we want to operate on
/// a structure that is agnostic to the actual positions of expressions in the
/// file, so that we don't recompute types whenever some whitespace is typed.
///
/// One complication here is that, due to macro expansion, a single `Body` might
/// be spread across several files. So, for each ExprId and PatId, we record
/// both the HirFileId and the position inside the file. However, we only store
/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
/// this properly for macros.
#[derive(Default, Debug, Eq, PartialEq)]
pub struct BodySourceMap {
    expr_map: FxHashMap<ExprSource, ExprId>,
    expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
    pat_map: FxHashMap<PatSource, PatId>,
    pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
    field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordField>>>,
    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
}

#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct SyntheticSyntax;

impl Body {
    pub(crate) fn body_with_source_map_query(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
    ) -> (Arc<Body>, Arc<BodySourceMap>) {
        let _p = profile("body_with_source_map_query");
        let mut params = None;

        let (file_id, module, body) = match def {
            DefWithBodyId::FunctionId(f) => {
                let f = f.lookup(db);
                let src = f.source(db);
                params = src.value.param_list();
                (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
            }
            DefWithBodyId::ConstId(c) => {
                let c = c.lookup(db);
                let src = c.source(db);
                (src.file_id, c.module(db), src.value.body())
            }
            DefWithBodyId::StaticId(s) => {
                let s = s.lookup(db);
                let src = s.source(db);
                (src.file_id, s.module(db), src.value.body())
            }
        };
        let expander = Expander::new(db, file_id, module);
        let (body, source_map) = Body::new(db, def, expander, params, body);
        (Arc::new(body), Arc::new(source_map))
    }

    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
        db.body_with_source_map(def).0
    }

    fn new(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
        expander: Expander,
        params: Option<ast::ParamList>,
        body: Option<ast::Expr>,
    ) -> (Body, BodySourceMap) {
        lower::lower(db, def, expander, params, body)
    }
}

impl Index<ExprId> for Body {
    type Output = Expr;

    fn index(&self, expr: ExprId) -> &Expr {
        &self.exprs[expr]
    }
}

impl Index<PatId> for Body {
    type Output = Pat;

    fn index(&self, pat: PatId) -> &Pat {
        &self.pats[pat]
    }
}

impl BodySourceMap {
    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
        self.expr_map_back[expr].clone()
    }

    pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
        let src = node.map(|it| AstPtr::new(it));
        self.expr_map.get(&src).cloned()
    }

    pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
        let src = node.map(|it| AstPtr::new(it));
        self.expansions.get(&src).cloned()
    }

    pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
        self.pat_map_back[pat].clone()
    }

    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
        let src = node.map(|it| Either::Left(AstPtr::new(it)));
        self.pat_map.get(&src).cloned()
    }

    pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
        let src = node.map(|it| Either::Right(AstPtr::new(it)));
        self.pat_map.get(&src).cloned()
    }

    pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> {
        self.field_map[&(expr, field)].clone()
    }
}
<commit_msg>More tight recursion limit when expanding macros in function bodies<commit_after>//! Defines `Body`: a lowered representation of bodies of functions, statics and
//! consts.
mod lower;
pub mod scope;

use std::{mem, ops::Index, sync::Arc};

use drop_bomb::DropBomb;
use either::Either;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
use ra_arena::{map::ArenaMap, Arena};
use ra_cfg::CfgOptions;
use ra_db::CrateId;
use ra_prof::profile;
use ra_syntax::{ast, AstNode, AstPtr};
use rustc_hash::FxHashMap;
use test_utils::mark;

pub(crate) use lower::LowerCtx;

use crate::{
    attr::Attrs,
    db::DefDatabase,
    expr::{Expr, ExprId, Pat, PatId},
    item_scope::BuiltinShadowMode,
    item_scope::ItemScope,
    nameres::CrateDefMap,
    path::{ModPath, Path},
    src::HasSource,
    AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
};

/// A subset of Expander that only deals with cfg attributes. We only need it to
/// avoid cyclic queries in crate def map during enum processing.
pub(crate) struct CfgExpander {
    cfg_options: CfgOptions,
    hygiene: Hygiene,
}

pub(crate) struct Expander {
    cfg_expander: CfgExpander,
    crate_def_map: Arc<CrateDefMap>,
    current_file_id: HirFileId,
    ast_id_map: Arc<AstIdMap>,
    module: ModuleId,
    recursion_limit: usize,
}

impl CfgExpander {
    pub(crate) fn new(
        db: &dyn DefDatabase,
        current_file_id: HirFileId,
        krate: CrateId,
    ) -> CfgExpander {
        let hygiene = Hygiene::new(db.upcast(), current_file_id);
        let cfg_options = db.crate_graph()[krate].cfg_options.clone();
        CfgExpander { cfg_options, hygiene }
    }

    pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs {
        Attrs::new(owner, &self.hygiene)
    }

    pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
        let attrs = self.parse_attrs(owner);
        attrs.is_cfg_enabled(&self.cfg_options)
    }
}

impl Expander {
    pub(crate) fn new(
        db: &dyn DefDatabase,
        current_file_id: HirFileId,
        module: ModuleId,
    ) -> Expander {
        let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
        let crate_def_map = db.crate_def_map(module.krate);
        let ast_id_map = db.ast_id_map(current_file_id);
        Expander {
            cfg_expander,
            crate_def_map,
            current_file_id,
            ast_id_map,
            module,
            recursion_limit: 0,
        }
    }

    pub(crate) fn enter_expand<T: ast::AstNode>(
        &mut self,
        db: &dyn DefDatabase,
        local_scope: Option<&ItemScope>,
        macro_call: ast::MacroCall,
    ) -> Option<(Mark, T)> {
        self.recursion_limit += 1;
        if self.recursion_limit > 32 {
            mark::hit!(your_stack_belongs_to_me);
            return None;
        }

        let macro_call = InFile::new(self.current_file_id, &macro_call);

        if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
            if let Some(local_scope) = local_scope {
                if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
                    return Some(def);
                }
            }
            self.resolve_path_as_macro(db, &path)
        }) {
            let file_id = call_id.as_file();
            if let Some(node) = db.parse_or_expand(file_id) {
                if let Some(expr) = T::cast(node) {
                    log::debug!("macro expansion {:#?}", expr.syntax());

                    let mark = Mark {
                        file_id: self.current_file_id,
                        ast_id_map: mem::take(&mut self.ast_id_map),
                        bomb: DropBomb::new("expansion mark dropped"),
                    };
                    self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
                    self.current_file_id = file_id;
                    self.ast_id_map = db.ast_id_map(file_id);
                    return Some((mark, expr));
                }
            }
        }

        // FIXME: Instead of just dropping the error from expansion
        // report it
        None
    }

    pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
        self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
        self.current_file_id = mark.file_id;
        self.ast_id_map = mem::take(&mut mark.ast_id_map);
        self.recursion_limit -= 1;
        mark.bomb.defuse();
    }

    pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
        InFile { file_id: self.current_file_id, value }
    }

    pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
        self.cfg_expander.is_cfg_enabled(owner)
    }

    fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
        Path::from_src(path, &self.cfg_expander.hygiene)
    }

    fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
        self.crate_def_map
            .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
            .0
            .take_macros()
    }

    fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
        let file_local_id = self.ast_id_map.ast_id(item);
        AstId::new(self.current_file_id, file_local_id)
    }
}

pub(crate) struct Mark {
    file_id: HirFileId,
    ast_id_map: Arc<AstIdMap>,
    bomb: DropBomb,
}

/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
    pub exprs: Arena<Expr>,
    pub pats: Arena<Pat>,
    /// The patterns for the function's parameters. While the parameter types are
    /// part of the function signature, the patterns are not (they don't change
    /// the external type of the function).
    ///
    /// If this `Body` is for the body of a constant, this will just be
    /// empty.
    pub params: Vec<PatId>,
    /// The `ExprId` of the actual body expression.
    pub body_expr: ExprId,
    pub item_scope: ItemScope,
}

pub type ExprPtr = AstPtr<ast::Expr>;
pub type ExprSource = InFile<ExprPtr>;

pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
pub type PatSource = InFile<PatPtr>;

/// An item body together with the mapping from syntax nodes to HIR expression
/// IDs. This is needed to go from e.g. a position in a file to the HIR
/// expression containing it; but for type inference etc., we want to operate on
/// a structure that is agnostic to the actual positions of expressions in the
/// file, so that we don't recompute types whenever some whitespace is typed.
///
/// One complication here is that, due to macro expansion, a single `Body` might
/// be spread across several files. So, for each ExprId and PatId, we record
/// both the HirFileId and the position inside the file. However, we only store
/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
/// this properly for macros.
#[derive(Default, Debug, Eq, PartialEq)]
pub struct BodySourceMap {
    expr_map: FxHashMap<ExprSource, ExprId>,
    expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
    pat_map: FxHashMap<PatSource, PatId>,
    pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
    field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordField>>>,
    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
}

#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct SyntheticSyntax;

impl Body {
    pub(crate) fn body_with_source_map_query(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
    ) -> (Arc<Body>, Arc<BodySourceMap>) {
        let _p = profile("body_with_source_map_query");
        let mut params = None;

        let (file_id, module, body) = match def {
            DefWithBodyId::FunctionId(f) => {
                let f = f.lookup(db);
                let src = f.source(db);
                params = src.value.param_list();
                (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
            }
            DefWithBodyId::ConstId(c) => {
                let c = c.lookup(db);
                let src = c.source(db);
                (src.file_id, c.module(db), src.value.body())
            }
            DefWithBodyId::StaticId(s) => {
                let s = s.lookup(db);
                let src = s.source(db);
                (src.file_id, s.module(db), src.value.body())
            }
        };
        let expander = Expander::new(db, file_id, module);
        let (body, source_map) = Body::new(db, def, expander, params, body);
        (Arc::new(body), Arc::new(source_map))
    }

    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
        db.body_with_source_map(def).0
    }

    fn new(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
        expander: Expander,
        params: Option<ast::ParamList>,
        body: Option<ast::Expr>,
    ) -> (Body, BodySourceMap) {
        lower::lower(db, def, expander, params, body)
    }
}

impl Index<ExprId> for Body {
    type Output = Expr;

    fn index(&self, expr: ExprId) -> &Expr {
        &self.exprs[expr]
    }
}

impl Index<PatId> for Body {
    type Output = Pat;

    fn index(&self, pat: PatId) -> &Pat {
        &self.pats[pat]
    }
}

impl BodySourceMap {
    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
        self.expr_map_back[expr].clone()
    }

    pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
        let src = node.map(|it| AstPtr::new(it));
        self.expr_map.get(&src).cloned()
    }

    pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
        let src = node.map(|it| AstPtr::new(it));
        self.expansions.get(&src).cloned()
    }

    pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
        self.pat_map_back[pat].clone()
    }

    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
        let src = node.map(|it| Either::Left(AstPtr::new(it)));
        self.pat_map.get(&src).cloned()
    }

    pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
        let src = node.map(|it| Either::Right(AstPtr::new(it)));
        self.pat_map.get(&src).cloned()
    }

    pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> {
        self.field_map[&(expr, field)].clone()
    }
}

#[cfg(test)]
mod tests {
    use ra_db::{fixture::WithFixture, SourceDatabase};
    use test_utils::mark;

    use crate::ModuleDefId;

    use super::*;

    fn lower(ra_fixture: &str) -> Arc<Body> {
        let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture);

        let krate = db.crate_graph().iter().next().unwrap();
        let def_map = db.crate_def_map(krate);
        let module = def_map.modules_for_file(file_id).next().unwrap();
        let module = &def_map[module];
        let fn_def = match module.scope.declarations().next().unwrap() {
            ModuleDefId::FunctionId(it) => it,
            _ => panic!(),
        };

        db.body(fn_def.into())
    }

    #[test]
    fn your_stack_belongs_to_me() {
        mark::check!(your_stack_belongs_to_me);
        lower(
            r"
macro_rules! n_nuple {
    ($e:tt) => ();
    ($($rest:tt)*) => {{
        (n_nuple!($($rest)*)None,)
    }};
}
fn main() { n_nuple!(1,2,3); }
",
        );
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// 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
//
//     https://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.

/// Invocations of this macro instruct Rerast to replace the first expression/statement with the one
/// after the =>. For example replace!(5 => 6) says to replace all occurences of the literal 5 with
/// the literal 6.
///
/// The macro should be invoked inside a function definition. The arguments to the function
/// represent placeholders within the search and replace expressions.
#[macro_export]
macro_rules! replace {
    ($a:expr => $b:expr) => {
        // Suppress warnings about unused values in case we're replacing something like a Result.
        #[allow(unused_must_use)]
        // If we have a search or replacement pattern that is just a placeholder then this macro
        // will turn that into a statement. Normally a reference to a variable is not permitted as a
        // statement, allow it.
        #[allow(path_statements)]
        #[allow(unreachable_code)]
        {
            if false {
                use $crate::GetMode;
                match $crate::_ExprRuleMarker.get_mode() {
                    $crate::Mode::Search => {&$a;}
                    $crate::Mode::Replace => {&$b;}
                }
                // Some of the placeholders used in this replace! invocation might have been
                // moved. To avoid use-after-move errors, we mark this as unreachable.
                unreachable!();
            }
        }
    };
}

/// Replaces one type with another. This will not match trait bounds (e.g. in where clauses), since
/// they are not types. If you want to replace all references to a trait, use `replace_trait_ref!`
/// instead.
#[macro_export]
macro_rules! replace_type {
    ($a:ty => $b:ty) => {
        {
            use $crate::GetMode;
            match $crate::_TypeRuleMarker.get_mode() {
                $crate::Mode::Search => {
                    let _t: &$a;
                }
                $crate::Mode::Replace => {
                    let _t: &$b;
                }
            }
        }
    };
}

#[macro_export]
macro_rules! replace_trait_ref {
    ($a:ty => $b:ty) => {
        {
            use $crate::GetMode;
            match $crate::_TraitRefRuleMarker.get_mode() {
                $crate::Mode::Search => {
                    let _t: &$a;
                }
                $crate::Mode::Replace => {
                    let _t: &$b;
                }
            }
        }
    };
}

/// Replaces a pattern with another pattern. A placeholder is required in order to specify the types
/// of the search and replacement pattern. Although they can be the same if you're replacing a
/// pattern with another of the same type.
///
/// # Examples
/// ```
/// # #[macro_use] extern crate rerast;
/// # struct Foo(i32);
/// fn some_foo_to_result_foo(p1: Option<Foo>, p2: Result<Foo, ()>) {
///     replace_pattern!(Some(f1) = p1 => Result::Ok(f1) = p2);
///     replace_pattern!(None = p1 => Result::Err(()) = p2);
/// }
/// # fn main () {}
/// ```
///
/// This will transform:
/// ```
/// # #[derive(Copy, Clone)] struct Foo(i32);
/// # fn f() -> Option<Foo> {Some(Foo(42))}
/// match f() {
///     Some(Foo(x)) => x,
///     None => 0,
/// }
/// ```
/// Into:
/// ```
/// # fn f() -> Foo {Foo(42)}
/// match f() {
///     Result::Ok(Foo(x)) => x,
///     Result::Err(()) => 0,
/// }
/// ```
#[macro_export]
macro_rules! replace_pattern {
    ($a:pat = $at:ident => $b:pat = $bt:ident) => {
        if false {
            use $crate::GetMode;
            match $crate::_PatternRuleMarker.get_mode() {
                $crate::Mode::Search => {
                    if let Some($a) = Some($at) {};
                }
                $crate::Mode::Replace => {
                    if let Some($b) = Some($bt) {};
                }
            }
            // Some of the placeholders used in this replace_pattern! invocation might have been
            // moved. To avoid use-after-move errors, we mark this as unreachable.
            unreachable!();
        }
    }
}

/// A placeholder that matches zero or more statements
/// IMPORTANT: This is currently broken and will most likely be replaced with a macro.
pub type Statements = &'static Fn() -> _Statements;

// These types are internal markers to help rerast find and identify things in rules. They're not
// intended to be referenced directly.
pub enum Mode {
    Search,
    Replace,
}
pub struct _Statements;
pub trait GetMode {
    fn get_mode(&self) -> Mode {
        Mode::Search
    }
}
pub struct _TypeRuleMarker;
impl GetMode for _TypeRuleMarker {}
pub struct _ExprRuleMarker;
impl GetMode for _ExprRuleMarker {}
pub struct _PatternRuleMarker;
impl GetMode for _PatternRuleMarker {}
pub struct _TraitRefRuleMarker;
impl GetMode for _TraitRefRuleMarker {}

pub const _RERAST_MACROS_SRC: &'static str = include_str!("lib.rs");
<commit_msg>doc(macros) Fix formatting for rustdocs<commit_after>// Copyright 2017 Google Inc.
//
// 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
//
//     https://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.

/// Invocations of this macro instruct Rerast to replace the first expression/statement with the one
/// after the =>. For example replace!(5 => 6) says to replace all occurences of the literal 5 with
/// the literal 6.
///
/// The macro should be invoked inside a function definition. The arguments to the function
/// represent placeholders within the search and replace expressions.
#[macro_export]
macro_rules! replace {
    ($a:expr => $b:expr) => {
        // Suppress warnings about unused values in case we're replacing something like a Result.
        #[allow(unused_must_use)]
        // If we have a search or replacement pattern that is just a placeholder then this macro
        // will turn that into a statement. Normally a reference to a variable is not permitted as a
        // statement, allow it.
        #[allow(path_statements)]
        #[allow(unreachable_code)]
        {
            if false {
                use $crate::GetMode;
                match $crate::_ExprRuleMarker.get_mode() {
                    $crate::Mode::Search => {&$a;}
                    $crate::Mode::Replace => {&$b;}
                }
                // Some of the placeholders used in this replace! invocation might have been
                // moved. To avoid use-after-move errors, we mark this as unreachable.
                unreachable!();
            }
        }
    };
}

/// Replaces one type with another. This will not match trait bounds (e.g. in where clauses), since
/// they are not types. If you want to replace all references to a trait, use `replace_trait_ref!`
/// instead.
#[macro_export]
macro_rules! replace_type {
    ($a:ty => $b:ty) => {
        {
            use $crate::GetMode;
            match $crate::_TypeRuleMarker.get_mode() {
                $crate::Mode::Search => {
                    let _t: &$a;
                }
                $crate::Mode::Replace => {
                    let _t: &$b;
                }
            }
        }
    };
}

#[macro_export]
macro_rules! replace_trait_ref {
    ($a:ty => $b:ty) => {
        {
            use $crate::GetMode;
            match $crate::_TraitRefRuleMarker.get_mode() {
                $crate::Mode::Search => {
                    let _t: &$a;
                }
                $crate::Mode::Replace => {
                    let _t: &$b;
                }
            }
        }
    };
}

/// Replaces a pattern with another pattern. A placeholder is required in order to specify the types
/// of the search and replacement pattern. Although they can be the same if you're replacing a
/// pattern with another of the same type.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate rerast;
/// # struct Foo(i32);
/// fn some_foo_to_result_foo(p1: Option<Foo>, p2: Result<Foo, ()>) {
///     replace_pattern!(Some(f1) = p1 => Result::Ok(f1) = p2);
///     replace_pattern!(None = p1 => Result::Err(()) = p2);
/// }
/// # fn main () {}
/// ```
///
/// This will transform:
///
/// ```
/// # #[derive(Copy, Clone)] struct Foo(i32);
/// # fn f() -> Option<Foo> {Some(Foo(42))}
/// match f() {
///     Some(Foo(x)) => x,
///     None => 0,
/// }
/// ```
///
/// Into:
///
/// ```
/// # fn f() -> Foo {Foo(42)}
/// match f() {
///     Result::Ok(Foo(x)) => x,
///     Result::Err(()) => 0,
/// }
/// ```
#[macro_export]
macro_rules! replace_pattern {
    ($a:pat = $at:ident => $b:pat = $bt:ident) => {
        if false {
            use $crate::GetMode;
            match $crate::_PatternRuleMarker.get_mode() {
                $crate::Mode::Search => {
                    if let Some($a) = Some($at) {};
                }
                $crate::Mode::Replace => {
                    if let Some($b) = Some($bt) {};
                }
            }
            // Some of the placeholders used in this replace_pattern! invocation might have been
            // moved. To avoid use-after-move errors, we mark this as unreachable.
            unreachable!();
        }
    }
}

/// A placeholder that matches zero or more statements
/// IMPORTANT: This is currently broken and will most likely be replaced with a macro.
pub type Statements = &'static Fn() -> _Statements;

// These types are internal markers to help rerast find and identify things in rules. They're not
// intended to be referenced directly.
pub enum Mode {
    Search,
    Replace,
}
pub struct _Statements;
pub trait GetMode {
    fn get_mode(&self) -> Mode {
        Mode::Search
    }
}
pub struct _TypeRuleMarker;
impl GetMode for _TypeRuleMarker {}
pub struct _ExprRuleMarker;
impl GetMode for _ExprRuleMarker {}
pub struct _PatternRuleMarker;
impl GetMode for _PatternRuleMarker {}
pub struct _TraitRefRuleMarker;
impl GetMode for _TraitRefRuleMarker {}

pub const _RERAST_MACROS_SRC: &'static str = include_str!("lib.rs");
<|endoftext|>