<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

mod analysis;
mod api;
mod codegen_cpp;
mod codegen_rs;
#[cfg(test)]
mod conversion_tests;
mod convert_error;
mod doc_attr;
mod error_reporter;
mod parse;
mod utilities;

use analysis::fun::FnAnalyzer;
use autocxx_parser::IncludeCppConfig;
pub(crate) use codegen_cpp::CppCodeGenerator;
pub(crate) use convert_error::ConvertError;
use itertools::Itertools;
use syn::{Item, ItemMod};

use crate::{
    conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy,
};

use self::{
    analysis::{
        abstract_types::{discard_ignored_functions, mark_types_abstract},
        allocators::create_alloc_and_frees,
        casts::add_casts,
        check_names,
        constructor_deps::decorate_types_with_constructor_deps,
        fun::FnPhase,
        gc::filter_apis_by_following_edges_from_allowlist,
        pod::analyze_pod_apis,
        remove_ignored::filter_apis_by_ignored_dependents,
        tdef::convert_typedef_targets,
    },
    api::{AnalysisPhase, Api},
    codegen_rs::RsCodeGenerator,
    parse::ParseBindgen,
};

const LOG_APIS: bool = true;

/// Converts the bindings generated by bindgen into a form suitable
/// for use with `cxx`.
/// In fact, most of the actual operation happens within an
/// individual `BridgeConversion`.
///
/// # Flexibility in handling bindgen output
///
/// autocxx is inevitably tied to the details of the bindgen output;
/// e.g. the creation of a 'root' mod when namespaces are enabled.
/// At the moment this crate takes the view that it's OK to panic
/// if the bindgen output is not as expected. It may be in future that
/// we need to be a bit more graceful, but for now, that's OK.
pub(crate) struct BridgeConverter<'a> {
    include_list: &'a [String],
    config: &'a IncludeCppConfig,
}

/// C++ and Rust code generation output.
pub(crate) struct CodegenResults {
    pub(crate) rs: Vec<Item>,
    pub(crate) cpp: Option<CppFilePair>,
}

impl<'a> BridgeConverter<'a> {
    pub fn new(include_list: &'a [String], config: &'a IncludeCppConfig) -> Self {
        Self {
            include_list,
            config,
        }
    }

    fn dump_apis<T: AnalysisPhase>(label: &str, apis: &[Api<T>]) {
        if LOG_APIS {
            log::info!(
                "APIs after {}:\n{}",
                label,
                apis.iter().map(|api| { format!("  {:?}", api) }).join("\n")
            )
        }
    }

    fn dump_apis_with_deps(label: &str, apis: &[Api<FnPhase>]) {
        if LOG_APIS {
            log::info!(
                "APIs after {}:\n{}",
                label,
                apis.iter()
                    .map(|api| { format!("  {:?}, deps={}", api, api.format_deps()) })
                    .join("\n")
            )
        }
    }

    /// Convert a TokenStream of bindgen-generated bindings to a form
    /// suitable for cxx.
    ///
    /// This is really the heart of autocxx. It parses the output of `bindgen`
    /// (although really by "parse" we mean to interpret the structures already built
    /// up by the `syn` crate).
    pub(crate) fn convert(
        &self,
        mut bindgen_mod: ItemMod,
        unsafe_policy: UnsafePolicy,
        inclusions: String,
        cpp_codegen_options: &CppCodegenOptions,
    ) -> Result<CodegenResults, ConvertError> {
        match &mut bindgen_mod.content {
            None => Err(ConvertError::NoContent),
            Some((_, items)) => {
                // Parse the bindgen mod.
                let items_to_process = items.drain(..).collect();
                let parser = ParseBindgen::new(self.config);
                let apis = parser.parse_items(items_to_process)?;
                Self::dump_apis("parsing", &apis);
                // Inside parse_results, we now have a list of APIs.
                // We now enter various analysis phases.
                // Next, convert any typedefs.
                // "Convert" means replacing bindgen-style type targets
                // (e.g. root::std::unique_ptr) with cxx-style targets (e.g. UniquePtr).
                let apis = convert_typedef_targets(self.config, apis);
                Self::dump_apis("typedefs", &apis);
                // Now analyze which of them can be POD (i.e. trivial, movable, pass-by-value
                // versus which need to be opaque).
                // Specifically, let's confirm that the items requested by the user to be
                // POD really are POD, and duly mark any dependent types.
                // This returns a new list of `Api`s, which will be parameterized with
                // the analysis results. It also returns an object which can be used
                // by subsequent phases to work out which objects are POD.
                let analyzed_apis = analyze_pod_apis(apis, self.config)?;
                Self::dump_apis("pod analysis", &analyzed_apis);
                let analyzed_apis = add_casts(analyzed_apis);
                let analyzed_apis = create_alloc_and_frees(analyzed_apis);
                // Next, figure out how we materialize different functions.
                // Some will be simple entries in the cxx::bridge module; others will
                // require C++ wrapper functions. This is probably the most complex
                // part of `autocxx`. Again, this returns a new set of `Api`s, but
                // parameterized by a richer set of metadata.
                Self::dump_apis("adding casts", &analyzed_apis);
                let analyzed_apis =
                    FnAnalyzer::analyze_functions(analyzed_apis, unsafe_policy, self.config);
                // If any of those functions turned out to be pure virtual, don't attempt
                // to generate UniquePtr implementations for the type, since it can't
                // be instantiated.
                Self::dump_apis("analyze fns", &analyzed_apis);
                let analyzed_apis = mark_types_abstract(analyzed_apis);
                Self::dump_apis("marking abstract", &analyzed_apis);
                // Annotate structs with a note of any copy/move constructors which
                // we may want to retain to avoid garbage collecting them later.
                let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
                Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis);
                let analyzed_apis = discard_ignored_functions(analyzed_apis);
                Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis);
                // Remove any APIs whose names are not compatible with cxx.
                let analyzed_apis = check_names(analyzed_apis);
                // During parsing or subsequent processing we might have encountered
                // items which we couldn't process due to as-yet-unsupported features.
                // There might be other items depending on such things. Let's remove them
                // too.
                let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
                Self::dump_apis_with_deps("removing ignored dependents", &analyzed_apis);

                // We now garbage collect the ones we don't need...
                let mut analyzed_apis =
                    filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
                // Determine what variably-sized C types (e.g. int) we need to include
                analysis::ctypes::append_ctype_information(&mut analyzed_apis);
                Self::dump_apis_with_deps("GC", &analyzed_apis);
                // And finally pass them to the code gen phases, which outputs
                // code suitable for cxx to consume.
                let cpp = CppCodeGenerator::generate_cpp_code(
                    inclusions,
                    &analyzed_apis,
                    self.config,
                    cpp_codegen_options,
                )?;
                let rs = RsCodeGenerator::generate_rs_code(
                    analyzed_apis,
                    self.include_list,
                    bindgen_mod,
                    self.config,
                );
                Ok(CodegenResults { rs, cpp })
            }
        }
    }
}
<commit_msg>Print APIs in a sorted order in logs.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

mod analysis;
mod api;
mod codegen_cpp;
mod codegen_rs;
#[cfg(test)]
mod conversion_tests;
mod convert_error;
mod doc_attr;
mod error_reporter;
mod parse;
mod utilities;

use analysis::fun::FnAnalyzer;
use autocxx_parser::IncludeCppConfig;
pub(crate) use codegen_cpp::CppCodeGenerator;
pub(crate) use convert_error::ConvertError;
use itertools::Itertools;
use syn::{Item, ItemMod};

use crate::{
    conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy,
};

use self::{
    analysis::{
        abstract_types::{discard_ignored_functions, mark_types_abstract},
        allocators::create_alloc_and_frees,
        casts::add_casts,
        check_names,
        constructor_deps::decorate_types_with_constructor_deps,
        fun::FnPhase,
        gc::filter_apis_by_following_edges_from_allowlist,
        pod::analyze_pod_apis,
        remove_ignored::filter_apis_by_ignored_dependents,
        tdef::convert_typedef_targets,
    },
    api::{AnalysisPhase, Api},
    codegen_rs::RsCodeGenerator,
    parse::ParseBindgen,
};

const LOG_APIS: bool = true;

/// Converts the bindings generated by bindgen into a form suitable
/// for use with `cxx`.
/// In fact, most of the actual operation happens within an
/// individual `BridgeConversion`.
///
/// # Flexibility in handling bindgen output
///
/// autocxx is inevitably tied to the details of the bindgen output;
/// e.g. the creation of a 'root' mod when namespaces are enabled.
/// At the moment this crate takes the view that it's OK to panic
/// if the bindgen output is not as expected. It may be in future that
/// we need to be a bit more graceful, but for now, that's OK.
pub(crate) struct BridgeConverter<'a> {
    include_list: &'a [String],
    config: &'a IncludeCppConfig,
}

/// C++ and Rust code generation output.
pub(crate) struct CodegenResults {
    pub(crate) rs: Vec<Item>,
    pub(crate) cpp: Option<CppFilePair>,
}

impl<'a> BridgeConverter<'a> {
    pub fn new(include_list: &'a [String], config: &'a IncludeCppConfig) -> Self {
        Self {
            include_list,
            config,
        }
    }

    fn dump_apis<T: AnalysisPhase>(label: &str, apis: &[Api<T>]) {
        if LOG_APIS {
            log::info!(
                "APIs after {}:\n{}",
                label,
                apis.iter()
                    .map(|api| { format!("  {:?}", api) })
                    .sorted()
                    .join("\n")
            )
        }
    }

    fn dump_apis_with_deps(label: &str, apis: &[Api<FnPhase>]) {
        if LOG_APIS {
            log::info!(
                "APIs after {}:\n{}",
                label,
                apis.iter()
                    .map(|api| { format!("  {:?}, deps={}", api, api.format_deps()) })
                    .sorted()
                    .join("\n")
            )
        }
    }

    /// Convert a TokenStream of bindgen-generated bindings to a form
    /// suitable for cxx.
    ///
    /// This is really the heart of autocxx. It parses the output of `bindgen`
    /// (although really by "parse" we mean to interpret the structures already built
    /// up by the `syn` crate).
    pub(crate) fn convert(
        &self,
        mut bindgen_mod: ItemMod,
        unsafe_policy: UnsafePolicy,
        inclusions: String,
        cpp_codegen_options: &CppCodegenOptions,
    ) -> Result<CodegenResults, ConvertError> {
        match &mut bindgen_mod.content {
            None => Err(ConvertError::NoContent),
            Some((_, items)) => {
                // Parse the bindgen mod.
                let items_to_process = items.drain(..).collect();
                let parser = ParseBindgen::new(self.config);
                let apis = parser.parse_items(items_to_process)?;
                Self::dump_apis("parsing", &apis);
                // Inside parse_results, we now have a list of APIs.
                // We now enter various analysis phases.
                // Next, convert any typedefs.
                // "Convert" means replacing bindgen-style type targets
                // (e.g. root::std::unique_ptr) with cxx-style targets (e.g. UniquePtr).
                let apis = convert_typedef_targets(self.config, apis);
                Self::dump_apis("typedefs", &apis);
                // Now analyze which of them can be POD (i.e. trivial, movable, pass-by-value
                // versus which need to be opaque).
                // Specifically, let's confirm that the items requested by the user to be
                // POD really are POD, and duly mark any dependent types.
                // This returns a new list of `Api`s, which will be parameterized with
                // the analysis results. It also returns an object which can be used
                // by subsequent phases to work out which objects are POD.
                let analyzed_apis = analyze_pod_apis(apis, self.config)?;
                Self::dump_apis("pod analysis", &analyzed_apis);
                let analyzed_apis = add_casts(analyzed_apis);
                let analyzed_apis = create_alloc_and_frees(analyzed_apis);
                // Next, figure out how we materialize different functions.
                // Some will be simple entries in the cxx::bridge module; others will
                // require C++ wrapper functions. This is probably the most complex
                // part of `autocxx`. Again, this returns a new set of `Api`s, but
                // parameterized by a richer set of metadata.
                Self::dump_apis("adding casts", &analyzed_apis);
                let analyzed_apis =
                    FnAnalyzer::analyze_functions(analyzed_apis, unsafe_policy, self.config);
                // If any of those functions turned out to be pure virtual, don't attempt
                // to generate UniquePtr implementations for the type, since it can't
                // be instantiated.
                Self::dump_apis("analyze fns", &analyzed_apis);
                let analyzed_apis = mark_types_abstract(analyzed_apis);
                Self::dump_apis("marking abstract", &analyzed_apis);
                // Annotate structs with a note of any copy/move constructors which
                // we may want to retain to avoid garbage collecting them later.
                let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
                Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis);
                let analyzed_apis = discard_ignored_functions(analyzed_apis);
                Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis);
                // Remove any APIs whose names are not compatible with cxx.
                let analyzed_apis = check_names(analyzed_apis);
                // During parsing or subsequent processing we might have encountered
                // items which we couldn't process due to as-yet-unsupported features.
                // There might be other items depending on such things. Let's remove them
                // too.
                let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
                Self::dump_apis_with_deps("removing ignored dependents", &analyzed_apis);

                // We now garbage collect the ones we don't need...
                let mut analyzed_apis =
                    filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
                // Determine what variably-sized C types (e.g. int) we need to include
                analysis::ctypes::append_ctype_information(&mut analyzed_apis);
                Self::dump_apis_with_deps("GC", &analyzed_apis);
                // And finally pass them to the code gen phases, which outputs
                // code suitable for cxx to consume.
                let cpp = CppCodeGenerator::generate_cpp_code(
                    inclusions,
                    &analyzed_apis,
                    self.config,
                    cpp_codegen_options,
                )?;
                let rs = RsCodeGenerator::generate_rs_code(
                    analyzed_apis,
                    self.include_list,
                    bindgen_mod,
                    self.config,
                );
                Ok(CodegenResults { rs, cpp })
            }
        }
    }
}
<|endoftext|>


<commit_before>//! # C API for rav1e
//!
//! [rav1e](https://github.com/xiph/rav1e/) is an [AV1](https://aomediacodec.github.io/av1-spec/)
//! encoder written in [Rust](https://rust-lang.org)
//!
//! This is the C-compatible API

extern crate rav1e;
extern crate libc;

use std::slice;
use std::sync::Arc;

use std::ffi::CStr;
use std::os::raw::c_char;
use std::os::raw::c_int;

use libc::size_t;
use libc::ptrdiff_t;

/// Raw video Frame
///
/// It can be allocated throught rav1e_frame_new(), populated using rav1e_frame_fill_plane()
/// and freed using rav1e_frame_unref().
pub struct Frame(Arc<rav1e::Frame>);

type EncoderStatus=rav1e::EncoderStatus;

/// Encoder configuration
///
/// Instantiate it using rav1e_config_default() and fine-tune it using
/// rav1e_config_parse().
///
/// Use rav1e_config_unref() to free its memory.
pub struct Config {
    cfg: rav1e::Config,
    last_err: Option<EncoderStatus>,
}

/// Encoder context
///
/// Contains the encoding state, it is created by rav1e_context_new() using an
/// Encoder configuration.
///
/// Use rav1e_context_unref() to free its memory.
pub struct Context {
    ctx: rav1e::Context,
    last_err: Option<EncoderStatus>,
}

type FrameType = rav1e::FrameType;

/// Encoded Packet
///
/// The encoded packets are retrieved using rav1e_receive_packet().
///
/// Use rav1e_packet_unref() to free its memory.
#[repr(C)]
pub struct Packet {
    /// Encoded data buffer
    pub data: *const u8,
    /// Encoded data buffer size
    pub len: size_t,
    /// Frame sequence number
    pub number: u64,
    /// Frame type
    pub frame_type: FrameType,
}

type ChromaSamplePosition=rav1e::ChromaSamplePosition;
type ChromaSampling=rav1e::ChromaSampling;
type MatrixCoefficients=rav1e::MatrixCoefficients;
type ColorPrimaries=rav1e::ColorPrimaries;
type TransferCharacteristics=rav1e::TransferCharacteristics;
type Rational=rav1e::Rational;

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_default(
    width: u32,
    height: u32,
    bit_depth: u8,
    chroma_sampling: ChromaSampling,
    chroma_sample_position: ChromaSamplePosition,
    time_base: Rational,
) -> *mut Config {
    let enc = rav1e::EncoderConfig{
      width : width as usize,
      height: height as usize,
      bit_depth: bit_depth as usize,
      chroma_sampling,
      chroma_sample_position,
      time_base,
      ..Default::default()
    };
    let cfg = rav1e::Config {
        enc
    };

    let c = Box::new(Config {
        cfg,
        last_err: None,
    });

    Box::into_raw(c)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_color_description(cfg: *mut Config,
                                                            matrix: MatrixCoefficients,
                                                            primaries: ColorPrimaries,
                                                            transfer: TransferCharacteristics)
{
    (*cfg).cfg.enc.color_description = Some(rav1e::ColorDescription {
        matrix_coefficients: matrix,
        color_primaries: primaries,
        transfer_characteristics: transfer,
    });
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_unref(cfg: *mut Config) {
    let _ = Box::from_raw(cfg);
}

/// Set a configuration parameter using its key and value
///
/// Available keys and values
/// - "quantizer": 0-255, default 100
/// - "speed": 0-10, default 3
/// - "tune": "psnr"-"psychovisual", default "psnr"
///
/// Returns a negative value on non-zero.
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_parse(
    cfg: *mut Config,
    key: *const c_char,
    value: *const c_char,
) -> c_int {
    let key = CStr::from_ptr(key).to_string_lossy();
    let value = CStr::from_ptr(value).to_string_lossy();

    (*cfg)
        .cfg
        .parse(&key, &value)
        .map(|_v| {
            (*cfg).last_err = None;
            0
        }).map_err(|e| (*cfg).last_err = Some(e))
        .unwrap_or(-1)
}

/// Generate a new encoding context from a populated encoder configuration
///
/// Multiple contexts can be generated through it.
#[no_mangle]
pub unsafe extern "C" fn rav1e_context_new(cfg: *const Config) -> *mut Context {
    let ctx = Context {
        ctx: (*cfg).cfg.new_context(),
        last_err: None,
    };

    Box::into_raw(Box::new(ctx))
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_context_unref(ctx: *mut Context) {
    let _ = Box::from_raw(ctx);
}

/// Produce a new frame from the encoding context
///
/// It must be populated using rav1e_frame_fill_plane().
///
/// The frame is reference counted and must be released passing it to rav1e_frame_unref(),
/// see rav1e_send_frame().
#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_new(ctx: *const Context) -> *mut Frame {
    let f = (*ctx).ctx.new_frame();
    let frame = Box::new(Frame(f));

    Box::into_raw(frame)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_unref(frame: *mut Frame) {
    let _ = Arc::from_raw(frame);
}

/// Send the frame for encoding
///
/// The function increases the frame internal reference count and it can be passed multiple
/// times to different rav1e_send_frame().
#[no_mangle]
pub unsafe extern "C" fn rav1e_send_frame(ctx: *mut Context, frame: *const Frame) -> c_int {
    let frame = if frame.is_null() {
        None
    } else {
        Some((*frame).0.clone())
    };

    (*ctx)
        .ctx
        .send_frame(frame)
        .map(|_v| {
            (*ctx).last_err = None;
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

/// Receive encoded data
#[no_mangle]
pub unsafe extern "C" fn rav1e_receive_packet(
    ctx: *mut Context,
    pkt: *mut *mut Packet,
) -> c_int {
    (*ctx)
        .ctx
        .receive_packet()
        .map(|p| {
            (*ctx).last_err = None;
            let packet = Packet {
                data: p.data.as_ptr(),
                len: p.data.len(),
                number: p.number,
                frame_type: p.frame_type,
            };
            *pkt = Box::into_raw(Box::new(packet));
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern fn rav1e_packet_unref(pkt: *mut Packet) {
    let _ = Box::from_raw(pkt);
}

/// Produce a sequence header matching the current encoding context
///
/// Its format is compatible with the AV1 Matroska and ISOBMFF specification.
///
/// Use rav1e_container_sequence_header_unref() to free it.
#[no_mangle]
pub unsafe extern fn rav1e_container_sequence_header(ctx: *mut Context, buf_size: *mut size_t) -> *mut u8 {
    let buf = (*ctx).ctx.container_sequence_header();

    *buf_size = buf.len();
    Box::into_raw(buf.into_boxed_slice()) as *mut u8
}

#[no_mangle]
pub unsafe extern fn rav1e_container_sequence_header_unref(sequence: *mut u8) {
    let _ = Box::from_raw(sequence);
}

/// Fill a frame plane
///
/// Currently the frame contains 3 planes, the first is luminance followed by
/// chrominance.
///
/// The data is copied and this function has to be called for each plane.
///
/// frame: A frame provided by rav1e_frame_new()
/// plane: The index of the plane starting from 0
/// data: The data to be copied
/// data_len: Lenght of the buffer
/// stride: Plane line in bytes, including padding
/// bytewidth: Number of bytes per component, either 1 or 2
#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_fill_plane(
    frame: *mut Frame,
    plane: c_int,
    data: *const u8,
    data_len: size_t,
    stride: ptrdiff_t,
    bytewidth: c_int,
) {
    let input = Arc::get_mut(&mut (*frame).0).unwrap();
    let data_slice = slice::from_raw_parts(data, data_len as usize);

    input.planes[plane as usize].copy_from_raw_u8(data_slice, stride as usize, bytewidth as usize);
}
<commit_msg>crav1e: Add wrappers for content_light and mastering_display<commit_after>//! # C API for rav1e
//!
//! [rav1e](https://github.com/xiph/rav1e/) is an [AV1](https://aomediacodec.github.io/av1-spec/)
//! encoder written in [Rust](https://rust-lang.org)
//!
//! This is the C-compatible API

extern crate rav1e;
extern crate libc;

use std::slice;
use std::sync::Arc;

use std::ffi::CStr;
use std::os::raw::c_char;
use std::os::raw::c_int;

use libc::size_t;
use libc::ptrdiff_t;

/// Raw video Frame
///
/// It can be allocated throught rav1e_frame_new(), populated using rav1e_frame_fill_plane()
/// and freed using rav1e_frame_unref().
pub struct Frame(Arc<rav1e::Frame>);

type EncoderStatus=rav1e::EncoderStatus;

/// Encoder configuration
///
/// Instantiate it using rav1e_config_default() and fine-tune it using
/// rav1e_config_parse().
///
/// Use rav1e_config_unref() to free its memory.
pub struct Config {
    cfg: rav1e::Config,
    last_err: Option<EncoderStatus>,
}

/// Encoder context
///
/// Contains the encoding state, it is created by rav1e_context_new() using an
/// Encoder configuration.
///
/// Use rav1e_context_unref() to free its memory.
pub struct Context {
    ctx: rav1e::Context,
    last_err: Option<EncoderStatus>,
}

type FrameType = rav1e::FrameType;

/// Encoded Packet
///
/// The encoded packets are retrieved using rav1e_receive_packet().
///
/// Use rav1e_packet_unref() to free its memory.
#[repr(C)]
pub struct Packet {
    /// Encoded data buffer
    pub data: *const u8,
    /// Encoded data buffer size
    pub len: size_t,
    /// Frame sequence number
    pub number: u64,
    /// Frame type
    pub frame_type: FrameType,
}

type ChromaSamplePosition=rav1e::ChromaSamplePosition;
type ChromaSampling=rav1e::ChromaSampling;
type MatrixCoefficients=rav1e::MatrixCoefficients;
type ColorPrimaries=rav1e::ColorPrimaries;
type TransferCharacteristics=rav1e::TransferCharacteristics;
type Rational=rav1e::Rational;

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_default(
    width: u32,
    height: u32,
    bit_depth: u8,
    chroma_sampling: ChromaSampling,
    chroma_sample_position: ChromaSamplePosition,
    time_base: Rational,
) -> *mut Config {
    let enc = rav1e::EncoderConfig{
      width : width as usize,
      height: height as usize,
      bit_depth: bit_depth as usize,
      chroma_sampling,
      chroma_sample_position,
      time_base,
      ..Default::default()
    };
    let cfg = rav1e::Config {
        enc
    };

    let c = Box::new(Config {
        cfg,
        last_err: None,
    });

    Box::into_raw(c)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_color_description(cfg: *mut Config,
                                                            matrix: MatrixCoefficients,
                                                            primaries: ColorPrimaries,
                                                            transfer: TransferCharacteristics)
{
    (*cfg).cfg.enc.color_description = Some(rav1e::ColorDescription {
        matrix_coefficients: matrix,
        color_primaries: primaries,
        transfer_characteristics: transfer,
    });
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_content_light(cfg: *mut Config,
                                                        max_content_light_level: u16,
                                                        max_frame_average_light_level: u16)
{
    (*cfg).cfg.enc.content_light = Some(rav1e::ContentLight {
        max_content_light_level,
        max_frame_average_light_level,
    });
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_mastering_display(cfg: *mut Config,
                                                            primaries: [rav1e::Point; 3],
                                                            white_point: rav1e::Point,
                                                            max_luminance: u32,
                                                            min_luminance: u32)
{
    (*cfg).cfg.enc.mastering_display = Some(rav1e::MasteringDisplay {
        primaries,
        white_point,
        max_luminance,
        min_luminance,
    });
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_unref(cfg: *mut Config) {
    let _ = Box::from_raw(cfg);
}

/// Set a configuration parameter using its key and value
///
/// Available keys and values
/// - "quantizer": 0-255, default 100
/// - "speed": 0-10, default 3
/// - "tune": "psnr"-"psychovisual", default "psnr"
///
/// Returns a negative value on non-zero.
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_parse(
    cfg: *mut Config,
    key: *const c_char,
    value: *const c_char,
) -> c_int {
    let key = CStr::from_ptr(key).to_string_lossy();
    let value = CStr::from_ptr(value).to_string_lossy();

    (*cfg)
        .cfg
        .parse(&key, &value)
        .map(|_v| {
            (*cfg).last_err = None;
            0
        }).map_err(|e| (*cfg).last_err = Some(e))
        .unwrap_or(-1)
}

/// Generate a new encoding context from a populated encoder configuration
///
/// Multiple contexts can be generated through it.
#[no_mangle]
pub unsafe extern "C" fn rav1e_context_new(cfg: *const Config) -> *mut Context {
    let ctx = Context {
        ctx: (*cfg).cfg.new_context(),
        last_err: None,
    };

    Box::into_raw(Box::new(ctx))
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_context_unref(ctx: *mut Context) {
    let _ = Box::from_raw(ctx);
}

/// Produce a new frame from the encoding context
///
/// It must be populated using rav1e_frame_fill_plane().
///
/// The frame is reference counted and must be released passing it to rav1e_frame_unref(),
/// see rav1e_send_frame().
#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_new(ctx: *const Context) -> *mut Frame {
    let f = (*ctx).ctx.new_frame();
    let frame = Box::new(Frame(f));

    Box::into_raw(frame)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_unref(frame: *mut Frame) {
    let _ = Arc::from_raw(frame);
}

/// Send the frame for encoding
///
/// The function increases the frame internal reference count and it can be passed multiple
/// times to different rav1e_send_frame().
#[no_mangle]
pub unsafe extern "C" fn rav1e_send_frame(ctx: *mut Context, frame: *const Frame) -> c_int {
    let frame = if frame.is_null() {
        None
    } else {
        Some((*frame).0.clone())
    };

    (*ctx)
        .ctx
        .send_frame(frame)
        .map(|_v| {
            (*ctx).last_err = None;
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

/// Receive encoded data
#[no_mangle]
pub unsafe extern "C" fn rav1e_receive_packet(
    ctx: *mut Context,
    pkt: *mut *mut Packet,
) -> c_int {
    (*ctx)
        .ctx
        .receive_packet()
        .map(|p| {
            (*ctx).last_err = None;
            let packet = Packet {
                data: p.data.as_ptr(),
                len: p.data.len(),
                number: p.number,
                frame_type: p.frame_type,
            };
            *pkt = Box::into_raw(Box::new(packet));
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern fn rav1e_packet_unref(pkt: *mut Packet) {
    let _ = Box::from_raw(pkt);
}

/// Produce a sequence header matching the current encoding context
///
/// Its format is compatible with the AV1 Matroska and ISOBMFF specification.
///
/// Use rav1e_container_sequence_header_unref() to free it.
#[no_mangle]
pub unsafe extern fn rav1e_container_sequence_header(ctx: *mut Context, buf_size: *mut size_t) -> *mut u8 {
    let buf = (*ctx).ctx.container_sequence_header();

    *buf_size = buf.len();
    Box::into_raw(buf.into_boxed_slice()) as *mut u8
}

#[no_mangle]
pub unsafe extern fn rav1e_container_sequence_header_unref(sequence: *mut u8) {
    let _ = Box::from_raw(sequence);
}

/// Fill a frame plane
///
/// Currently the frame contains 3 planes, the first is luminance followed by
/// chrominance.
///
/// The data is copied and this function has to be called for each plane.
///
/// frame: A frame provided by rav1e_frame_new()
/// plane: The index of the plane starting from 0
/// data: The data to be copied
/// data_len: Lenght of the buffer
/// stride: Plane line in bytes, including padding
/// bytewidth: Number of bytes per component, either 1 or 2
#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_fill_plane(
    frame: *mut Frame,
    plane: c_int,
    data: *const u8,
    data_len: size_t,
    stride: ptrdiff_t,
    bytewidth: c_int,
) {
    let input = Arc::get_mut(&mut (*frame).0).unwrap();
    let data_slice = slice::from_raw_parts(data, data_len as usize);

    input.planes[plane as usize].copy_from_raw_u8(data_slice, stride as usize, bytewidth as usize);
}
<|endoftext|>


<commit_before>use ast::*;
use types::Type;
use typecheck::{Symtable, Exprtable};

pub struct Generator<'a> {
    tmp_counter: i32,
    symtable: &'a Symtable,
    exprtable: &'a Exprtable,
}

pub fn codegen(program: &Program, symtable: &Symtable, exprtable: &Exprtable) {
    let mut generator = Generator {
        tmp_counter: 0,
        symtable: symtable,
        exprtable: exprtable,
    };
    generator.codegen_program(program);
}

impl<'a> Generator<'a> {
    fn codegen_program(&mut self, program: &Program) {
        println!("#include <stdio.h>");
        println!("int main(void) {{");

        self.codegen_decls();
        self.codegen_stmts(&program.stmts);

        println!("}}");
    }

    fn codegen_decls(&mut self) {
        for (id, ty) in self.symtable {
            match *ty {
                Type::Int => { println!("int {};", id); }
                Type::Float => { println!("float {};", id); }
            }
        }
    }

    fn codegen_stmts(&mut self, stmts: &[Stmt]) {
        for stmt in stmts {
            self.codegen_stmt(&stmt);
        }
    }

    fn codegen_stmt(&mut self, stmt: &Stmt) {
        match *stmt {
            Stmt::Read(ref stmt_) => {
                match self.symtable.get(&stmt_.id) {
                    Some(ty) => { println!("scanf(\"%{}\", &{});", ty.format_letter(), stmt_.id); }
                    None => { println!("/* read error */"); }
                }
            }
            Stmt::Print(ref stmt_) => {
                let tmp = self.codegen_expr(&stmt_.expr);
                match self.exprtable.get(&stmt_.expr) {
                    Some(ty) => { println!("printf(\"%{}\\n\", {});", ty.format_letter(), tmp); }
                    None => { println!("/* read error */"); }
                }
            }
            Stmt::Assign(ref stmt_) => {
                let tmp = self.codegen_expr(&stmt_.expr);
                println!("{} = {};", stmt_.id, tmp);
            }
            Stmt::If(ref stmt_) => {
                let tmp = self.codegen_expr(&stmt_.expr);
                println!("if ({}) {{", tmp);
                self.codegen_stmts(&stmt_.then_stmts);
                println!("}} else {{");
                self.codegen_stmts(&stmt_.else_stmts);
                println!("}}");
            }
            Stmt::While(ref stmt_) => {
                let tmp = self.codegen_expr(&stmt_.expr);
                println!("while ({}) {{", tmp);
                self.codegen_stmts(&stmt_.stmts);
                println!("}}");
            }
        }
    }

    fn new_tmp(&mut self) -> String {
        self.tmp_counter += 1;
        let tmp = format!("tmp_{}", self.tmp_counter);
        tmp.to_string()
    }

    fn codegen_expr(&mut self, expr: &Expr) -> String {
        let tmp = self.new_tmp();
        let ty_str = match self.exprtable.get(expr) {
            Some(&Type::Int) => "int",
            Some(&Type::Float) => "float",
            None => "/* fail */",
        };
        match *expr {
            Expr::Int(ref expr_) => { println!("{} {} = {};", ty_str, tmp, expr_.value); }
            Expr::Float(ref expr_) => { println!("{} {} = {};", ty_str, tmp, expr_.value.0); }
            Expr::Id(ref expr_) => { return expr_.id.clone(); }
            Expr::Negate(ref expr_) => {
                let id1 = self.codegen_expr(&expr_.expr);
                println!("{} {} = -{};", ty_str, tmp, id1);
            }
            Expr::Binop(ref expr_) => {
                let op_char = match expr_.op {
                    Binop::Add => '+',
                    Binop::Sub => '-',
                    Binop::Mul => '*',
                    Binop::Div => '/',
                };
                let id1 = self.codegen_expr(&expr_.expr1);
                let id2 = self.codegen_expr(&expr_.expr2);
                println!("{} {} = {} {} {};", ty_str, tmp, id1, op_char, id2);
            }
        }
        tmp
    }
}
<commit_msg>Add ExprReturn to reduce amount of temporaries<commit_after>use std::fmt;
use std::fmt::Display;

use ast::*;
use types::Type;
use typecheck::{Symtable, Exprtable};

enum ExprReturn {
    Id(String),
    Int(i64),
    Float(f64),
}

impl Display for ExprReturn {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ExprReturn::Id(ref s) => write!(f, "{}", s.clone()),
            ExprReturn::Int(n) => write!(f, "{}", n),
            ExprReturn::Float(fl) => write!(f, "{}", fl),
        }
    }
}

pub struct Generator<'a> {
    tmp_counter: i32,
    symtable: &'a Symtable,
    exprtable: &'a Exprtable,
}

pub fn codegen(program: &Program, symtable: &Symtable, exprtable: &Exprtable) {
    let mut generator = Generator {
        tmp_counter: 0,
        symtable: symtable,
        exprtable: exprtable,
    };
    generator.codegen_program(program);
}

impl<'a> Generator<'a> {
    fn codegen_program(&mut self, program: &Program) {
        println!("#include <stdio.h>");
        println!("int main(void) {{");

        self.codegen_decls();
        self.codegen_stmts(&program.stmts);

        println!("}}");
    }

    fn codegen_decls(&mut self) {
        for (id, ty) in self.symtable {
            match *ty {
                Type::Int => { println!("int {};", id); }
                Type::Float => { println!("float {};", id); }
            }
        }
    }

    fn codegen_stmts(&mut self, stmts: &[Stmt]) {
        for stmt in stmts {
            self.codegen_stmt(&stmt);
        }
    }

    fn codegen_stmt(&mut self, stmt: &Stmt) {
        match *stmt {
            Stmt::Read(ref stmt_) => {
                match self.symtable.get(&stmt_.id) {
                    Some(ty) => { println!("scanf(\"%{}\", &{});", ty.format_letter(), stmt_.id); }
                    None => { println!("/* read error */"); }
                }
            }
            Stmt::Print(ref stmt_) => {
                let expr_ret = self.codegen_expr(&stmt_.expr);
                match self.exprtable.get(&stmt_.expr) {
                    Some(ty) => { println!("printf(\"%{}\\n\", {});", ty.format_letter(), expr_ret); }
                    None => { println!("/* read error */"); }
                }
            }
            Stmt::Assign(ref stmt_) => {
                let expr_ret = self.codegen_expr(&stmt_.expr);
                println!("{} = {};", stmt_.id, expr_ret);
            }
            Stmt::If(ref stmt_) => {
                let expr_ret = self.codegen_expr(&stmt_.expr);
                println!("if ({}) {{", expr_ret);
                self.codegen_stmts(&stmt_.then_stmts);
                println!("}} else {{");
                self.codegen_stmts(&stmt_.else_stmts);
                println!("}}");
            }
            Stmt::While(ref stmt_) => {
                let expr_ret = self.codegen_expr(&stmt_.expr);
                println!("while ({}) {{", expr_ret);
                self.codegen_stmts(&stmt_.stmts);
                println!("}}");
            }
        }
    }

    fn new_tmp(&mut self) -> String {
        self.tmp_counter += 1;
        let tmp = format!("tmp_{}", self.tmp_counter);
        tmp.to_string()
    }

    fn codegen_expr(&mut self, expr: &Expr) -> ExprReturn {
        let ty_str = match self.exprtable.get(expr) {
            Some(&Type::Int) => "int",
            Some(&Type::Float) => "float",
            None => "/* fail */",
        };

        match *expr {
            Expr::Int(ref expr_) => { ExprReturn::Int(expr_.value) }
            Expr::Float(ref expr_) => { ExprReturn::Float(expr_.value.0) }
            Expr::Id(ref expr_) => { ExprReturn::Id(expr_.id.clone()) }
            Expr::Negate(ref expr_) => {
                let tmp = self.new_tmp();
                let expr_ret = self.codegen_expr(&expr_.expr);
                println!("{} {} = -{};", ty_str, tmp, expr_ret);
                ExprReturn::Id(tmp)
            }
            Expr::Binop(ref expr_) => {
                let op_char = match expr_.op {
                    Binop::Add => '+',
                    Binop::Sub => '-',
                    Binop::Mul => '*',
                    Binop::Div => '/',
                };
                let tmp = self.new_tmp();
                let expr_ret1 = self.codegen_expr(&expr_.expr1);
                let expr_ret2 = self.codegen_expr(&expr_.expr2);
                println!("{} {} = {} {} {};", ty_str, tmp, expr_ret1, op_char, expr_ret2);
                ExprReturn::Id(tmp)
            }
        }
    }
}
<|endoftext|>


<commit_before>
// FIXME: There is so many PathBuf's in this module
//        Conver them to Path

use std::io::prelude::*;
use std::io;
use std::path::{Path, PathBuf};
use std::fs;

use regex::Regex;

/// Copies files from source directory to destination directory.
pub fn copy_dir<P: AsRef<Path>>(source: P,
                                destination: P) -> io::Result<()> {
    copy_files_and_handle_html(source.as_ref().to_path_buf(),
    destination.as_ref().to_path_buf(),
    false, "")
}

/// Copies documentation from a crate's target directory to destination.
///
/// Target directory must have doc directory.
///
/// This function is designed to avoid file duplications. It is using rustc version string
/// to rename common files (css files, jquery.js, playpen.js, main.js etc.) in a standard rustdoc.
pub fn copy_doc_dir<P: AsRef<Path>>(target: P,
                                    destination: P,
                                    rustc_version: &str) -> io::Result<()> {
    let source = PathBuf::from(target.as_ref()).join("doc");
    copy_files_and_handle_html(source,
                               destination.as_ref().to_path_buf(),
                               true,
                               rustc_version)
}

fn copy_files_and_handle_html(source: PathBuf,
                              destination: PathBuf,
                              handle_html: bool,
                              rustc_version: &str) -> io::Result<()> {

    // Make sure destination directory is exists
    if !destination.exists() {
        try!(fs::create_dir_all(&destination));
    }

    // Avoid copying duplicated files
    let dup_regex = Regex::new(r"(\.lock|\.txt|\.woff|jquery\.js|playpen\.js|main\.js|\.css)$")
        .unwrap();

    for file in try!(source.read_dir()) {

        let file = try!(file);
        let mut destination_full_path = PathBuf::from(&destination);
        destination_full_path.push(file.file_name());

        let metadata = try!(file.metadata());

        if metadata.is_dir() {
            try!(fs::create_dir_all(&destination_full_path));
            try!(copy_files_and_handle_html(file.path(), destination_full_path, handle_html,
            &rustc_version));
        } else if handle_html && file.file_name().into_string().unwrap().ends_with(".html") {
            try!(copy_html(&file.path(), &destination_full_path, rustc_version));
        } else if handle_html && dup_regex.is_match(&file.file_name().into_string().unwrap()[..]) {
            continue;
        } else {
            try!(fs::copy(&file.path(), &destination_full_path));
        }

    }
    Ok(())
}

fn copy_html(source: &PathBuf,
             destination: &PathBuf,
             rustc_version: &str) -> io::Result<()> {

    let source_file = try!(fs::File::open(source));
    let mut destination_file = try!(fs::OpenOptions::new()
                                    .write(true).create(true).open(destination));

    let reader = io::BufReader::new(source_file);

    let replace_regex = Regex::new(r#"(href|src)="(.*)(main|jquery|rustdoc)\.(css|js)""#).unwrap();
    let replace_str = format!("$1=\"../../$2$3-{}.$4\"", rustc_version);

    for line in reader.lines() {
        let mut line = try!(line);

        // replace css links
        line = replace_regex.replace_all(&line[..], &replace_str[..]);

        try!(destination_file.write(line.as_bytes()));
        // need to write consumed newline
        try!(destination_file.write(&['\n' as u8]));
    }

    Ok(())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    extern crate tempdir;
    use std::fs;
    use std::path::Path;
    use super::*;

    #[test]
    #[ignore]
    fn test_copy_dir() {
        let destination = tempdir::TempDir::new("cratesfyi").unwrap();

        // lets try to copy a src directory to tempdir
        let res = copy_dir(Path::new("src"), destination.path());
        // remove temp dir
        fs::remove_dir_all(destination.path()).unwrap();

        assert!(res.is_ok());
    }

    #[test]
    #[ignore]
    fn test_copy_doc_dir() {
        // lets build documentation of rand crate
        use ::build_doc;
        let pkg = build_doc("rand", None).unwrap();

        let pkg_dir = format!("rand-{}", pkg.manifest().version());
        let target = Path::new(&pkg_dir);
        let destination = tempdir::TempDir::new("cratesfyi").unwrap();
        let res = copy_doc_dir(target,
                               destination.path(),
                               "UNKNOWN");

        // remove build and temp dir
        fs::remove_dir_all(target).unwrap();
        fs::remove_dir_all(destination.path()).unwrap();

        assert!(res.is_ok());
    }
}
<commit_msg>rustfmt and fix playpen.js<commit_after>
// FIXME: There is so many PathBuf's in this module
//        Conver them to Path

use std::io::prelude::*;
use std::io;
use std::path::{Path, PathBuf};
use std::fs;

use regex::Regex;

/// Copies files from source directory to destination directory.
pub fn copy_dir<P: AsRef<Path>>(source: P, destination: P) -> io::Result<()> {
    copy_files_and_handle_html(source.as_ref().to_path_buf(),
                               destination.as_ref().to_path_buf(),
                               false,
                               "")
}

/// Copies documentation from a crate's target directory to destination.
///
/// Target directory must have doc directory.
///
/// This function is designed to avoid file duplications. It is using rustc version string
/// to rename common files (css files, jquery.js, playpen.js, main.js etc.) in a standard rustdoc.
pub fn copy_doc_dir<P: AsRef<Path>>(target: P,
                                    destination: P,
                                    rustc_version: &str)
                                    -> io::Result<()> {
    let source = PathBuf::from(target.as_ref()).join("doc");
    copy_files_and_handle_html(source,
                               destination.as_ref().to_path_buf(),
                               true,
                               rustc_version)
}

fn copy_files_and_handle_html(source: PathBuf,
                              destination: PathBuf,
                              handle_html: bool,
                              rustc_version: &str)
                              -> io::Result<()> {

    // Make sure destination directory is exists
    if !destination.exists() {
        try!(fs::create_dir_all(&destination));
    }

    // Avoid copying duplicated files
    let dup_regex = Regex::new(r"(\.lock|\.txt|\.woff|jquery\.js|playpen\.js|main\.js|\.css)$")
                        .unwrap();

    for file in try!(source.read_dir()) {

        let file = try!(file);
        let mut destination_full_path = PathBuf::from(&destination);
        destination_full_path.push(file.file_name());

        let metadata = try!(file.metadata());

        if metadata.is_dir() {
            try!(fs::create_dir_all(&destination_full_path));
            try!(copy_files_and_handle_html(file.path(),
                                            destination_full_path,
                                            handle_html,
                                            &rustc_version));
        } else if handle_html && file.file_name().into_string().unwrap().ends_with(".html") {
            try!(copy_html(&file.path(), &destination_full_path, rustc_version));
        } else if handle_html && dup_regex.is_match(&file.file_name().into_string().unwrap()[..]) {
            continue;
        } else {
            try!(fs::copy(&file.path(), &destination_full_path));
        }

    }
    Ok(())
}

fn copy_html(source: &PathBuf, destination: &PathBuf, rustc_version: &str) -> io::Result<()> {

    let source_file = try!(fs::File::open(source));
    let mut destination_file = try!(fs::OpenOptions::new()
                                        .write(true)
                                        .create(true)
                                        .open(destination));

    let reader = io::BufReader::new(source_file);

    let replace_regex = Regex::new(r#"(href|src)="(.*)(main|jquery|rustdoc|playpen)\.(css|js)""#)
                            .unwrap();
    let replace_str = format!("$1=\"../../$2$3-{}.$4\"", rustc_version);

    for line in reader.lines() {
        let mut line = try!(line);

        // replace css links
        line = replace_regex.replace_all(&line[..], &replace_str[..]);

        try!(destination_file.write(line.as_bytes()));
        // need to write consumed newline
        try!(destination_file.write(&['\n' as u8]));
    }

    Ok(())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    extern crate tempdir;
    use std::fs;
    use std::path::Path;
    use super::*;

    #[test]
    #[ignore]
    fn test_copy_dir() {
        let destination = tempdir::TempDir::new("cratesfyi").unwrap();

        // lets try to copy a src directory to tempdir
        let res = copy_dir(Path::new("src"), destination.path());
        // remove temp dir
        fs::remove_dir_all(destination.path()).unwrap();

        assert!(res.is_ok());
    }

    #[test]
    #[ignore]
    fn test_copy_doc_dir() {
        // lets build documentation of rand crate
        use build_doc;
        let pkg = build_doc("rand", None).unwrap();

        let pkg_dir = format!("rand-{}", pkg.manifest().version());
        let target = Path::new(&pkg_dir);
        let destination = tempdir::TempDir::new("cratesfyi").unwrap();
        let res = copy_doc_dir(target, destination.path(), "UNKNOWN");

        // remove build and temp dir
        fs::remove_dir_all(target).unwrap();
        fs::remove_dir_all(destination.path()).unwrap();

        assert!(res.is_ok());
    }
}
<|endoftext|>


<commit_before>/*!
General Admin Commands
*/

use std::env;
use std::fs;
use std::path::PathBuf;
use std::io::Write;
use clap::ArgMatches;

use errors::*;

fn default_static_root() -> PathBuf {
    let mut root = env::current_dir().expect("Failed to get the current directory");
    root.push("static/badges");
    root
}

/// Print a message and require y/n confirmation
fn confirm(msg: &str) -> Result<()> {
    print!("{}", msg);
    ::std::io::stdout().flush().expect("Error flushing stdout");
    let mut input = String::new();
    let stdin = ::std::io::stdin();
    stdin.read_line(&mut input).expect("Error reading stdin");
    if input.trim().to_lowercase() == "y" { return Ok(()) }
    Err(Error::Msg("Unable to confirm...".to_string()))
}

fn clear_cached_files(no_confirm: bool, dir: &str) -> Result<()> {
    let static_root = if dir.is_empty() { default_static_root() } else { PathBuf::from(dir) };
    if !no_confirm {
        confirm(&format!("** Delete everything in {:?}? (y/n) > ", &static_root))?;
    }
    let read_dir = fs::read_dir(&static_root)
        .map_err(|e| Error::IoErrorMsg(e, format!("Unable to read `STATIC_ROOT` dir: {:?} - make sure you run this from the project root", &static_root)))?;
    for entry in read_dir {
        if let Ok(entry) = entry {
            let path = entry.path();

            if path.is_dir() {
                fs::remove_dir_all(path)?;
            } else {
                fs::remove_file(path)?;
            }
        };
    }
    println!("[badge-cache] [admin] - cleaned out cached badges in {:?}", &static_root);
    Ok(())
}

pub fn handle(matches: &ArgMatches) -> Result<()> {
    let no_confirm = matches.is_present("no-confirm");
    if let Some(dir) = matches.value_of("badge-dir") {
        clear_cached_files(no_confirm, &dir)?;
        return Ok(())
    }
    Ok(())
}
<commit_msg>admin | cache clearing<commit_after>/*!
General Admin Commands
*/

use std::env;
use std::fs;
use std::path::PathBuf;
use std::io::Write;
use std::ffi::OsStr;
use clap::ArgMatches;

use errors::*;

fn default_static_root() -> PathBuf {
    let mut root = env::current_dir().expect("Failed to get the current directory");
    root.push("static/badges");
    root
}

/// Print a message and require y/n confirmation
fn confirm(msg: &str) -> Result<()> {
    print!("{}", msg);
    ::std::io::stdout().flush().expect("Error flushing stdout");
    let mut input = String::new();
    let stdin = ::std::io::stdin();
    stdin.read_line(&mut input).expect("Error reading stdin");
    if input.trim().to_lowercase() == "y" { return Ok(()) }
    Err(Error::Msg("Unable to confirm...".to_string()))
}

const CACHE_KEEP: [&'static str; 1] = [".gitkeep"];

fn clear_cached_files(no_confirm: bool, dir: &str) -> Result<()> {
    let static_root = if dir.is_empty() { default_static_root() } else { PathBuf::from(dir) };
    if !no_confirm {
        confirm(&format!("** Delete everything in {:?}? (y/n) > ", &static_root))?;
    }
    let read_dir = fs::read_dir(&static_root)
        .map_err(|e| Error::IoErrorMsg(e, format!("Unable to read `STATIC_ROOT` dir: {:?} - make sure you run this from the project root", &static_root)))?;

    let mut count = 0;
    for entry in read_dir {
        if let Ok(entry) = entry {
            let path = entry.path();

            if path.is_dir() {
                fs::remove_dir_all(path)?;
                continue;
            }

            if let Some(fname) = path.file_name().and_then(OsStr::to_str) {
                if CACHE_KEEP.contains(&fname) { continue; }
            }

            fs::remove_file(path)?;
            count += 1;
        };
    }

    println!("[badge-cache] [admin] - cleaned out {} cached badges in {:?}", count, &static_root);
    Ok(())
}

pub fn handle(matches: &ArgMatches) -> Result<()> {
    let no_confirm = matches.is_present("no-confirm");
    if let Some(dir) = matches.value_of("badge-dir") {
        clear_cached_files(no_confirm, &dir)?;
        return Ok(())
    }
    Ok(())
}
<|endoftext|>


<commit_before>use raw::*;
use handle::{ From, Temp };

pub trait Array {
    fn get_pthx(&self) -> PerlContext;
    fn get_raw_ptr(&self) -> *mut AV;

    fn fetch_raw(&self, idx: Size_t) -> *mut *mut SV {
        unsafe { Perl_av_fetch(self.get_pthx(), self.get_raw_ptr(), idx, 0) }
    }

    fn fetch<T>(&self, idx: Size_t) -> T where T: From<Option<Temp<SV>>> {
        let svpp = self.fetch_raw(idx);
        let temp = if svpp.is_null() {
            None
        } else {
            Some(Temp::new(self.get_pthx(), unsafe{ *svpp }))
        };
        T::from(temp)
    }
}
<commit_msg>Array::fetch() always returns Option.<commit_after>use raw::*;
use handle::{ From, Temp };

pub trait Array {
    fn get_pthx(&self) -> PerlContext;
    fn get_raw_ptr(&self) -> *mut AV;

    fn fetch_raw(&self, idx: Size_t) -> *mut *mut SV {
        unsafe { Perl_av_fetch(self.get_pthx(), self.get_raw_ptr(), idx, 0) }
    }

    fn fetch<T>(&self, idx: Size_t) -> Option<T> where T: From<Temp<SV>> {
        let svpp = self.fetch_raw(idx);
        if svpp.is_null() {
            None
        } else {
            let temp = Temp::new(self.get_pthx(), unsafe{ *svpp });
            Some(T::from(temp))
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
#[cfg(feature="unstable")]
use std::ascii::AsciiExt;

use AsciiCast;

/// Datatype to hold one ascii character. It wraps a `u8`, with the highest bit always zero.
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Copy)]
pub struct Ascii { chr: u8 }

impl Ascii {
    /// Constructs an Ascii character from a `char`.
    ///
    /// # Failure
    ///
    /// Returns `Err(())` if the character can't be ascii encoded.
    ///
    /// # Example
    /// ```
    /// # use ascii::Ascii;
    /// let a = Ascii::from('g').unwrap();
    /// assert_eq!(a.as_char(), 'g');
    /// ```
    #[inline]
    pub fn from(ch: char) -> Result<Ascii, ()> {
        if ch as u32 <= 0x7F {
            return Ok( Ascii { chr: ch as u8 });
        }
        Err(())
    }

    /// Constructs an Ascii character from a `u8`.
    ///
    /// # Failure
    ///
    /// Returns `Err(())` if the character can't be ascii encoded.
    ///
    /// # Example
    ///
    /// ```
    /// # use ascii::Ascii;
    /// let a = Ascii::from_byte(65).unwrap();
    /// assert_eq!(a.as_char(), 'A');
    /// ```
    #[inline]
    pub fn from_byte(ch: u8) -> Result<Ascii, ()> {
        if ch <= 0x7F {
            return Ok( Ascii { chr: ch });
        }
        Err(())
    }

    /// Converts an ascii character into a `u8`.
    #[inline]
    pub fn as_byte(&self) -> u8 {
        self.chr
    }

    /// Converts an ascii character into a `char`.
    #[inline]
    pub fn as_char(&self) -> char {
        self.chr as char
    }

    // the following methods are like ctype, and the implementation is inspired by musl

    /// Check if the character is a letter (a-z, A-Z)
    #[inline]
    pub fn is_alphabetic(&self) -> bool {
        (self.chr >= 0x41 && self.chr <= 0x5A) || (self.chr >= 0x61 && self.chr <= 0x7A)
    }

    /// Check if the character is a number (0-9)
    #[inline]
    pub fn is_digit(&self) -> bool {
        self.chr >= 0x30 && self.chr <= 0x39
    }

    /// Check if the character is a letter or number
    #[inline]
    pub fn is_alphanumeric(&self) -> bool {
        self.is_alphabetic() || self.is_digit()
    }

    /// Check if the character is a space or horizontal tab
    #[inline]
    pub fn is_blank(&self) -> bool {
        self.chr == b' ' || self.chr == b'\t'
    }

    /// Check if the character is a control character
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('\0'.to_ascii().unwrap().is_control(), true);
    /// assert_eq!('n'.to_ascii().unwrap().is_control(), false);
    /// assert_eq!(' '.to_ascii().unwrap().is_control(), false);
    /// assert_eq!('\n'.to_ascii().unwrap().is_control(), true);
    /// ```
    #[inline]
    pub fn is_control(&self) -> bool {
        self.chr < 0x20 || self.chr == 0x7F
    }

    /// Checks if the character is printable (except space)
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('n'.to_ascii().unwrap().is_graph(), true);
    /// assert_eq!(' '.to_ascii().unwrap().is_graph(), false);
    /// assert_eq!('\n'.to_ascii().unwrap().is_graph(), false);
    /// ```
    #[inline]
    pub fn is_graph(&self) -> bool {
        self.chr.wrapping_sub(0x21) < 0x5E
    }

    /// Checks if the character is printable (including space)
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('n'.to_ascii().unwrap().is_print(), true);
    /// assert_eq!(' '.to_ascii().unwrap().is_print(), true);
    /// assert_eq!('\n'.to_ascii().unwrap().is_print(), false);
    /// ```
    #[inline]
    pub fn is_print(&self) -> bool {
        self.chr.wrapping_sub(0x20) < 0x5F
    }

    /// Checks if the character is alphabetic and lowercase
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('a'.to_ascii().unwrap().is_lowercase(), true);
    /// assert_eq!('A'.to_ascii().unwrap().is_lowercase(), false);
    /// assert_eq!('@'.to_ascii().unwrap().is_lowercase(), false);
    /// ```
    #[inline]
    pub fn is_lowercase(&self) -> bool {
        self.chr.wrapping_sub(b'a') < 26
    }

    /// Checks if the character is alphabetic and uppercase
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('A'.to_ascii().unwrap().is_uppercase(), true);
    /// assert_eq!('a'.to_ascii().unwrap().is_uppercase(), false);
    /// assert_eq!('@'.to_ascii().unwrap().is_uppercase(), false);
    /// ```
    #[inline]
    pub fn is_uppercase(&self) -> bool {
        self.chr.wrapping_sub(b'A') < 26
    }

    /// Checks if the character is punctuation
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('n'.to_ascii().unwrap().is_punctuation(), false);
    /// assert_eq!(' '.to_ascii().unwrap().is_punctuation(), false);
    /// assert_eq!('_'.to_ascii().unwrap().is_punctuation(), true);
    /// assert_eq!('~'.to_ascii().unwrap().is_punctuation(), true);
    /// ```
    #[inline]
    pub fn is_punctuation(&self) -> bool {
        self.is_graph() && !self.is_alphanumeric()
    }

    /// Checks if the character is a valid hex digit
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('5'.to_ascii().unwrap().is_hex(), true);
    /// assert_eq!('a'.to_ascii().unwrap().is_hex(), true);
    /// assert_eq!('F'.to_ascii().unwrap().is_hex(), true);
    /// assert_eq!(32u8.to_ascii().unwrap().is_hex(), false);
    /// ```
    #[inline]
    pub fn is_hex(&self) -> bool {
        self.is_digit() || (self.chr | 32u8).wrapping_sub(b'a') < 6
    }
}

impl fmt::Display for Ascii {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_char().fmt(f)
    }
}

impl fmt::Debug for Ascii {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_char().fmt(f)
     }
}

#[cfg(feature = "unstable")]
impl AsciiExt for Ascii {
    type Owned = Ascii;

    #[inline]
    fn is_ascii(&self) -> bool {
        true
    }

    fn to_ascii_uppercase(&self) -> Ascii {
        Ascii{chr: self.chr.to_ascii_uppercase()}
    }

    fn to_ascii_lowercase(&self) -> Ascii {
        Ascii{chr: self.chr.to_ascii_lowercase()}
    }

    fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
        self.chr.eq_ignore_ascii_case(&other.chr)
    }

    #[inline]
    fn make_ascii_uppercase(&mut self) {
        self.chr.make_ascii_uppercase()
    }

    #[inline]
    fn make_ascii_lowercase(&mut self) {
        self.chr.make_ascii_lowercase()
    }
}

impl<'a> AsciiCast<'a> for u8 {
    type Target = Ascii;

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        Ascii{ chr: *self }
    }
}

impl<'a> AsciiCast<'a> for char {
    type Target = Ascii;

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        Ascii{ chr: *self as u8 }
    }
}

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

    #[test]
    fn to_ascii() {
        assert_eq!(65_u8.to_ascii(), Ok(Ascii { chr: 65_u8 }));
        assert_eq!(255_u8.to_ascii(), Err(()));

        assert_eq!('A'.to_ascii(), Ok(Ascii { chr: 65_u8 }));
        assert_eq!('λ'.to_ascii(), Err(()));
    }

    #[test]
    fn as_byte() {
        assert_eq!(65u8.to_ascii().unwrap().as_byte(), 65u8);
        assert_eq!('A'.to_ascii().unwrap().as_byte(), 65u8);
    }

    #[test]
    fn as_char() {
        assert_eq!(65u8.to_ascii().unwrap().as_char(), 'A');
        assert_eq!('A'.to_ascii().unwrap().as_char(), 'A');
    }

    #[test]
    fn is_digit() {
        assert!('0'.to_ascii().unwrap().is_digit());
        assert!('9'.to_ascii().unwrap().is_digit());
        assert!(!'/'.to_ascii().unwrap().is_digit());
        assert!(!':'.to_ascii().unwrap().is_digit());
    }

    #[test]
    fn is_control() {
        assert!(0x1f_u8.to_ascii().unwrap().is_control());
        assert!(0x7f_u8.to_ascii().unwrap().is_control());
        assert!(!' '.to_ascii().unwrap().is_control());
    }

    #[test]
    fn fmt_display_ascii() {
        let s = Ascii { chr: b't' };
        assert_eq!(format!("{}", s), "t".to_string());
    }

    #[test]
    fn fmt_debug_ascii() {
        let c = Ascii { chr: b't' };
        assert_eq!(format!("{:?}", c), "'t'".to_string());
    }
}
<commit_msg>Convert `Ascii` into an enum.<commit_after>use std::mem::transmute;
use std::fmt;
#[cfg(feature="unstable")]
use std::ascii::AsciiExt;

use AsciiCast;

#[allow(non_camel_case_types)]
/// Datatype to hold one ascii character. It wraps a `u8`, with the highest bit always zero.
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Copy)]
#[repr(u8)]
pub enum Ascii {
	/**`'\0'`*/NUL             =   0,
	/** [Start Of Heading](http://en.wikipedia.org/wiki/Start_of_Heading)
	 */        SOH             =   1,
	/** [Start Of teXt](http://en.wikipedia.org/wiki/Start_of_Text)
	 */        SOX             =   2,
	/** [End Of teXt](http://en.wikipedia.org/wiki/End-of-Text_character)
	 */        ETX             =   3,
	/** [End Of Transmission](http://en.wikipedia.org/wiki/End-of-Transmission_character)
	 */        EOT             =   4,
	/** [Enquiry](http://en.wikipedia.org/wiki/Enquiry_character)
	 */        ENQ             =   5,
	/** [Acknowledgement](http://en.wikipedia.org/wiki/Acknowledge_character)
	 */        ACK             =   6,
	/** [bell / alarm / audible](http://en.wikipedia.org/wiki/Bell_character)

	   '\a' is not supported by Rust.
	 */        Bell            =   7,
	/** [Backspace character](http://en.wikipedia.org/wiki/Backspace)

	   '\b' is not supported by Rust.
	 */        BackSpace       =   8,
	/**`'\t'`*/Tab             =   9,
	/**`'\n'`*/LineFeed        =  10,
	/** [Vertical tab](http://en.wikipedia.org/wiki/Vertical_Tab)

	   '\v' is not supported by Rust.
	 */        VT              =  11,
	/** [Form Feed](http://en.wikipedia.org/wiki/Form_Feed)

	   '\f' is not supported by Rust.
	 */        FF              =  12,
	/**`'\r'`*/CarriageReturn  =  13,
	/** [Shift In](http://en.wikipedia.org/wiki/Shift_Out_and_Shift_In_characters)
	 */        SI              =  14,
	/** [Shift Out](http://en.wikipedia.org/wiki/Shift_Out_and_Shift_In_characters)
	 */        SO              =  15,
	/** [Data Link Escape](http://en.wikipedia.org/wiki/Data_Link_Escape)
	 */        DLE             =  16,
	/** [Device control 1, often XON](http://en.wikipedia.org/wiki/Device_Control_1)
	 */        DC1             =  17,
	/** Device control 2
	 */        DC2             =  18,
	/** Device control 3, Often XOFF
	 */        DC3             =  19,
	/** Device control 4
	 */        DC4             =  20,
	/** [Negative Acknowledgement](http://en.wikipedia.org/wiki/Negative-acknowledge_character)
	 */        NAK             =  21,
	/** [Synchronous idle](http://en.wikipedia.org/wiki/Synchronous_Idle)
	 */        SYN             =  22,
	/** [End of Transmission Block](http://en.wikipedia.org/wiki/End-of-Transmission-Block_character)
	 */        ETB             =  23,
	/** [Cancel](http://en.wikipedia.org/wiki/Cancel_character)
	 */        CAN             =  24,
	/** [End of Medium](http://en.wikipedia.org/wiki/End_of_Medium)
	 */        EM              =  25,
	/** [Substitute](http://en.wikipedia.org/wiki/Substitute_character)
	 */        SUB             =  26,
	/** [Escape](http://en.wikipedia.org/wiki/Escape_character)

	   '\e' is not supported in Rust.
	 */        Escape          =  27,
	/** [File Separator](http://en.wikipedia.org/wiki/File_separator)
	 */        FS              =  28,
	/** [Group Separator](http://en.wikipedia.org/wiki/Group_separator)
	 */        GS              =  29,
	/** [Record Separator](http://en.wikipedia.org/wiki/Record_separator)
	 */        RS              =  30,
	/** [Unit Separator](http://en.wikipedia.org/wiki/Unit_separator)
	 */        US              =  31,
	/**`' '`*/ Space           =  32,
	/**`'!'`*/ Exclamation     =  33,
	/**`'"'`*/ Quotation       =  34,
	/**`'''`*/ Hash            =  35,
	/**`'$'`*/ Dollar          =  36,
	/**`'%'`*/ Percent         =  37,
	/**`'&'`*/ Ampersand       =  38,
	/**`'''`*/ Apostrophe      =  39,
	/**`'('`*/ ParenOpen       =  40,
	/**`')'`*/ ParenClose      =  41,
	/**`'*'`*/ Asterisk        =  42,
	/**`'+'`*/ Plus            =  43,
	/**`','`*/ Comma           =  44,
	/**`'-'`*/ Minus           =  45,
	/**`'.'`*/ Dot             =  46,
	/**`'/'`*/ Slash           =  47,
	/**`'0'`*/ _0              =  48,
	/**`'1'`*/ _1              =  49,
	/**`'2'`*/ _2              =  50,
	/**`'3'`*/ _3              =  51,
	/**`'4'`*/ _4              =  52,
	/**`'5'`*/ _5              =  53,
	/**`'6'`*/ _6              =  54,
	/**`'7'`*/ _7              =  55,
	/**`'8'`*/ _8              =  56,
	/**`'9'`*/ _9              =  57,
	/**`':'`*/ Colon           =  58,
	/**`';'`*/ SemiColon       =  59,
	/**`'<'`*/ LessThan        =  60,
	/**`'='`*/ Equal           =  61,
	/**`'>'`*/ GreaterThan     =  62,
	/**`'?'`*/ Question        =  63,
	/**`'@'`*/ At              =  64,
	/**`'A'`*/ A               =  65,
	/**`'B'`*/ B               =  66,
	/**`'C'`*/ C               =  67,
	/**`'D'`*/ D               =  68,
	/**`'E'`*/ E               =  69,
	/**`'F'`*/ F               =  70,
	/**`'G'`*/ G               =  71,
	/**`'H'`*/ H               =  72,
	/**`'I'`*/ I               =  73,
	/**`'J'`*/ J               =  74,
	/**`'K'`*/ K               =  75,
	/**`'L'`*/ L               =  76,
	/**`'M'`*/ M               =  77,
	/**`'N'`*/ N               =  78,
	/**`'O'`*/ O               =  79,
	/**`'P'`*/ P               =  80,
	/**`'Q'`*/ Q               =  81,
	/**`'R'`*/ R               =  82,
	/**`'S'`*/ S               =  83,
	/**`'T'`*/ T               =  84,
	/**`'U'`*/ U               =  85,
	/**`'V'`*/ V               =  86,
	/**`'W'`*/ W               =  87,
	/**`'X'`*/ X               =  88,
	/**`'Y'`*/ Y               =  89,
	/**`'Z'`*/ Z               =  90,
	/**`'['`*/ BracketOpen     =  91,
	/**`'\'`*/ BackSlash       =  92,
	/**`']'`*/ BracketClose    =  93,
	/**`'_'`*/ Caret           =  94,
	/**`'_'`*/ UnderScore      =  95,
	/**`'`'`*/ Grave           =  96,
	/**`'a'`*/ a               =  97,
	/**`'b'`*/ b               =  98,
	/**`'c'`*/ c               =  99,
	/**`'d'`*/ d               = 100,
	/**`'e'`*/ e               = 101,
	/**`'f'`*/ f               = 102,
	/**`'g'`*/ g               = 103,
	/**`'h'`*/ h               = 104,
	/**`'i'`*/ i               = 105,
	/**`'j'`*/ j               = 106,
	/**`'k'`*/ k               = 107,
	/**`'l'`*/ l               = 108,
	/**`'m'`*/ m               = 109,
	/**`'n'`*/ n               = 110,
	/**`'o'`*/ o               = 111,
	/**`'p'`*/ p               = 112,
	/**`'q'`*/ q               = 113,
	/**`'r'`*/ r               = 114,
	/**`'s'`*/ s               = 115,
	/**`'t'`*/ t               = 116,
	/**`'u'`*/ u               = 117,
	/**`'v'`*/ v               = 118,
	/**`'w'`*/ w               = 119,
	/**`'x'`*/ x               = 120,
	/**`'y'`*/ y               = 121,
	/**`'z'`*/ z               = 122,
	/**`'{'`*/ CurlyBraceOpen  = 123,
	/**`'|'`*/ VerticalBar     = 124,
	/**`'}'`*/ CurlyBraceClose = 125,
	/**`'~'`*/ Tilde           = 126,
	/**[Delete](http://en.wikipedia.org/wiki/Delete_character)
	 */        Delete          = 127,
}

impl Ascii {
    /// Constructs an Ascii character from a `char`.
    ///
    /// # Failure
    ///
    /// Returns `Err(())` if the character can't be ascii encoded.
    ///
    /// # Example
    /// ```
    /// # use ascii::Ascii;
    /// let a = Ascii::from('g').unwrap();
    /// assert_eq!(a.as_char(), 'g');
    /// ```
    #[inline]
    pub fn from(ch: char) -> Result<Ascii, ()> {
        if ch as u32 <= 0x7F {
            return Ok(unsafe{ ch.to_ascii_nocheck() });
        }
        Err(())
    }

    /// Constructs an Ascii character from a `u8`.
    ///
    /// # Failure
    ///
    /// Returns `Err(())` if the character can't be ascii encoded.
    ///
    /// # Example
    ///
    /// ```
    /// # use ascii::Ascii;
    /// let a = Ascii::from_byte(65).unwrap();
    /// assert_eq!(a.as_char(), 'A');
    /// ```
    #[inline]
    pub fn from_byte(ch: u8) -> Result<Ascii, ()> {
        if ch <= 0x7F {
            return Ok(unsafe{ ch.to_ascii_nocheck() });
        }
        Err(())
    }

    /// Converts an ascii character into a `u8`.
    #[inline]
    pub fn as_byte(&self) -> u8 {
        unsafe{ transmute(*self) }
    }

    /// Converts an ascii character into a `char`.
    #[inline]
    pub fn as_char(&self) -> char {
        self.as_byte() as char
    }

    // the following methods are like ctype, and the implementation is inspired by musl

    /// Check if the character is a letter (a-z, A-Z)
    #[inline]
    pub fn is_alphabetic(&self) -> bool {
        (self >= &Ascii::a && self <= &Ascii::z) ||
        (self >= &Ascii::A && self <= &Ascii::Z)
    }

    /// Check if the character is a number (0-9)
    #[inline]
    pub fn is_digit(&self) -> bool {
        self >= &Ascii::_0 && self <= &Ascii::_9
    }

    /// Check if the character is a letter or number
    #[inline]
    pub fn is_alphanumeric(&self) -> bool {
        self.is_alphabetic() || self.is_digit()
    }

    /// Check if the character is a space or horizontal tab
    #[inline]
    pub fn is_blank(&self) -> bool {
        *self == Ascii::Space || *self == Ascii::Tab
    }

    /// Check if the character is a control character
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('\0'.to_ascii().unwrap().is_control(), true);
    /// assert_eq!('n'.to_ascii().unwrap().is_control(), false);
    /// assert_eq!(' '.to_ascii().unwrap().is_control(), false);
    /// assert_eq!('\n'.to_ascii().unwrap().is_control(), true);
    /// ```
    #[inline]
    pub fn is_control(&self) -> bool {
        self.as_byte() < 0x20 || *self == Ascii::Delete
    }

    /// Checks if the character is printable (except space)
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('n'.to_ascii().unwrap().is_graph(), true);
    /// assert_eq!(' '.to_ascii().unwrap().is_graph(), false);
    /// assert_eq!('\n'.to_ascii().unwrap().is_graph(), false);
    /// ```
    #[inline]
    pub fn is_graph(&self) -> bool {
        self.as_byte().wrapping_sub(0x21) < 0x5E
    }

    /// Checks if the character is printable (including space)
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('n'.to_ascii().unwrap().is_print(), true);
    /// assert_eq!(' '.to_ascii().unwrap().is_print(), true);
    /// assert_eq!('\n'.to_ascii().unwrap().is_print(), false);
    /// ```
    #[inline]
    pub fn is_print(&self) -> bool {
        self.as_byte().wrapping_sub(0x20) < 0x5F
    }

    /// Checks if the character is alphabetic and lowercase
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('a'.to_ascii().unwrap().is_lowercase(), true);
    /// assert_eq!('A'.to_ascii().unwrap().is_lowercase(), false);
    /// assert_eq!('@'.to_ascii().unwrap().is_lowercase(), false);
    /// ```
    #[inline]
    pub fn is_lowercase(&self) -> bool {
        self.as_byte().wrapping_sub(b'a') < 26
    }

    /// Checks if the character is alphabetic and uppercase
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('A'.to_ascii().unwrap().is_uppercase(), true);
    /// assert_eq!('a'.to_ascii().unwrap().is_uppercase(), false);
    /// assert_eq!('@'.to_ascii().unwrap().is_uppercase(), false);
    /// ```
    #[inline]
    pub fn is_uppercase(&self) -> bool {
        self.as_byte().wrapping_sub(b'A') < 26
    }

    /// Checks if the character is punctuation
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('n'.to_ascii().unwrap().is_punctuation(), false);
    /// assert_eq!(' '.to_ascii().unwrap().is_punctuation(), false);
    /// assert_eq!('_'.to_ascii().unwrap().is_punctuation(), true);
    /// assert_eq!('~'.to_ascii().unwrap().is_punctuation(), true);
    /// ```
    #[inline]
    pub fn is_punctuation(&self) -> bool {
        self.is_graph() && !self.is_alphanumeric()
    }

    /// Checks if the character is a valid hex digit
    ///
    /// # Examples
    ///
    /// ```
    /// use ascii::AsciiCast;
    /// assert_eq!('5'.to_ascii().unwrap().is_hex(), true);
    /// assert_eq!('a'.to_ascii().unwrap().is_hex(), true);
    /// assert_eq!('F'.to_ascii().unwrap().is_hex(), true);
    /// assert_eq!(32u8.to_ascii().unwrap().is_hex(), false);
    /// ```
    #[inline]
    pub fn is_hex(&self) -> bool {
        self.is_digit() || (self.as_byte() | 32u8).wrapping_sub(b'a') < 6
    }
}

impl fmt::Display for Ascii {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_char().fmt(f)
    }
}

impl fmt::Debug for Ascii {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_char().fmt(f)
     }
}

#[cfg(feature = "unstable")]
impl AsciiExt for Ascii {
    type Owned = Ascii;

    #[inline]
    fn is_ascii(&self) -> bool {
        true
    }

    fn to_ascii_uppercase(&self) -> Ascii {
        unsafe{ self.as_byte().to_ascii_uppercase().to_ascii_nocheck() }
    }

    fn to_ascii_lowercase(&self) -> Ascii {
        unsafe{ self.as_byte().to_ascii_uppercase().to_ascii_nocheck() }
    }

    fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
        self.as_byte().eq_ignore_ascii_case(&other.as_byte())
    }

    #[inline]
    fn make_ascii_uppercase(&mut self) {
        *self = self.to_ascii_uppercase();
    }

    #[inline]
    fn make_ascii_lowercase(&mut self) {
        *self = self.to_ascii_lowercase();
    }
}

impl<'a> AsciiCast<'a> for u8 {
    type Target = Ascii;

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        transmute(*self)
    }
}

impl<'a> AsciiCast<'a> for char {
    type Target = Ascii;

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        (*self as u8).to_ascii_nocheck()
    }
}

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

    #[test]
    fn to_ascii() {
        assert_eq!(65_u8.to_ascii(), Ok(Ascii::A));
        assert_eq!(255_u8.to_ascii(), Err(()));

        assert_eq!('A'.to_ascii(), Ok(Ascii::A));
        assert_eq!('λ'.to_ascii(), Err(()));
    }

    #[test]
    fn as_byte() {
        assert_eq!(65u8.to_ascii().unwrap().as_byte(), 65u8);
        assert_eq!('A'.to_ascii().unwrap().as_byte(), 65u8);
    }

    #[test]
    fn as_char() {
        assert_eq!(65u8.to_ascii().unwrap().as_char(), 'A');
        assert_eq!('A'.to_ascii().unwrap().as_char(), 'A');
    }

    #[test]
    fn is_digit() {
        assert!('0'.to_ascii().unwrap().is_digit());
        assert!('9'.to_ascii().unwrap().is_digit());
        assert!(!'/'.to_ascii().unwrap().is_digit());
        assert!(!':'.to_ascii().unwrap().is_digit());
    }

    #[test]
    fn is_control() {
        assert!(0x1f_u8.to_ascii().unwrap().is_control());
        assert!(0x7f_u8.to_ascii().unwrap().is_control());
        assert!(!' '.to_ascii().unwrap().is_control());
    }

    #[test]
    fn fmt_display_ascii() {
        assert_eq!(format!("{}", Ascii::t), "t".to_string());
    }

    #[test]
    fn fmt_debug_ascii() {
        assert_eq!(format!("{:?}", Ascii::t), "'t'".to_string());
    }
}
<|endoftext|>


<commit_before>//! # `batch` - batches of files to marshal.
//!
//! A `Batch` represents a collection of files which are being marshalled and a stream of hashed
//! output objects. For a single `Batch`, there should correspond a set of valid marshalled files
//! and a stream of valid hashed objects produced from marshalling/hashing the batched files.
//!
//! Marshalling files in a `Batch` will spawn files to a specified threadpool provided by the
//! `Context`.

use std::fs::File;
use std::path::Path;

use futures::prelude::*;
use futures::sync::mpsc::{self, Sender, Receiver};
use futures_cpupool::CpuPool;
use memmap::{Mmap, Protection};

use BATCH_FUTURE_BUFFER_SIZE;
use arc_slice::{self, ArcSlice};
use errors::*;
use marshal::{Hashed, Hasher, ObjectHash, SmallRecord, Tree};
use split::{self, Chunked};
use trace::BatchTrace;

/// A batch of files being marshalled.
pub struct Batch<T: BatchTrace = ()> {
    trace: T,

    marshal_tx: Sender<Hashed>,
    marshal_rx: Receiver<Hashed>,

    marshal_pool: CpuPool,

    len: usize,
}

impl<T: BatchTrace> Batch<T> {
    pub fn with_trace(marshal_pool: CpuPool, trace: T) -> Self {
        let (marshal_tx, marshal_rx) = mpsc::channel(BATCH_FUTURE_BUFFER_SIZE);

        Batch {
            trace,

            marshal_tx,
            marshal_rx,

            marshal_pool,

            len: 0,
        }
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    ///
    /// * `file` - the file to read. *Must be opened with read permissions!*
    fn read(&mut self, file: &File) -> Result<ArcSlice> {
        Ok(arc_slice::mapped(Mmap::open(file, Protection::Read)?))
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    fn read_path<P: AsRef<Path>>(&mut self, path: P) -> Result<ArcSlice> {
        self.read(&File::open(path)?)
    }

    /// Chunk the file at the given path.
    pub fn chunk_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Chunked> {
        let slice = self.read_path(path)?;
        let mut trace = self.trace.on_split(slice.len() as u64);
        let chunked = split::chunk_with_trace(slice, &mut trace);

        Ok(chunked)
    }

    /// Marshal a chunked file into a tree of objects, returning the marshalled objects along with
    /// the hash of the root object.
    pub fn marshal_file(
        &mut self,
        chunked: Chunked,
    ) -> Box<Future<Item = ObjectHash, Error = Error> + Send> {
        let tree = Tree::load(chunked.to_vec().into_iter().map(SmallRecord::from));
        let tree_len = tree.total();

        self.len += tree_len;

        let marshal = tree.marshal(Hasher::with_trace(
            self.marshal_tx.clone(),
            self.trace.on_marshal(tree_len),
        ));

        Box::new(self.marshal_pool.spawn(marshal))
    }

    /// The total size of the batch, in chunks.
    pub fn len(&self) -> usize {
        self.len
    }

    /// Convert this `Batch` into a stream of `Hashed` objects.
    pub fn into_stream(self) -> Box<Stream<Item = Hashed, Error = Error> + Send> {
        Box::new(self.marshal_rx.map_err(|()| "Upstream error!".into()))
    }
}
<commit_msg>Microscopic cleanup<commit_after>//! # `batch` - batches of files to marshal.
//!
//! A `Batch` represents a collection of files which are being marshalled and a stream of hashed
//! output objects. For a single `Batch`, there should correspond a set of valid marshalled files
//! and a stream of valid hashed objects produced from marshalling/hashing the batched files.
//!
//! Marshalling files in a `Batch` will spawn files to a specified threadpool provided by the
//! `Context`.

use std::fs::File;
use std::path::Path;

use futures::prelude::*;
use futures::sync::mpsc::{self, Sender, Receiver};
use futures_cpupool::CpuPool;
use memmap::{Mmap, Protection};

use BATCH_FUTURE_BUFFER_SIZE;
use arc_slice::{self, ArcSlice};
use errors::*;
use marshal::{Hashed, Hasher, ObjectHash, SmallRecord, Tree};
use split::{self, Chunked};
use trace::BatchTrace;

/// A batch of files being marshalled.
pub struct Batch<T: BatchTrace = ()> {
    trace: T,

    marshal_tx: Sender<Hashed>,
    marshal_rx: Receiver<Hashed>,

    marshal_pool: CpuPool,

    len: usize,
}

impl<T: BatchTrace> Batch<T> {
    pub fn with_trace(marshal_pool: CpuPool, trace: T) -> Self {
        let (marshal_tx, marshal_rx) = mpsc::channel(BATCH_FUTURE_BUFFER_SIZE);

        Batch {
            trace,

            marshal_tx,
            marshal_rx,

            marshal_pool,

            len: 0,
        }
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    ///
    /// * `file` - the file to read. *Must be opened with read permissions!*
    fn read(&mut self, file: &File) -> Result<ArcSlice> {
        Ok(arc_slice::mapped(Mmap::open(file, Protection::Read)?))
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    fn read_path<P: AsRef<Path>>(&mut self, path: P) -> Result<ArcSlice> {
        self.read(&File::open(path)?)
    }

    /// Chunk the file at the given path.
    pub fn chunk_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Chunked> {
        let slice = self.read_path(path)?;
        let mut trace = self.trace.on_split(slice.len() as u64);
        let chunked = split::chunk_with_trace(slice, &mut trace);

        Ok(chunked)
    }

    /// Marshal a chunked file into a tree of objects, returning the marshalled objects along with
    /// the hash of the root object.
    pub fn marshal_file(
        &mut self,
        chunked: Chunked,
    ) -> Box<Future<Item = ObjectHash, Error = Error> + Send> {
        let tree = Tree::load(chunked.to_vec().into_iter().map(SmallRecord::from));

        self.len += tree.total();

        let marshal = tree.marshal(Hasher::with_trace(
            self.marshal_tx.clone(),
            self.trace.on_marshal(tree_len),
        ));

        Box::new(self.marshal_pool.spawn(marshal))
    }

    /// The total size of the batch, in chunks.
    pub fn len(&self) -> usize {
        self.len
    }

    /// Convert this `Batch` into a stream of `Hashed` objects.
    pub fn into_stream(self) -> Box<Stream<Item = Hashed, Error = Error> + Send> {
        Box::new(self.marshal_rx.map_err(|()| "Upstream error!".into()))
    }
}
<|endoftext|>


<commit_before>use nom;
use nom::IResult::*;

use helper::*;
use super::{Date,Time,DateTime};

use macros::{take_2_digits,take_4_digits};

named!(sign <&[u8], i32>, alt!(
        tag!("-") => { |_| -1 } |
        tag!("+") => { |_| 1 }
        )
    );

named!(positive_year  <&[u8], i32>, map!(call!(take_4_digits), buf_to_i32));
named!(pub year <&[u8], i32>, chain!(
        pref: opt!(sign) ~
        y:    positive_year
        ,
        || {
            pref.unwrap_or(1) * y
        }));

named!(pub month <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));
named!(pub day   <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));

named!(pub date <&[u8], Date>, chain!(
        y: year ~ tag!("-") ~ m: month ~ tag!("-") ~ d: day
        , || { Date{ year: y, month: m, day: d }
        }
        ));

named!(pub hour   <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));
named!(pub minute <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));
named!(pub second <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));

named!(pub time <&[u8], Time>, chain!(
        h: hour      ~
           tag!(":") ~
        m: minute    ~
        s: empty_or!(chain!(
                tag!(":") ~ s:second , || { s }))
        ,
        || { Time{ hour: h,
                   minute: m,
                   second: s.unwrap_or(0),
                   tz_offset: 0 }
           }
        ));

named!(timezone_hour <&[u8], i32>, chain!(
        s: sign ~
        h: hour ~
        m: empty_or!(
            chain!(
                tag!(":")? ~ m: minute , || { m }
            ))
        ,
        || { (s * (h as i32) * 3600) + (m.unwrap_or(0) * 60) as i32 }
        ));

named!(tag_z, tag!("Z"));
named!(timezone_utc <&[u8], i32>, map!(tag_z, |_| 0));
named!(timezone <&[u8], i32>, alt!(timezone_utc | timezone_hour));

named!(pub datetime <&[u8], DateTime>, chain!(
        d:   date      ~
             tag!("T") ~
        t:   time      ~
        tzo: empty_or!(call!(timezone))
        ,
        || {
            DateTime{
                date: d,
                time: t.set_tz(tzo.unwrap_or(0)),
            }
        }
        ));
<commit_msg>Combine tag and map into one simple parser<commit_after>use nom;
use nom::IResult::*;

use helper::*;
use super::{Date,Time,DateTime};

use macros::{take_2_digits,take_4_digits};

named!(sign <&[u8], i32>, alt!(
        tag!("-") => { |_| -1 } |
        tag!("+") => { |_| 1 }
        )
    );

named!(positive_year  <&[u8], i32>, map!(call!(take_4_digits), buf_to_i32));
named!(pub year <&[u8], i32>, chain!(
        pref: opt!(sign) ~
        y:    positive_year
        ,
        || {
            pref.unwrap_or(1) * y
        }));

named!(pub month <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));
named!(pub day   <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));

named!(pub date <&[u8], Date>, chain!(
        y: year ~ tag!("-") ~ m: month ~ tag!("-") ~ d: day
        , || { Date{ year: y, month: m, day: d }
        }
        ));

named!(pub hour   <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));
named!(pub minute <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));
named!(pub second <&[u8], u32>, map!(call!(take_2_digits), buf_to_u32));

named!(pub time <&[u8], Time>, chain!(
        h: hour      ~
           tag!(":") ~
        m: minute    ~
        s: empty_or!(chain!(
                tag!(":") ~ s:second , || { s }))
        ,
        || { Time{ hour: h,
                   minute: m,
                   second: s.unwrap_or(0),
                   tz_offset: 0 }
           }
        ));

named!(timezone_hour <&[u8], i32>, chain!(
        s: sign ~
        h: hour ~
        m: empty_or!(
            chain!(
                tag!(":")? ~ m: minute , || { m }
            ))
        ,
        || { (s * (h as i32) * 3600) + (m.unwrap_or(0) * 60) as i32 }
        ));

named!(timezone_utc <&[u8], i32>, map!(tag!("Z"), |_| 0));
named!(timezone <&[u8], i32>, alt!(timezone_utc | timezone_hour));

named!(pub datetime <&[u8], DateTime>, chain!(
        d:   date      ~
             tag!("T") ~
        t:   time      ~
        tzo: empty_or!(call!(timezone))
        ,
        || {
            DateTime{
                date: d,
                time: t.set_tz(tzo.unwrap_or(0)),
            }
        }
        ));
<|endoftext|>


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

#[cfg(feature = "snappy")]
const SNAPPY_VERSION: &'static str = "1.1.7";
const LEVELDB_VERSION: &'static str = "1.22";
/// Directory name within `$OUT_DIR` where the static libraries should be built.
const LIBDIR: &'static str = "lib";

#[cfg(feature = "snappy")]
fn build_snappy() -> PathBuf {
    println!("[snappy] Building");

    let outdir = env::var("OUT_DIR").unwrap();
    let libdir = Path::new(&outdir).join(LIBDIR);

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let dest_prefix =
        cmake::Config::new(Path::new("deps").join(format!("snappy-{}", SNAPPY_VERSION)))
            .define("BUILD_SHARED_LIBS", "OFF")
            .define("SNAPPY_BUILD_TESTS", "OFF")
            .define("HAVE_LIBZ", "OFF")
            .define("CMAKE_INSTALL_LIBDIR", &libdir)
            .build();

    assert_eq!(
        dest_prefix.join(LIBDIR),
        libdir,
        "CMake should build Snappy in provided LIBDIR"
    );
    println!("cargo:rustc-link-search=native={}", libdir.display());
    println!("cargo:rustc-link-lib=static=snappy");

    dest_prefix
}

fn build_leveldb(snappy_prefix: Option<PathBuf>) {
    println!("[leveldb] Building");

    let outdir = env::var("OUT_DIR").unwrap();
    let libdir = Path::new(&outdir).join(LIBDIR);

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let mut config =
        cmake::Config::new(Path::new("deps").join(format!("leveldb-{}", LEVELDB_VERSION)));
    config
        .define("LEVELDB_BUILD_TESTS", "OFF")
        .define("LEVELDB_BUILD_BENCHMARKS", "OFF")
        .define("CMAKE_INSTALL_LIBDIR", &libdir);
    if let Some(snappy_prefix) = snappy_prefix {
        #[cfg(target_env = "msvc")]
        let ldflags = format!("/LIBPATH:{}", snappy_prefix.join(LIBDIR).display());
        #[cfg(not(target_env = "msvc"))]
        let ldflags = format!("-L{}", snappy_prefix.join(LIBDIR).display());

        env::set_var(
            "LDFLAGS",
            ldflags
        );

        config
            .define("HAVE_SNAPPY", "ON")
            .cflag(format!("-I{}", snappy_prefix.join("include").display()))
            .cxxflag(format!("-I{}", snappy_prefix.join("include").display()));
    } else {
        config.define("HAVE_SNAPPY", "OFF");
    }
    let dest_prefix = config.build();

    assert_eq!(
        dest_prefix.join(LIBDIR),
        libdir,
        "CMake should build LevelDB in provided LIBDIR"
    );
    println!("cargo:rustc-link-search=native={}", libdir.display());
    println!("cargo:rustc-link-lib=static=leveldb");
}

fn main() {
    println!("[build] Started");

    // If we have the appropriate feature, then we build snappy.
    #[cfg(feature = "snappy")]
    let snappy_prefix = Some(build_snappy());
    #[cfg(not(feature = "snappy"))]
    let snappy_prefix: Option<PathBuf> = None;

    // Build LevelDB
    build_leveldb(snappy_prefix);

    // Link to the standard C++ library
    let target = env::var("TARGET").unwrap();
    if target.contains("apple") || target.contains("freebsd") {
        println!("cargo:rustc-link-lib=c++");
    } else if target.contains("gnu") || target.contains("netbsd") || target.contains("openbsd") {
        println!("cargo:rustc-link-lib=stdc++");
    }

    println!("[build] Finished");
}
<commit_msg>Statically link against libstdc++ on musl targets<commit_after>use std::{
    env,
    path::{Path, PathBuf},
};

#[cfg(feature = "snappy")]
const SNAPPY_VERSION: &'static str = "1.1.7";
const LEVELDB_VERSION: &'static str = "1.22";
/// Directory name within `$OUT_DIR` where the static libraries should be built.
const LIBDIR: &'static str = "lib";

#[cfg(feature = "snappy")]
fn build_snappy() -> PathBuf {
    println!("[snappy] Building");

    let outdir = env::var("OUT_DIR").unwrap();
    let libdir = Path::new(&outdir).join(LIBDIR);

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let dest_prefix =
        cmake::Config::new(Path::new("deps").join(format!("snappy-{}", SNAPPY_VERSION)))
            .define("BUILD_SHARED_LIBS", "OFF")
            .define("SNAPPY_BUILD_TESTS", "OFF")
            .define("HAVE_LIBZ", "OFF")
            .define("CMAKE_INSTALL_LIBDIR", &libdir)
            .build();

    assert_eq!(
        dest_prefix.join(LIBDIR),
        libdir,
        "CMake should build Snappy in provided LIBDIR"
    );
    println!("cargo:rustc-link-search=native={}", libdir.display());
    println!("cargo:rustc-link-lib=static=snappy");

    dest_prefix
}

fn build_leveldb(snappy_prefix: Option<PathBuf>) {
    println!("[leveldb] Building");

    let outdir = env::var("OUT_DIR").unwrap();
    let libdir = Path::new(&outdir).join(LIBDIR);

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let mut config =
        cmake::Config::new(Path::new("deps").join(format!("leveldb-{}", LEVELDB_VERSION)));
    config
        .define("LEVELDB_BUILD_TESTS", "OFF")
        .define("LEVELDB_BUILD_BENCHMARKS", "OFF")
        .define("CMAKE_INSTALL_LIBDIR", &libdir);
    if let Some(snappy_prefix) = snappy_prefix {
        #[cfg(target_env = "msvc")]
        let ldflags = format!("/LIBPATH:{}", snappy_prefix.join(LIBDIR).display());
        #[cfg(not(target_env = "msvc"))]
        let ldflags = format!("-L{}", snappy_prefix.join(LIBDIR).display());

        env::set_var(
            "LDFLAGS",
            ldflags
        );

        config
            .define("HAVE_SNAPPY", "ON")
            .cflag(format!("-I{}", snappy_prefix.join("include").display()))
            .cxxflag(format!("-I{}", snappy_prefix.join("include").display()));
    } else {
        config.define("HAVE_SNAPPY", "OFF");
    }
    let dest_prefix = config.build();

    assert_eq!(
        dest_prefix.join(LIBDIR),
        libdir,
        "CMake should build LevelDB in provided LIBDIR"
    );
    println!("cargo:rustc-link-search=native={}", libdir.display());
    println!("cargo:rustc-link-lib=static=leveldb");
}

fn main() {
    println!("[build] Started");

    // If we have the appropriate feature, then we build snappy.
    #[cfg(feature = "snappy")]
    let snappy_prefix = Some(build_snappy());
    #[cfg(not(feature = "snappy"))]
    let snappy_prefix: Option<PathBuf> = None;

    // Build LevelDB
    build_leveldb(snappy_prefix);

    // Link to the standard C++ library
    let target = env::var("TARGET").unwrap();
    if target.contains("apple") || target.contains("freebsd") {
        println!("cargo:rustc-link-lib=c++");
    } else if target.contains("gnu") || target.contains("netbsd") || target.contains("openbsd") {
        println!("cargo:rustc-link-lib=stdc++");
    } else if target.contains("musl") {
        // We want to link to libstdc++ *statically*. This requires that the user passes the right
        // search path to rustc via `-Lstatic=/path/to/libstdc++`.
        println!("cargo:rustc-link-lib=static=stdc++");
    }

    println!("[build] Finished");
}
<|endoftext|>