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

use std::collections::HashSet;
use std::{ffi, fmt, mem, str};
use gl;
use core::Capabilities;

/// A version number for a specific component of an OpenGL implementation
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct Version {
    pub is_embedded: bool,
    pub major: u32,
    pub minor: u32,
    pub revision: Option<u32>,
    pub vendor_info: &'static str,
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: u32, minor: u32, revision: Option<u32>,
               vendor_info: &'static str) -> Version {
        Version {
            is_embedded: false,
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }
    /// Create a new OpenGL ES version number
    pub fn new_embedded(major: u32, minor: u32, vendor_info: &'static str) -> Version {
        Version {
            is_embedded: true,
            major: major,
            minor: minor,
            revision: None,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(mut src: &'static str) -> Result<Version, &'static str> {
        let es_sig = " ES ";
        let is_es = match src.rfind(es_sig) {
            Some(pos) => {
                src = &src[pos + es_sig.len() ..];
                true
            },
            None => false,
        };
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (&src[..i], &src[i+1..]),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(|s| s.parse().ok());
        let minor = it.next().and_then(|s| s.parse().ok());
        let revision = it.next().and_then(|s| s.parse().ok());

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                is_embedded: is_es,
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Debug for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

const EMPTY_STRING: &'static str = "";

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { c_str_as_static_str(ptr) }
    } else {
        error!("Invalid GLenum passed to `get_string`: {:x}", name);
        EMPTY_STRING
    }
}

fn get_usize(gl: &gl::Gl, name: gl::types::GLenum) -> usize {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as usize
}

unsafe fn c_str_as_static_str(c_str: *const i8) -> &'static str {
    mem::transmute(str::from_utf8(ffi::CStr::from_ptr(c_str as *const _).to_bytes()).unwrap())
}

/// A unique platform identifier that does not change between releases
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// Private capabilities that don't need to be exposed.
#[derive(Debug)]
pub struct PrivateCaps {
    pub array_buffer_supported: bool,
    pub frame_buffer_supported: bool,
    pub immutable_storage_supported: bool,
    pub sampler_objects_supported: bool,
    pub program_interface_supported: bool,
    pub buffer_storage_supported: bool,
    pub clear_buffer_supported: bool,
}

/// OpenGL implementation information
#[derive(Debug)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 0, None, "") {
            let num_exts = get_usize(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            (0..num_exts)
                .map(|i| unsafe { c_str_as_static_str(gl.GetStringi(gl::EXTENSIONS, i) as *const i8) })
                .collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    pub fn is_version_supported(&self, major: u32, minor: u32) -> bool {
        !self.version.is_embedded && self.version >= Version::new(major, minor, None, "")
    }

    pub fn is_embedded_version_supported(&self, major: u32, minor: u32) -> bool {
        self.version.is_embedded && self.version >= Version::new(major, minor, None, "")
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &'static str) -> bool {
        self.extensions.contains(&s)
    }

    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &'static str) -> bool {
        self.is_version_supported(major, minor) || self.is_extension_supported(ext)
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities, PrivateCaps) {
    let info = Info::get(gl);
    let caps = Capabilities {
        max_vertex_count: get_usize(gl, gl::MAX_ELEMENTS_VERTICES),
        max_index_count:  get_usize(gl, gl::MAX_ELEMENTS_INDICES),
        max_texture_size: get_usize(gl, gl::MAX_TEXTURE_SIZE),
        max_patch_size: get_usize(gl, gl::MAX_PATCH_VERTICES),

        instance_base_supported:           info.is_version_or_extension_supported(4, 2, "GL_ARB_base_instance"),
        instance_call_supported:           info.is_version_or_extension_supported(3, 1, "GL_ARB_draw_instanced"),
        instance_rate_supported:           info.is_version_or_extension_supported(3, 3, "GL_ARB_instanced_arrays"),
        vertex_base_supported:             info.is_version_or_extension_supported(3, 2, "GL_ARB_draw_elements_base_vertex"),
        srgb_color_supported:              info.is_version_or_extension_supported(3, 2, "GL_ARB_framebuffer_sRGB"),
        constant_buffer_supported:         info.is_version_or_extension_supported(3, 1, "GL_ARB_uniform_buffer_object"),
        unordered_access_view_supported:   info.is_version_supported(4, 0), //TODO: extension
        separate_blending_slots_supported: info.is_version_or_extension_supported(4, 0, "GL_ARB_draw_buffers_blend"),
    };
    let private = PrivateCaps {
        array_buffer_supported:            info.is_version_or_extension_supported(3, 0, "GL_ARB_vertex_array_object"),
        frame_buffer_supported:            info.is_version_or_extension_supported(3, 0, "GL_ARB_framebuffer_object") |
                                           info.is_embedded_version_supported(2, 0),
        immutable_storage_supported:       info.is_version_or_extension_supported(4, 2, "GL_ARB_texture_storage"),
        sampler_objects_supported:         info.is_version_or_extension_supported(3, 3, "GL_ARB_sampler_objects"),
        program_interface_supported:       info.is_version_or_extension_supported(4, 3, "GL_ARB_program_interface_query"),
        buffer_storage_supported:          info.is_version_or_extension_supported(4, 4, "GL_ARB_buffer_storage"),
        clear_buffer_supported:            info.is_version_supported(3, 0) |  //TODO: extension
                                           info.is_embedded_version_supported(3, 0),
    };
    (info, caps, private)
}

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

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
        assert_eq!(Version::parse("OpenGL ES 3.1"), Ok(Version::new_embedded(3, 1, "")));
        assert_eq!(Version::parse("OpenGL ES 2.0 Google Nexus"), Ok(Version::new_embedded(2, 0, "Google Nexus")));
        assert_eq!(Version::parse("GLSL ES 1.1"), Ok(Version::new_embedded(1, 1, "")));
    }
}
<commit_msg>[GL] proper tessellation support check<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;
use std::{ffi, fmt, mem, str};
use gl;
use core::Capabilities;

/// A version number for a specific component of an OpenGL implementation
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct Version {
    pub is_embedded: bool,
    pub major: u32,
    pub minor: u32,
    pub revision: Option<u32>,
    pub vendor_info: &'static str,
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: u32, minor: u32, revision: Option<u32>,
               vendor_info: &'static str) -> Version {
        Version {
            is_embedded: false,
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }
    /// Create a new OpenGL ES version number
    pub fn new_embedded(major: u32, minor: u32, vendor_info: &'static str) -> Version {
        Version {
            is_embedded: true,
            major: major,
            minor: minor,
            revision: None,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(mut src: &'static str) -> Result<Version, &'static str> {
        let es_sig = " ES ";
        let is_es = match src.rfind(es_sig) {
            Some(pos) => {
                src = &src[pos + es_sig.len() ..];
                true
            },
            None => false,
        };
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (&src[..i], &src[i+1..]),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(|s| s.parse().ok());
        let minor = it.next().and_then(|s| s.parse().ok());
        let revision = it.next().and_then(|s| s.parse().ok());

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                is_embedded: is_es,
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Debug for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

const EMPTY_STRING: &'static str = "";

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { c_str_as_static_str(ptr) }
    } else {
        error!("Invalid GLenum passed to `get_string`: {:x}", name);
        EMPTY_STRING
    }
}

fn get_usize(gl: &gl::Gl, name: gl::types::GLenum) -> usize {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as usize
}

unsafe fn c_str_as_static_str(c_str: *const i8) -> &'static str {
    mem::transmute(str::from_utf8(ffi::CStr::from_ptr(c_str as *const _).to_bytes()).unwrap())
}

/// A unique platform identifier that does not change between releases
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// Private capabilities that don't need to be exposed.
#[derive(Debug)]
pub struct PrivateCaps {
    pub array_buffer_supported: bool,
    pub frame_buffer_supported: bool,
    pub immutable_storage_supported: bool,
    pub sampler_objects_supported: bool,
    pub program_interface_supported: bool,
    pub buffer_storage_supported: bool,
    pub clear_buffer_supported: bool,
}

/// OpenGL implementation information
#[derive(Debug)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 0, None, "") {
            let num_exts = get_usize(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            (0..num_exts)
                .map(|i| unsafe { c_str_as_static_str(gl.GetStringi(gl::EXTENSIONS, i) as *const i8) })
                .collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    pub fn is_version_supported(&self, major: u32, minor: u32) -> bool {
        !self.version.is_embedded && self.version >= Version::new(major, minor, None, "")
    }

    pub fn is_embedded_version_supported(&self, major: u32, minor: u32) -> bool {
        self.version.is_embedded && self.version >= Version::new(major, minor, None, "")
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &'static str) -> bool {
        self.extensions.contains(&s)
    }

    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &'static str) -> bool {
        self.is_version_supported(major, minor) || self.is_extension_supported(ext)
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities, PrivateCaps) {
    let info = Info::get(gl);
    let tessellation_supported =           info.is_version_or_extension_supported(4, 0, "GL_ARB_tessellation_shader");
    let caps = Capabilities {
        max_vertex_count: get_usize(gl, gl::MAX_ELEMENTS_VERTICES),
        max_index_count:  get_usize(gl, gl::MAX_ELEMENTS_INDICES),
        max_texture_size: get_usize(gl, gl::MAX_TEXTURE_SIZE),
        max_patch_size: if tessellation_supported { get_usize(gl, gl::MAX_PATCH_VERTICES) } else {0},

        instance_base_supported:           info.is_version_or_extension_supported(4, 2, "GL_ARB_base_instance"),
        instance_call_supported:           info.is_version_or_extension_supported(3, 1, "GL_ARB_draw_instanced"),
        instance_rate_supported:           info.is_version_or_extension_supported(3, 3, "GL_ARB_instanced_arrays"),
        vertex_base_supported:             info.is_version_or_extension_supported(3, 2, "GL_ARB_draw_elements_base_vertex"),
        srgb_color_supported:              info.is_version_or_extension_supported(3, 2, "GL_ARB_framebuffer_sRGB"),
        constant_buffer_supported:         info.is_version_or_extension_supported(3, 1, "GL_ARB_uniform_buffer_object"),
        unordered_access_view_supported:   info.is_version_supported(4, 0), //TODO: extension
        separate_blending_slots_supported: info.is_version_or_extension_supported(4, 0, "GL_ARB_draw_buffers_blend"),
    };
    let private = PrivateCaps {
        array_buffer_supported:            info.is_version_or_extension_supported(3, 0, "GL_ARB_vertex_array_object"),
        frame_buffer_supported:            info.is_version_or_extension_supported(3, 0, "GL_ARB_framebuffer_object") |
                                           info.is_embedded_version_supported(2, 0),
        immutable_storage_supported:       info.is_version_or_extension_supported(4, 2, "GL_ARB_texture_storage"),
        sampler_objects_supported:         info.is_version_or_extension_supported(3, 3, "GL_ARB_sampler_objects"),
        program_interface_supported:       info.is_version_or_extension_supported(4, 3, "GL_ARB_program_interface_query"),
        buffer_storage_supported:          info.is_version_or_extension_supported(4, 4, "GL_ARB_buffer_storage"),
        clear_buffer_supported:            info.is_version_supported(3, 0) |  //TODO: extension
                                           info.is_embedded_version_supported(3, 0),
    };
    (info, caps, private)
}

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

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
        assert_eq!(Version::parse("OpenGL ES 3.1"), Ok(Version::new_embedded(3, 1, "")));
        assert_eq!(Version::parse("OpenGL ES 2.0 Google Nexus"), Ok(Version::new_embedded(2, 0, "Google Nexus")));
        assert_eq!(Version::parse("GLSL ES 1.1"), Ok(Version::new_embedded(1, 1, "")));
    }
}
<|endoftext|>


<commit_before>use serde_json;
use sputnikvm;

use sputnikvm::{read_hex, Gas, M256, Address};
use sputnikvm::vm::{Machine, VectorMachine, Stack, PC};
use sputnikvm::blockchain::Block;
use sputnikvm::transaction::{Transaction, VectorTransaction};

use super::json_schema::{create_machine, test_machine};

use serde_json::{Value, Error};
use std::fs::File;
use std::path::Path;
use std::io::{BufReader, Write, stdout};
use std::str::FromStr;

use rustyline::error::ReadlineError;
use rustyline::Editor;

pub fn test_transaction(name: &str, v: &Value, debug: bool) {
    print!("Testing {} ... ", name);
    if debug {
        print!("\n");
    }
    stdout().flush();

    let mut machine = create_machine(v);
    let result = machine.fire();

    let out = v["out"].as_str();

    if out.is_some() {
        if result.is_ok() && test_machine(v, &machine, debug) {
            println!("OK");
        } else {
            println!("Failed {:?}", result.err());
        }
    } else {
        if result.is_err() {
            println!("OK");
        } else {
            println!("Failed");
        }
    }
}

pub fn debug_transaction(v: &Value) {
    let mut machine = create_machine(v);
    let mut rl = Editor::<()>::new();

    loop {
        let readline = rl.readline(">> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                match line.as_ref() {
                    "step" => {
                        if machine.pc().stopped() {
                            println!("Stopped");
                        } else {
                            println!("Running {:?} ... {:?}.", machine.pc().peek_opcode(),
                                     machine.step());
                        }
                    },
                    "fire" => {
                        let result = machine.fire();
                        println!("{:?}", result);
                    },
                    "fire debug" => {
                        while !machine.pc().stopped() {
                            println!("Running {:?} ... {:?}.", machine.pc().peek_opcode(),
                                     machine.step());
                        }
                    },
                    "print stack" => {
                        for i in 0..machine.stack().size() {
                            println!("{}: {:x}", i, machine.stack().peek(i).unwrap());
                        }
                    },
                    _ => {
                        println!("Unknown command.");
                    }
                }
            },
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break
            },
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);
                break
            }
        }
    }
}
<commit_msg>Problem: no way to query out in edb Solution: add out command<commit_after>use serde_json;
use sputnikvm;

use sputnikvm::{read_hex, Gas, M256, Address};
use sputnikvm::vm::{Machine, VectorMachine, Stack, PC};
use sputnikvm::blockchain::Block;
use sputnikvm::transaction::{Transaction, VectorTransaction};

use super::json_schema::{create_machine, test_machine};

use serde_json::{Value, Error};
use std::fs::File;
use std::path::Path;
use std::io::{BufReader, Write, stdout};
use std::str::FromStr;

use rustyline::error::ReadlineError;
use rustyline::Editor;

pub fn test_transaction(name: &str, v: &Value, debug: bool) {
    print!("Testing {} ... ", name);
    if debug {
        print!("\n");
    }
    stdout().flush();

    let mut machine = create_machine(v);
    let result = machine.fire();

    let out = v["out"].as_str();

    if out.is_some() {
        if result.is_ok() && test_machine(v, &machine, debug) {
            println!("OK");
        } else {
            println!("Failed {:?}", result.err());
        }
    } else {
        if result.is_err() {
            println!("OK");
        } else {
            println!("Failed");
        }
    }
}

pub fn debug_transaction(v: &Value) {
    let mut machine = create_machine(v);
    let mut rl = Editor::<()>::new();

    loop {
        let readline = rl.readline(">> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                match line.as_ref() {
                    "step" => {
                        if machine.pc().stopped() {
                            println!("Stopped");
                        } else {
                            println!("Running {:?} ... {:?}.", machine.pc().peek_opcode(),
                                     machine.step());
                        }
                    },
                    "fire" => {
                        let result = machine.fire();
                        println!("{:?}", result);
                    },
                    "fire debug" => {
                        while !machine.pc().stopped() {
                            println!("Running {:?} ... {:?}.", machine.pc().peek_opcode(),
                                     machine.step());
                        }
                    },
                    "out" => {
                        let ret = machine.return_values();
                        println!("{:?}", ret);
                    }
                    "print stack" => {
                        for i in 0..machine.stack().size() {
                            println!("{}: {:x}", i, machine.stack().peek(i).unwrap());
                        }
                    },
                    _ => {
                        println!("Unknown command.");
                    }
                }
            },
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break
            },
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);
                break
            }
        }
    }
}
<|endoftext|>


<commit_before>use regex::Regex;

use cases::lowercase::to_lower_case;

pub fn to_snake_case<'a>(non_snake_case_string: String) -> String {
    if non_snake_case_string.contains(" ")
        || non_snake_case_string.contains("_")
        || non_snake_case_string.contains("-") {
        return to_snake_from_sentence_or_kebab(non_snake_case_string);
    } else {
        return to_snake_from_camel_or_class(non_snake_case_string);
    }
}
    fn to_snake_from_camel_or_class <'a>(non_snake_case_string: String) -> String {
        let re = Regex::new(r"(?P<a>[A-Z0-9])").unwrap();
        let result: String = re.replace_all(&non_snake_case_string, "_$a$b").to_string();
        return to_lower_case(result.trim_left_matches("_").to_string());
    }

    fn to_snake_from_sentence_or_kebab<'a>(non_snake_case_string: String) -> String {
        return to_lower_case(non_snake_case_string.replace(" ", "_").replace("-", "_"));
    }

pub fn is_snake_case<'a>(test_string: String) -> bool{
    let snake_matcher = Regex::new(r"(?:[^-|^ ]?=^|[_])([a-z]+)").unwrap();
    if snake_matcher.is_match(test_string.as_ref()){
        return true;
    }
    return false;
}

#[cfg(test)]
mod snakecase_test;
<commit_msg>Getting those times down :feelsgood:<commit_after>use std::ascii::*;
use regex::Regex;

use cases::lowercase::to_lower_case;

pub fn to_snake_case<'a>(non_snake_case_string: String) -> String {
    if non_snake_case_string.contains(" ")
        || non_snake_case_string.contains("_")
        || non_snake_case_string.contains("-") {
        return to_snake_from_sentence_or_kebab(non_snake_case_string);
    } else {
        return to_snake_from_camel_or_class(non_snake_case_string);
    }
}
    fn to_snake_from_camel_or_class <'a>(non_snake_case_string: String) -> String {
        let mut result:String = "".to_string();
        let mut first_character: bool = true;
        for character in non_snake_case_string.chars() {
            if character == character.to_ascii_uppercase() && !first_character {
                result = format!("{}_{}", result, character.to_ascii_lowercase());
            } else {
                result = format!("{}{}", result, character.to_ascii_lowercase());
                first_character = false;
            }
        }
        return result
    }
    // fn to_snake_from_camel_or_class <'a>(non_snake_case_string: String) -> String {
    //     let re = Regex::new(r"(?P<a>[A-Z0-9])").unwrap();
    //     let result: String = re.replace_all(&non_snake_case_string, "_$a$b").to_string();
    //     return to_lower_case(result.trim_left_matches("_").to_string());
    // }

    fn to_snake_from_sentence_or_kebab<'a>(non_snake_case_string: String) -> String {
        return to_lower_case(non_snake_case_string.replace(" ", "_").replace("-", "_"));
    }

pub fn is_snake_case<'a>(test_string: String) -> bool{
    let snake_matcher = Regex::new(r"(?:[^-|^ ]?=^|[_])([a-z]+)").unwrap();
    if snake_matcher.is_match(test_string.as_ref()){
        return true;
    }
    return false;
}

#[cfg(test)]
mod snakecase_test;
<|endoftext|>


<commit_before>//! Safe wrapper for [POSIX regular expressions API][regex-h] (provided by libc on POSIX-compliant OSes).
//!
//! [regex-h]: https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/basedefs/regex.h.html#tag_13_37
//!
//! ```
//! use regex_rs::*;
//!
//! let pattern = "This( often)? repeats time and again(, and again)*\\.";
//! let compilation_flags = CompFlags::EXTENDED;
//! let regex = Regex::new(pattern, compilation_flags)
//!     .expect("Failed to compile pattern as POSIX extended regular expression");
//!
//! let input = "This repeats time and again, and again, and again.";
//! // We're only interested in the first match, i.e. the part of text
//! // that's matched by the whole regex
//! let max_matches = 1;
//! let match_flags = MatchFlags::empty();
//! let matches = regex
//!     .matches(input, max_matches, match_flags)
//!     .expect("Error matching input against regex");
//!
//! // Found a match
//! assert_eq!(matches.len(), 1);
//!
//! // Match spans from the beginning to the end of the input
//! assert_eq!(matches[0].start_pos, 0);
//! // `end_pos` holds one-past-the-end index
//! assert_eq!(matches[0].end_pos, input.len());
//! ```

use bitflags::bitflags;
use gettextrs::gettext;
use libc::{regcomp, regerror, regex_t, regexec, regfree, regmatch_t};
use std::ffi::{CString, OsString};
use std::mem;
use std::os::unix::ffi::OsStringExt;
use std::ptr;
use strprintf::fmt;

/// POSIX regular expression.
pub struct Regex {
    /// Compiled POSIX regular expression.
    regex: regex_t,
}

bitflags! {
    /// Compilation flags.
    ///
    /// These affect what features are available inside the regex, and also how it's matched
    /// against the input string.
    pub struct CompFlags: i32 {
        /// Use Extended Regular Expressions.
        ///
        /// POSIX calls this `REG_EXTENDED`.
        const EXTENDED = libc::REG_EXTENDED;

        /// Ignore case when matching.
        ///
        /// POSIX calls this `REG_ICASE`.
        const IGNORE_CASE = libc::REG_ICASE;

        /// Report only success or fail of the compilation.
        ///
        /// POSIX calls this `REG_NOSUB`.
        const NO_SUB = libc::REG_NOSUB;

        /// Give special meaning to newline characters.
        ///
        /// POSIX calls this `REG_NEWLINE`.
        ///
        /// Without this flag, newlines match themselves.
        ///
        /// With this flag, newlines match themselves except:
        ///
        /// 1. newline is not matched by `.` outside of bracket expressions or by any form of
        ///    non-matching lists;
        ///
        /// 2. beginning-of-line (`^`) matches zero-width string right after newline, regardless of
        ///    `CompFlag::NOTBOL`;
        ///
        /// 3. end-of-line (`$`) matches zero-width string right before a newline, regardless of
        ///    `CompFlags::NOTEOL`.
        const NEWLINE = libc::REG_NEWLINE;
    }
}

bitflags! {
    /// Matching flags.
    ///
    /// These affect how regex is matched against the input string.
    pub struct MatchFlags: i32 {
        /// The circumflex character (`^`), when taken as a special character, does not match the
        /// beginning of string.
        const NOTBOL = libc::REG_NOTBOL;

        /// The dollar-sign (`$`), when taken as a special character, does not match the end of
        /// string.
        const NOTEOL = libc::REG_NOTEOL;
    }
}

/// Start and end positions of a matched substring.
pub struct Match {
    /// Start position (counting from zero).
    pub start_pos: usize,

    /// One-past-end position (counting from zero).
    pub end_pos: usize,
}

/// A wrapper around `libc::regerror()`.
unsafe fn regex_error_to_str(errcode: libc::c_int, regex: &regex_t) -> Option<String> {
    // Find out the size of the buffer needed to hold the error message
    let errmsg_length = regerror(errcode, regex, ptr::null_mut(), 0);

    // Allocate the buffer and get the message.
    // Casting u64 to usize is safe since the error message is unlikely to hit usize's
    // upper bound.
    let mut errmsg: Vec<u8> = vec![0; errmsg_length as usize];
    // Casting `*mut u8` to `*mut c_char` should be safe since C doesn't really care:
    // it can store any ASCII symbol in a `char`, disregarding signedness.
    regerror(
        errcode,
        regex,
        errmsg.as_mut_ptr() as *mut std::os::raw::c_char,
        errmsg_length,
    );

    // Drop the trailing NUL byte that C uses to terminate strings
    errmsg.pop();

    OsString::from_vec(errmsg).into_string().ok()
}

impl Regex {
    /// Compiles pattern as a regular expression.
    ///
    /// By default, pattern is assumed to be a basic regular expression. To interpret it as an
    /// extended regular expression, add `CompFlags::EXTENDED` to the `flags`. See also other
    /// `CompFlags` values to control some other aspects of the regex.
    ///
    /// # Returns
    ///
    /// Compiled regex or an error message.
    pub fn new(pattern: &str, flags: CompFlags) -> Result<Regex, String> {
        let pattern = CString::new(pattern)
            .map_err(|_| String::from("Regular expression contains NUL byte"))?;

        unsafe {
            let mut regex: regex_t = mem::zeroed();
            let errcode = regcomp(&mut regex, pattern.into_raw(), flags.bits());

            if errcode == 0 {
                Ok(Regex { regex })
            } else {
                match regex_error_to_str(errcode, &regex) {
                    Some(regcomp_errmsg) => {
                        let msg = fmt!(&gettext("regcomp returned code %i"), errcode);
                        let msg = format!("{}: {}", msg, regcomp_errmsg);
                        Err(msg)
                    }

                    None => Err(fmt!(&gettext("regcomp returned code %i"), errcode)),
                }
            }
        }
    }

    /// Matches input string against regex, looking for up to `max_matches` matches.
    ///
    /// Regexes can contain parenthesized subexpressions. This method will return up to
    /// `max_matches`-1 of those. First match is reserved for the text that the whole regex
    /// matched.
    ///
    /// `flags` dictate how matching is performed. See `MatchFlags` for details.
    ///
    /// # Returns
    ///
    /// - `Ok` with an empty vector if no match found, or if `max_matches` is 0 and there were no
    ///   errors.
    /// - `Ok` with a non-empty vector if `max_matches` was non-zero and a match was found. First
    ///   element of the vector is the text that regex as a whole matched. The rest of the elements
    ///   are pieces of text that were matched by parenthesized subexpressions.
    /// - `Err` with an error message.
    pub fn matches(
        &self,
        input: &str,
        max_matches: usize,
        flags: MatchFlags,
    ) -> Result<Vec<Match>, String> {
        let input =
            CString::new(input).map_err(|_| String::from("Input string contains NUL byte"))?;

        let mut pmatch: Vec<regmatch_t>;

        let errcode = unsafe {
            pmatch = vec![mem::zeroed(); max_matches];

            regexec(
                &self.regex,
                input.into_raw(),
                max_matches as libc::size_t,
                pmatch.as_mut_ptr(),
                flags.bits(),
            )
        };

        match errcode {
            0 => {
                // Success. Let's copy results
                let mut matches: Vec<Match> = Vec::new();

                for m in pmatch {
                    if m.rm_so < 0 || m.rm_eo < 0 {
                        // Since `max_matches` can be bigger than the number of parenthesized
                        // blocks in the regex, it's possible that some of the `pmatch` values are
                        // empty. We exit the loop after detecting first such value.
                        break;
                    }

                    // It's safe to cast i32 to usize here:
                    // - we already checked that the values aren't negative
                    // - usize's upper bound is higher than i32's
                    matches.push(Match {
                        start_pos: m.rm_so as usize,
                        end_pos: m.rm_eo as usize,
                    });
                }

                Ok(matches)
            }

            libc::REG_NOMATCH => {
                // Matching went okay, but nothing found
                Ok(Vec::new())
            }

            // POSIX only specifies two return codes for regexec(), but implementations are free to
            // extend that.
            _ => unsafe {
                match regex_error_to_str(errcode, &self.regex) {
                    Some(regexec_errmsg) => {
                        let msg = fmt!(&gettext("regexec returned code %i"), errcode);
                        let msg = format!("{}: {}", msg, regexec_errmsg);
                        Err(msg)
                    }
                    None => Err(fmt!(&gettext("regexec returned code %i"), errcode)),
                }
            },
        }
    }
}

impl Drop for Regex {
    fn drop(&mut self) {
        unsafe {
            regfree(&mut self.regex);
        }
    }
}

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

    #[test]
    fn matches_basic_posix_regular_expression() {
        let regex = Regex::new("abc+", CompFlags::empty()).unwrap();
        let matches = regex.matches("abc+others", 1, MatchFlags::empty()).unwrap();

        assert_eq!(matches.len(), 1);

        // Match spans from the start of the input until the 4th character
        assert_eq!(matches[0].start_pos, 0);
        assert_eq!(matches[0].end_pos, 4); // one-past-last offset
    }

    #[test]
    fn matches_extended_posix_regular_expression() {
        let regex = Regex::new("aBc+", CompFlags::EXTENDED | CompFlags::IGNORE_CASE).unwrap();
        let matches = regex
            .matches("AbCcCcCC and others", 1, MatchFlags::empty())
            .unwrap();

        assert_eq!(matches.len(), 1);

        // Match spans from the start of the input until the 4th character
        assert_eq!(matches[0].start_pos, 0);
        assert_eq!(matches[0].end_pos, 8); // one-past-last offset
    }

    #[test]
    fn new_returns_error_on_invalid_regex() {
        let result = Regex::new("(abc", CompFlags::EXTENDED);

        assert!(result.is_err());
        if let Err(msg) = result {
            // There should be at least an error code, so string can't possibly be empty
            assert!(!msg.is_empty());

            // The message shouldn't contain a C string terminator (NUL) at the end
            assert!(!msg.ends_with('\0'));
        }
    }
}
<commit_msg>regex-rs: Add tests cases related to number of results<commit_after>//! Safe wrapper for [POSIX regular expressions API][regex-h] (provided by libc on POSIX-compliant OSes).
//!
//! [regex-h]: https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/basedefs/regex.h.html#tag_13_37
//!
//! ```
//! use regex_rs::*;
//!
//! let pattern = "This( often)? repeats time and again(, and again)*\\.";
//! let compilation_flags = CompFlags::EXTENDED;
//! let regex = Regex::new(pattern, compilation_flags)
//!     .expect("Failed to compile pattern as POSIX extended regular expression");
//!
//! let input = "This repeats time and again, and again, and again.";
//! // We're only interested in the first match, i.e. the part of text
//! // that's matched by the whole regex
//! let max_matches = 1;
//! let match_flags = MatchFlags::empty();
//! let matches = regex
//!     .matches(input, max_matches, match_flags)
//!     .expect("Error matching input against regex");
//!
//! // Found a match
//! assert_eq!(matches.len(), 1);
//!
//! // Match spans from the beginning to the end of the input
//! assert_eq!(matches[0].start_pos, 0);
//! // `end_pos` holds one-past-the-end index
//! assert_eq!(matches[0].end_pos, input.len());
//! ```

use bitflags::bitflags;
use gettextrs::gettext;
use libc::{regcomp, regerror, regex_t, regexec, regfree, regmatch_t};
use std::ffi::{CString, OsString};
use std::mem;
use std::os::unix::ffi::OsStringExt;
use std::ptr;
use strprintf::fmt;

/// POSIX regular expression.
pub struct Regex {
    /// Compiled POSIX regular expression.
    regex: regex_t,
}

bitflags! {
    /// Compilation flags.
    ///
    /// These affect what features are available inside the regex, and also how it's matched
    /// against the input string.
    pub struct CompFlags: i32 {
        /// Use Extended Regular Expressions.
        ///
        /// POSIX calls this `REG_EXTENDED`.
        const EXTENDED = libc::REG_EXTENDED;

        /// Ignore case when matching.
        ///
        /// POSIX calls this `REG_ICASE`.
        const IGNORE_CASE = libc::REG_ICASE;

        /// Report only success or fail of the compilation.
        ///
        /// POSIX calls this `REG_NOSUB`.
        const NO_SUB = libc::REG_NOSUB;

        /// Give special meaning to newline characters.
        ///
        /// POSIX calls this `REG_NEWLINE`.
        ///
        /// Without this flag, newlines match themselves.
        ///
        /// With this flag, newlines match themselves except:
        ///
        /// 1. newline is not matched by `.` outside of bracket expressions or by any form of
        ///    non-matching lists;
        ///
        /// 2. beginning-of-line (`^`) matches zero-width string right after newline, regardless of
        ///    `CompFlag::NOTBOL`;
        ///
        /// 3. end-of-line (`$`) matches zero-width string right before a newline, regardless of
        ///    `CompFlags::NOTEOL`.
        const NEWLINE = libc::REG_NEWLINE;
    }
}

bitflags! {
    /// Matching flags.
    ///
    /// These affect how regex is matched against the input string.
    pub struct MatchFlags: i32 {
        /// The circumflex character (`^`), when taken as a special character, does not match the
        /// beginning of string.
        const NOTBOL = libc::REG_NOTBOL;

        /// The dollar-sign (`$`), when taken as a special character, does not match the end of
        /// string.
        const NOTEOL = libc::REG_NOTEOL;
    }
}

/// Start and end positions of a matched substring.
pub struct Match {
    /// Start position (counting from zero).
    pub start_pos: usize,

    /// One-past-end position (counting from zero).
    pub end_pos: usize,
}

/// A wrapper around `libc::regerror()`.
unsafe fn regex_error_to_str(errcode: libc::c_int, regex: &regex_t) -> Option<String> {
    // Find out the size of the buffer needed to hold the error message
    let errmsg_length = regerror(errcode, regex, ptr::null_mut(), 0);

    // Allocate the buffer and get the message.
    // Casting u64 to usize is safe since the error message is unlikely to hit usize's
    // upper bound.
    let mut errmsg: Vec<u8> = vec![0; errmsg_length as usize];
    // Casting `*mut u8` to `*mut c_char` should be safe since C doesn't really care:
    // it can store any ASCII symbol in a `char`, disregarding signedness.
    regerror(
        errcode,
        regex,
        errmsg.as_mut_ptr() as *mut std::os::raw::c_char,
        errmsg_length,
    );

    // Drop the trailing NUL byte that C uses to terminate strings
    errmsg.pop();

    OsString::from_vec(errmsg).into_string().ok()
}

impl Regex {
    /// Compiles pattern as a regular expression.
    ///
    /// By default, pattern is assumed to be a basic regular expression. To interpret it as an
    /// extended regular expression, add `CompFlags::EXTENDED` to the `flags`. See also other
    /// `CompFlags` values to control some other aspects of the regex.
    ///
    /// # Returns
    ///
    /// Compiled regex or an error message.
    pub fn new(pattern: &str, flags: CompFlags) -> Result<Regex, String> {
        let pattern = CString::new(pattern)
            .map_err(|_| String::from("Regular expression contains NUL byte"))?;

        unsafe {
            let mut regex: regex_t = mem::zeroed();
            let errcode = regcomp(&mut regex, pattern.into_raw(), flags.bits());

            if errcode == 0 {
                Ok(Regex { regex })
            } else {
                match regex_error_to_str(errcode, &regex) {
                    Some(regcomp_errmsg) => {
                        let msg = fmt!(&gettext("regcomp returned code %i"), errcode);
                        let msg = format!("{}: {}", msg, regcomp_errmsg);
                        Err(msg)
                    }

                    None => Err(fmt!(&gettext("regcomp returned code %i"), errcode)),
                }
            }
        }
    }

    /// Matches input string against regex, looking for up to `max_matches` matches.
    ///
    /// Regexes can contain parenthesized subexpressions. This method will return up to
    /// `max_matches`-1 of those. First match is reserved for the text that the whole regex
    /// matched.
    ///
    /// `flags` dictate how matching is performed. See `MatchFlags` for details.
    ///
    /// # Returns
    ///
    /// - `Ok` with an empty vector if no match found, or if `max_matches` is 0 and there were no
    ///   errors.
    /// - `Ok` with a non-empty vector if `max_matches` was non-zero and a match was found. First
    ///   element of the vector is the text that regex as a whole matched. The rest of the elements
    ///   are pieces of text that were matched by parenthesized subexpressions.
    /// - `Err` with an error message.
    pub fn matches(
        &self,
        input: &str,
        max_matches: usize,
        flags: MatchFlags,
    ) -> Result<Vec<Match>, String> {
        let input =
            CString::new(input).map_err(|_| String::from("Input string contains NUL byte"))?;

        let mut pmatch: Vec<regmatch_t>;

        let errcode = unsafe {
            pmatch = vec![mem::zeroed(); max_matches];

            regexec(
                &self.regex,
                input.into_raw(),
                max_matches as libc::size_t,
                pmatch.as_mut_ptr(),
                flags.bits(),
            )
        };

        match errcode {
            0 => {
                // Success. Let's copy results
                let mut matches: Vec<Match> = Vec::new();

                for m in pmatch {
                    if m.rm_so < 0 || m.rm_eo < 0 {
                        // Since `max_matches` can be bigger than the number of parenthesized
                        // blocks in the regex, it's possible that some of the `pmatch` values are
                        // empty. We exit the loop after detecting first such value.
                        break;
                    }

                    // It's safe to cast i32 to usize here:
                    // - we already checked that the values aren't negative
                    // - usize's upper bound is higher than i32's
                    matches.push(Match {
                        start_pos: m.rm_so as usize,
                        end_pos: m.rm_eo as usize,
                    });
                }

                Ok(matches)
            }

            libc::REG_NOMATCH => {
                // Matching went okay, but nothing found
                Ok(Vec::new())
            }

            // POSIX only specifies two return codes for regexec(), but implementations are free to
            // extend that.
            _ => unsafe {
                match regex_error_to_str(errcode, &self.regex) {
                    Some(regexec_errmsg) => {
                        let msg = fmt!(&gettext("regexec returned code %i"), errcode);
                        let msg = format!("{}: {}", msg, regexec_errmsg);
                        Err(msg)
                    }
                    None => Err(fmt!(&gettext("regexec returned code %i"), errcode)),
                }
            },
        }
    }
}

impl Drop for Regex {
    fn drop(&mut self) {
        unsafe {
            regfree(&mut self.regex);
        }
    }
}

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

    #[test]
    fn matches_basic_posix_regular_expression() {
        let regex = Regex::new("abc+", CompFlags::empty()).unwrap();
        let matches = regex.matches("abc+others", 1, MatchFlags::empty()).unwrap();

        assert_eq!(matches.len(), 1);

        // Match spans from the start of the input until the 4th character
        assert_eq!(matches[0].start_pos, 0);
        assert_eq!(matches[0].end_pos, 4); // one-past-last offset
    }

    #[test]
    fn matches_extended_posix_regular_expression() {
        let regex = Regex::new("aBc+", CompFlags::EXTENDED | CompFlags::IGNORE_CASE).unwrap();
        let matches = regex
            .matches("AbCcCcCC and others", 1, MatchFlags::empty())
            .unwrap();

        assert_eq!(matches.len(), 1);

        // Match spans from the start of the input until the 4th character
        assert_eq!(matches[0].start_pos, 0);
        assert_eq!(matches[0].end_pos, 8); // one-past-last offset
    }

    #[test]
    fn returns_empty_when_regex_valid_but_no_match() {
        let regex = Regex::new("abc", CompFlags::empty()).unwrap();
        let matches = regex.matches("cba", 1, MatchFlags::empty()).unwrap();

        assert_eq!(matches.len(), 0);
    }

    #[test]
    fn returns_no_more_results_than_max_matches() {
        let regex = Regex::new("(a)(b)(c)", CompFlags::EXTENDED).unwrap();
        let max_matches = 2;
        let matches = regex
            .matches("abc", max_matches, MatchFlags::empty())
            .unwrap();

        assert_eq!(matches.len(), 2);

        assert_eq!(matches[0].start_pos, 0);
        assert_eq!(matches[0].end_pos, 3);
        assert_eq!(matches[1].start_pos, 0);
        assert_eq!(matches[1].end_pos, 1);
    }

    #[test]
    fn returns_no_more_results_than_available() {
        let regex = Regex::new("abc", CompFlags::EXTENDED).unwrap();
        let max_matches = 10;
        let matches = regex
            .matches("abc", max_matches, MatchFlags::empty())
            .unwrap();

        assert_eq!(matches.len(), 1);

        assert_eq!(matches[0].start_pos, 0);
        assert_eq!(matches[0].end_pos, 3);
    }

    #[test]
    fn new_returns_error_on_invalid_regex() {
        let result = Regex::new("(abc", CompFlags::EXTENDED);

        assert!(result.is_err());
        if let Err(msg) = result {
            // There should be at least an error code, so string can't possibly be empty
            assert!(!msg.is_empty());

            // The message shouldn't contain a C string terminator (NUL) at the end
            assert!(!msg.ends_with('\0'));
        }
    }
}
<|endoftext|>


<commit_before>// Refer:
// https://www.ietf.org/rfc/rfc2388.txt:  Returning Values from Forms:  multipart/form-data
// https://www.ietf.org/rfc/rfc2183.txt:  The Content-Disposition Header Field

use std::fmt;
use std::ascii::AsciiExt;

use hyper::header::{Header, HeaderFormat, parsing};
use hyper::error::Error as HyperError;

/// We define a Content-Disposition form-data only header, which is found within
/// the mime multipart sections.  NOT FOR GENERAL USE
#[derive(Debug,Clone,PartialEq)]
pub struct ContentDispositionFormData {
    pub name: Option<String>,
    pub filename: Option<String>,
}

impl Header for ContentDispositionFormData {
    fn header_name() -> &'static str {
        "Content-Disposition"
    }

    fn parse_header(raw: &[Vec<u8>]) -> Result<ContentDispositionFormData,HyperError> {
        parsing::from_one_raw_str(raw).and_then(|s: String| {
            let mut sections = s.split(';');
            match sections.next() {
                None => return Err(HyperError::Header),
                Some(s) => {
                    if &s.trim().to_ascii_lowercase() != "form-data" {
                        return Err(HyperError::Header)
                    }
                }
            };
            let mut cd = ContentDispositionFormData { name: None, filename: None };
            for section in sections {
                let mut parts = section.split('=');
                if parts.clone().count() != 2 { continue; }
                let key = parts.next().unwrap().trim().to_ascii_lowercase();
                let mut val = parts.next().unwrap().trim();
                if val.chars().next()==Some('"') && val.chars().rev().next()==Some('"') {
                    val = &val[1..val.len()-1]; // unwrap quotes
                }
                match &*key {
                    "name" => cd.name = Some(val.to_string()),
                    "filename" => cd.filename = Some(val.to_string()),
                    _ => {}
                }
            }
            Ok(cd)
        })
    }
}

impl HeaderFormat for ContentDispositionFormData {
    fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("form-data")
    }
}

#[cfg(test)]
mod tests {
    use super::ContentDispositionFormData;
    use hyper::header::Header;
    use hyper::error::Error as HyperError;

    #[test]
    fn parse_header() {
        let a: ContentDispositionFormData =
            ContentDispositionFormData::parse_header(
                [b"form-data; dummy=3; name=upload;\r\n filename=\"sample.png\"".to_vec()].as_ref() )
            .unwrap();
        let b = ContentDispositionFormData {
            name: Some("upload".to_string()),
            filename: Some("sample.png".to_string()),
        };
        assert_eq!(a, b);

        match ContentDispositionFormData::parse_header([b"".to_vec()].as_ref()) {
            Ok(_) => assert!(false, "Empty header was parsed as ok!"),
            Err(HyperError::Header) => assert!(true),
            Err(_) => assert!(false, "Empty header parsed with wrong error"),
        }
    }

    // RFC 2231 (obsoletes 2184) is NOT supported presently
    // See closed issue #3
    //
    //#[test]
    //fn parse_header_test_rfc_2231() {
    //    let a: ContentDispositionFormData =
    //        ContentDispositionFormData::parse_header(
    //            [b"form-data; dummy=3;\r\n filename*0=\"sample%20music\"; filename*1=\".png\"".to_vec()].as_ref() )
    //        .unwrap();
    //    let b = ContentDispositionFormData {
    //        name: None,
    //        filename: Some("sample music.png".to_string()),
    //    };
    //    assert_eq!(a, b);
    //}
}
<commit_msg>Better doc comment on why ContentDispositionFormData is not for general use.<commit_after>// Refer:
// https://www.ietf.org/rfc/rfc2388.txt:  Returning Values from Forms:  multipart/form-data
// https://www.ietf.org/rfc/rfc2183.txt:  The Content-Disposition Header Field

use std::fmt;
use std::ascii::AsciiExt;

use hyper::header::{Header, HeaderFormat, parsing};
use hyper::error::Error as HyperError;

/// We define a Content-Disposition form-data only header, which is found within
/// the mime multipart sections.  NOT FOR GENERAL USE, as it is very limited
/// in scope.
#[derive(Debug,Clone,PartialEq)]
pub struct ContentDispositionFormData {
    pub name: Option<String>,
    pub filename: Option<String>,
}

impl Header for ContentDispositionFormData {
    fn header_name() -> &'static str {
        "Content-Disposition"
    }

    fn parse_header(raw: &[Vec<u8>]) -> Result<ContentDispositionFormData,HyperError> {
        parsing::from_one_raw_str(raw).and_then(|s: String| {
            let mut sections = s.split(';');
            match sections.next() {
                None => return Err(HyperError::Header),
                Some(s) => {
                    if &s.trim().to_ascii_lowercase() != "form-data" {
                        return Err(HyperError::Header)
                    }
                }
            };
            let mut cd = ContentDispositionFormData { name: None, filename: None };
            for section in sections {
                let mut parts = section.split('=');
                if parts.clone().count() != 2 { continue; }
                let key = parts.next().unwrap().trim().to_ascii_lowercase();
                let mut val = parts.next().unwrap().trim();
                if val.chars().next()==Some('"') && val.chars().rev().next()==Some('"') {
                    val = &val[1..val.len()-1]; // unwrap quotes
                }
                match &*key {
                    "name" => cd.name = Some(val.to_string()),
                    "filename" => cd.filename = Some(val.to_string()),
                    _ => {}
                }
            }
            Ok(cd)
        })
    }
}

impl HeaderFormat for ContentDispositionFormData {
    fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("form-data")
    }
}

#[cfg(test)]
mod tests {
    use super::ContentDispositionFormData;
    use hyper::header::Header;
    use hyper::error::Error as HyperError;

    #[test]
    fn parse_header() {
        let a: ContentDispositionFormData =
            ContentDispositionFormData::parse_header(
                [b"form-data; dummy=3; name=upload;\r\n filename=\"sample.png\"".to_vec()].as_ref() )
            .unwrap();
        let b = ContentDispositionFormData {
            name: Some("upload".to_string()),
            filename: Some("sample.png".to_string()),
        };
        assert_eq!(a, b);

        match ContentDispositionFormData::parse_header([b"".to_vec()].as_ref()) {
            Ok(_) => assert!(false, "Empty header was parsed as ok!"),
            Err(HyperError::Header) => assert!(true),
            Err(_) => assert!(false, "Empty header parsed with wrong error"),
        }
    }

    // RFC 2231 (obsoletes 2184) is NOT supported presently
    // See closed issue #3
    //
    //#[test]
    //fn parse_header_test_rfc_2231() {
    //    let a: ContentDispositionFormData =
    //        ContentDispositionFormData::parse_header(
    //            [b"form-data; dummy=3;\r\n filename*0=\"sample%20music\"; filename*1=\".png\"".to_vec()].as_ref() )
    //        .unwrap();
    //    let b = ContentDispositionFormData {
    //        name: None,
    //        filename: Some("sample music.png".to_string()),
    //    };
    //    assert_eq!(a, b);
    //}
}
<|endoftext|>


<commit_before>use common::{RingBuffer,Queue};
use nvic;
use rtc;
use gpio;
use peripheral_interrupts::NvicIdx;

const IQ_SIZE: usize = 100;
static mut IQ_BUF : [NvicIdx; IQ_SIZE] = [NvicIdx::POWER_CLOCK; IQ_SIZE];
pub static mut INTERRUPT_QUEUE : Option<RingBuffer<'static, NvicIdx>> = None;

pub struct Nrf51822;

impl Nrf51822 {
    pub unsafe fn new() -> Nrf51822 {
        INTERRUPT_QUEUE = Some(RingBuffer::new(&mut IQ_BUF));
        Nrf51822
    }

    pub unsafe fn service_pending_interrupts(&mut self) {
        INTERRUPT_QUEUE.as_mut().unwrap().dequeue().map(|interrupt| {
            match interrupt {
                NvicIdx::RTC1 => rtc::RTC.handle_interrupt(),
                NvicIdx::GPIOTE  => gpio::PORT.handle_interrupt(),
                NvicIdx::TIMER0  => timer::TIMER0.handle_interrupt(),
                NvicIdx::TIMER1  => timer::ALARM1.handle_interrupt(),
                NvicIdx::TIMER2  => timer::TIMER2.handle_interrupt(),
                _ => {}
            }
            nvic::enable(interrupt);
        });
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        INTERRUPT_QUEUE.as_mut().unwrap().has_elements()
    }
}
<commit_msg>Should use timer.<commit_after>use common::{RingBuffer,Queue};
use nvic;
use rtc;
use gpio;
use timer;
use peripheral_interrupts::NvicIdx;

const IQ_SIZE: usize = 100;
static mut IQ_BUF : [NvicIdx; IQ_SIZE] = [NvicIdx::POWER_CLOCK; IQ_SIZE];
pub static mut INTERRUPT_QUEUE : Option<RingBuffer<'static, NvicIdx>> = None;

pub struct Nrf51822;

impl Nrf51822 {
    pub unsafe fn new() -> Nrf51822 {
        INTERRUPT_QUEUE = Some(RingBuffer::new(&mut IQ_BUF));
        Nrf51822
    }

    pub unsafe fn service_pending_interrupts(&mut self) {
        INTERRUPT_QUEUE.as_mut().unwrap().dequeue().map(|interrupt| {
            match interrupt {
                NvicIdx::RTC1 => rtc::RTC.handle_interrupt(),
                NvicIdx::GPIOTE  => gpio::PORT.handle_interrupt(),
                NvicIdx::TIMER0  => timer::TIMER0.handle_interrupt(),
                NvicIdx::TIMER1  => timer::ALARM1.handle_interrupt(),
                NvicIdx::TIMER2  => timer::TIMER2.handle_interrupt(),
                _ => {}
            }
            nvic::enable(interrupt);
        });
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        INTERRUPT_QUEUE.as_mut().unwrap().has_elements()
    }
}
<|endoftext|>


<commit_before>use crate::extractors::BragiQuery;
use crate::model::{Autocomplete, BragiError};
use crate::routes::params;
use crate::{model, query, Context};
use actix_web::{Json, State};
use geojson::GeoJson;
use mimir::objects::Coord;
use std::time::Duration;

#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
enum Type {
    #[serde(rename = "city")]
    City,
    #[serde(rename = "house")]
    House,
    #[serde(rename = "poi")]
    Poi,
    #[serde(rename = "public_transport:stop_area")]
    StopArea,
    #[serde(rename = "street")]
    Street,
}

impl Type {
    fn as_str(&self) -> &'static str {
        match self {
            &Type::City => "city",
            &Type::House => "house",
            &Type::Poi => "poi",
            &Type::StopArea => "public_transport:stop_area",
            &Type::Street => "street",
        }
    }
}

fn default_limit() -> u64 {
    10u64
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Params {
    q: String,
    #[serde(default)]
    pt_dataset: Vec<String>,
    #[serde(rename = "_all_data", default)]
    all_data: bool,
    //Note: for the moment we can't use an external struct and flatten it (https://github.com/nox/serde_urlencoded/issues/33)
    #[serde(default = "default_limit")]
    limit: u64,
    #[serde(default)]
    offset: u64,
    timeout: Option<Duration>,
    lat: Option<f64>,
    lon: Option<f64>,
    #[serde(default)]
    types: Vec<Type>,
}

impl Params {
    fn types_as_str(&self) -> Vec<&str> {
        self.types.iter().map(Type::as_str).collect()
    }
    fn coord(&self) -> Result<Option<Coord>, BragiError> {
        match (self.lon, self.lat) {
            (Some(lon), Some(lat)) => Ok(Some(params::make_coord(lon, lat)?)),
            (None, None) => Ok(None),
            _ => Err(BragiError::InvalidParam(
                "you should provide a 'lon' AND a 'lat' parametr if you provide one of them",
            )),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JsonParams {
    shape: GeoJson,
}

impl JsonParams {
    fn get_es_shape(&self) -> Result<Vec<(f64, f64)>, model::BragiError> {
        match &self.shape {
            GeoJson::Feature(f) => {
                match &f.geometry {
                    Some(geom) => {
                        match &geom.value {
                            geojson::Value::Polygon(p) => {
                                match p.as_slice() {
                                    [p] => {
                                        dbg!(Ok(p
                                            .iter()
                                            .filter_map(|c: &Vec<f64>| c.get(0..=1))
                                            .map(|c| (c[1], c[0])) // Note: the coord are inverted for ES
                                            .collect()))
                                    }
                                    _ => Err(BragiError::InvalidShape(
                                        "only polygon without holes are supported",
                                    )), //only polygon without holes are supported by elasticsearch
                                }
                            }
                            _ => Err(BragiError::InvalidShape("only polygon are supported")),
                        }
                    }
                    None => Err(BragiError::InvalidShape("no geometry")),
                }
            }
            _ => Err(BragiError::InvalidShape("only 'feature' is supported")),
        }
    }
}

pub fn call_autocomplete(
    params: &Params,
    state: &Context,
    shape: Option<Vec<(f64, f64)>>,
) -> Result<Json<Autocomplete>, model::BragiError> {
    let timeout = params::get_timeout(&params.timeout, &state.max_es_timeout);
    let res = query::autocomplete(
        &params.q,
        &params
            .pt_dataset
            .iter()
            .map(String::as_str)
            .collect::<Vec<_>>(),
        params.all_data,
        params.offset,
        params.limit,
        params.coord()?,
        &state.es_cnx_string,
        shape,
        &params.types_as_str(),
        timeout,
    );
    res.map(Autocomplete::from).map(Json)
}

pub fn autocomplete(
    params: BragiQuery<Params>,
    state: State<Context>,
) -> Result<Json<Autocomplete>, model::BragiError> {
    println!("{:?}", *params);
    call_autocomplete(&*params, &*state, None)
}

pub fn post_autocomplete(
    params: BragiQuery<Params>,
    state: State<Context>,
    json_params: Json<JsonParams>,
) -> Result<Json<Autocomplete>, model::BragiError> {
    println!(
        "POST autocomplete {:?} -------- {:?}",
        *params, *json_params
    );
    call_autocomplete(&*params, &*state, Some(json_params.get_es_shape()?))
}
<commit_msg>fix type<commit_after>use crate::extractors::BragiQuery;
use crate::model::{Autocomplete, BragiError};
use crate::routes::params;
use crate::{model, query, Context};
use actix_web::{Json, State};
use geojson::GeoJson;
use mimir::objects::Coord;
use std::time::Duration;

#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
enum Type {
    #[serde(rename = "city")]
    City,
    #[serde(rename = "house")]
    House,
    #[serde(rename = "poi")]
    Poi,
    #[serde(rename = "public_transport:stop_area")]
    StopArea,
    #[serde(rename = "street")]
    Street,
}

impl Type {
    fn as_str(&self) -> &'static str {
        match self {
            &Type::City => "city",
            &Type::House => "house",
            &Type::Poi => "poi",
            &Type::StopArea => "public_transport:stop_area",
            &Type::Street => "street",
        }
    }
}

fn default_limit() -> u64 {
    10u64
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Params {
    q: String,
    #[serde(default)]
    pt_dataset: Vec<String>,
    #[serde(rename = "_all_data", default)]
    all_data: bool,
    //Note: for the moment we can't use an external struct and flatten it (https://github.com/nox/serde_urlencoded/issues/33)
    #[serde(default = "default_limit")]
    limit: u64,
    #[serde(default)]
    offset: u64,
    timeout: Option<Duration>,
    lat: Option<f64>,
    lon: Option<f64>,
    #[serde(default, rename = "type")]
    types: Vec<Type>,
}

impl Params {
    fn types_as_str(&self) -> Vec<&str> {
        self.types.iter().map(Type::as_str).collect()
    }
    fn coord(&self) -> Result<Option<Coord>, BragiError> {
        match (self.lon, self.lat) {
            (Some(lon), Some(lat)) => Ok(Some(params::make_coord(lon, lat)?)),
            (None, None) => Ok(None),
            _ => Err(BragiError::InvalidParam(
                "you should provide a 'lon' AND a 'lat' parametr if you provide one of them",
            )),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct JsonParams {
    shape: GeoJson,
}

impl JsonParams {
    fn get_es_shape(&self) -> Result<Vec<(f64, f64)>, model::BragiError> {
        match &self.shape {
            GeoJson::Feature(f) => {
                match &f.geometry {
                    Some(geom) => {
                        match &geom.value {
                            geojson::Value::Polygon(p) => {
                                match p.as_slice() {
                                    [p] => {
                                        dbg!(Ok(p
                                            .iter()
                                            .filter_map(|c: &Vec<f64>| c.get(0..=1))
                                            .map(|c| (c[1], c[0])) // Note: the coord are inverted for ES
                                            .collect()))
                                    }
                                    _ => Err(BragiError::InvalidShape(
                                        "only polygon without holes are supported",
                                    )), //only polygon without holes are supported by elasticsearch
                                }
                            }
                            _ => Err(BragiError::InvalidShape("only polygon are supported")),
                        }
                    }
                    None => Err(BragiError::InvalidShape("no geometry")),
                }
            }
            _ => Err(BragiError::InvalidShape("only 'feature' is supported")),
        }
    }
}

pub fn call_autocomplete(
    params: &Params,
    state: &Context,
    shape: Option<Vec<(f64, f64)>>,
) -> Result<Json<Autocomplete>, model::BragiError> {
    let timeout = params::get_timeout(&params.timeout, &state.max_es_timeout);
    let res = query::autocomplete(
        &params.q,
        &params
            .pt_dataset
            .iter()
            .map(String::as_str)
            .collect::<Vec<_>>(),
        params.all_data,
        params.offset,
        params.limit,
        params.coord()?,
        &state.es_cnx_string,
        shape,
        &params.types_as_str(),
        timeout,
    );
    res.map(Autocomplete::from).map(Json)
}

pub fn autocomplete(
    params: BragiQuery<Params>,
    state: State<Context>,
) -> Result<Json<Autocomplete>, model::BragiError> {
    println!("{:?}", *params);
    call_autocomplete(&*params, &*state, None)
}

pub fn post_autocomplete(
    params: BragiQuery<Params>,
    state: State<Context>,
    json_params: Json<JsonParams>,
) -> Result<Json<Autocomplete>, model::BragiError> {
    println!(
        "POST autocomplete {:?} -------- {:?}",
        *params, *json_params
    );
    call_autocomplete(&*params, &*state, Some(json_params.get_es_shape()?))
}
<|endoftext|>


<commit_before>use clap;
use crossbeam::sync::MsQueue;
use hyper::status::StatusCode;
use info::discovery_service_info;
use select::{select_collection, writable_environment};

use serde_json::to_string;
use std::{thread, time};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use walkdir::WalkDir;

use wdsapi::common::{ApiError, Credentials};
use wdsapi::document;

struct Context {
    creds: Credentials,
    env_id: String,
    col_id: String,
    retries: u32,
    pace: time::Duration,
    doc_id: AtomicUsize,
    tick: AtomicUsize,
}

fn send_file_with_retry(context: &Context, filename: &str) -> () {
    let mut unexplained_error_count = 0;
    let doc_id = format!("{:011x}",
                         context.doc_id
                                .fetch_add(1, Ordering::Relaxed));
    loop {
        match document::create(&context.creds,
                               &context.env_id,
                               &context.col_id,
                               None,
                               Some(&doc_id),
                               filename) {
            Ok(response) => {
                println!("{} {}",
                         filename,
                         to_string(&response).unwrap_or_default());
                break;
            }
            Err(e) => {
                if let ApiError::Service(ref se) = e {
                    if se.status_code == StatusCode::TooManyRequests {
                        // The service says we're going too fast.
                        // Tell the main pace to wait four ticks,
                        // also double sleep here and resend.
                        context.tick.fetch_add(4, Ordering::Relaxed);
                        println!("{} sleep then retry after {}", filename, e);
                        thread::sleep(context.pace
                                             .checked_mul(2)
                                             .expect("Internal error: \
                                                      double sleep?!"));
                        continue;
                    }
                }
                unexplained_error_count += 1;
                if unexplained_error_count <= context.retries {
                    // We will retry, so tell the pace to wait another tick.
                    context.tick.fetch_add(1, Ordering::Relaxed);
                    println!("{} retry after fail to create document {}",
                             filename,
                             e);
                } else {
                    println!("{} give up after fail to create document {}",
                             filename,
                             e);
                    break;
                }
            }
        }
    }
}

fn push_worker(context: &Context, queue: &MsQueue<String>) -> () {
    loop {
        let filename = queue.pop();
        if filename.is_empty() {
            break;
        };
        send_file_with_retry(context, &filename);
    }
}

pub fn add_document(creds: Credentials, matches: &clap::ArgMatches) {
    let info = discovery_service_info(creds);
    let env_info = writable_environment(&info);
    let collection = select_collection(&env_info, matches);
    let env_id = env_info.environment_id;
    let col_id = collection["collection_id"]
        .as_str()
        .expect("Internal error: missing collection_id");
    let retries: u32 = matches.value_of("retries")
                              .unwrap_or("2")
                              .parse()
                              .expect("Retries must be an integer");
    let threads: u32 = matches.value_of("threads")
                              .unwrap_or("64")
                              .parse()
                              .expect("Threads must be an integer");
    let pace: u64 = matches.value_of("pace")
                           .unwrap_or("500")
                           .parse()
                           .expect("Pace must be an integer");
    let pace = time::Duration::from_millis(pace);
    let doc_id: usize = match matches.value_of("document-id") {
        Some(id) => {
            usize::from_str_radix(id, 16)
                .expect("Document-id must be a hexadecimal integer")
        }
        None => {
            // Our default starting document id is
            // milliseconds elapsed since the epoch.
            let dur = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_else(|_| Duration::new(0, 0));
            1000 * dur.as_secs() as usize +
            (dur.subsec_nanos() as usize / 1000000)
        }
    };
    let context = Arc::new(Context {
        creds: info.creds.clone(),
        env_id: env_id.clone(),
        col_id: col_id.to_string(),
        retries: retries,
        doc_id: AtomicUsize::new(doc_id),
        pace: pace,
        tick: AtomicUsize::new(0),
    });
    let queue = Arc::new(MsQueue::new());

    // Fire up a thread pool...
    for _ in 0..threads {
        let worker_item = context.clone();
        let worker_queue = queue.clone();
        thread::spawn(move || push_worker(&worker_item, &worker_queue));
    }

    let base_time = Instant::now();

    // Send work into the thread pool...
    for path in matches.values_of("paths").unwrap() {
        for entry in WalkDir::new(path)
            .sort_by(|a, b| a.cmp(b))
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|e| e.file_type().is_file()) {
            if let Some(filename) = entry.path().to_str() {
                let filename = filename.to_string();
                context.tick.fetch_add(1, Ordering::Relaxed);
                queue.push(filename);
                while let Some(sleep_duration) =
                    pace.checked_mul(context.tick
                                            .load(Ordering::Relaxed) as
                                     u32)
                        .expect("Ran too long?!")
                        .checked_sub(base_time.elapsed()) {
                    thread::sleep(sleep_duration);
                }
            }
        }
    }

    // Tell my threads to shutdown.
    for _ in 0..threads {
        queue.push(String::new());
    }

    while let Some(item) = queue.try_pop() {
        queue.push(item);
        thread::sleep(time::Duration::from_secs(1));
    }
}
<commit_msg>Avoid hang in shutdown #12<commit_after>use clap;
use crossbeam::sync::MsQueue;
use hyper::status::StatusCode;
use info::discovery_service_info;
use select::{select_collection, writable_environment};

use serde_json::to_string;
use std::{cmp, process, thread, time};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use walkdir::WalkDir;

use wdsapi::common::{ApiError, Credentials};
use wdsapi::document;

struct Context {
    creds: Credentials,
    env_id: String,
    col_id: String,
    retries: u32,
    pace: time::Duration,
    doc_id: AtomicUsize,
    tick: AtomicUsize,
}

fn send_file_with_retry(context: &Context, filename: &str) -> () {
    let mut unexplained_error_count = 0;
    let doc_id = format!("{:011x}",
                         context.doc_id
                                .fetch_add(1, Ordering::Relaxed));
    loop {
        match document::create(&context.creds,
                               &context.env_id,
                               &context.col_id,
                               None,
                               Some(&doc_id),
                               filename) {
            Ok(response) => {
                println!("{} {}",
                         filename,
                         to_string(&response).unwrap_or_default());
                break;
            }
            Err(e) => {
                if let ApiError::Service(ref se) = e {
                    if se.status_code == StatusCode::TooManyRequests {
                        // The service says we're going too fast.
                        // Tell the main pace to wait four ticks,
                        // also double sleep here and resend.
                        context.tick.fetch_add(4, Ordering::Relaxed);
                        println!("{} sleep then retry after {}", filename, e);
                        thread::sleep(context.pace
                                             .checked_mul(2)
                                             .unwrap_or(context.pace));
                        continue;
                    }
                }
                unexplained_error_count += 1;
                if unexplained_error_count <= context.retries {
                    // We will retry, so tell the pace to wait another tick.
                    context.tick.fetch_add(1, Ordering::Relaxed);
                    println!("{} retry after fail to create document {}",
                             filename,
                             e);
                } else {
                    println!("{} give up after fail to create document {}",
                             filename,
                             e);
                    break;
                }
            }
        }
    }
}

fn push_worker(context: &Context, queue: &MsQueue<String>) -> () {
    loop {
        let filename = queue.pop();
        if filename.is_empty() {
            break;
        };
        send_file_with_retry(context, &filename);
    }
}

pub fn add_document(creds: Credentials, matches: &clap::ArgMatches) {
    let info = discovery_service_info(creds);
    let env_info = writable_environment(&info);
    let collection = select_collection(&env_info, matches);
    let env_id = env_info.environment_id;
    let col_id = collection["collection_id"]
        .as_str()
        .expect("Internal error: missing collection_id");
    let retries: u32 = matches.value_of("retries")
                              .unwrap_or("2")
                              .parse()
                              .expect("Retries must be an integer");
    let threads: u32 = matches.value_of("threads")
                              .unwrap_or("64")
                              .parse()
                              .expect("Threads must be an integer");
    let pace: u64 = matches.value_of("pace")
                           .unwrap_or("500")
                           .parse()
                           .expect("Pace must be an integer");
    let pace = time::Duration::from_millis(pace);
    let doc_id: usize = match matches.value_of("document-id") {
        Some(id) => {
            usize::from_str_radix(id, 16)
                .expect("Document-id must be a hexadecimal integer")
        }
        None => {
            // Our default starting document id is
            // milliseconds elapsed since the epoch.
            let dur = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_else(|_| Duration::new(0, 0));
            1000 * dur.as_secs() as usize +
            (dur.subsec_nanos() as usize / 1000000)
        }
    };
    let context = Arc::new(Context {
        creds: info.creds.clone(),
        env_id: env_id.clone(),
        col_id: col_id.to_string(),
        retries: retries,
        doc_id: AtomicUsize::new(doc_id),
        pace: pace,
        tick: AtomicUsize::new(0),
    });
    let queue = Arc::new(MsQueue::new());

    // Fire up a thread pool...
    for _ in 0..threads {
        let worker_item = context.clone();
        let worker_queue = queue.clone();
        thread::spawn(move || push_worker(&worker_item, &worker_queue));
    }

    let base_time = Instant::now();

    // Send work into the thread pool...
    for path in matches.values_of("paths").unwrap() {
        for entry in WalkDir::new(path)
            .sort_by(|a, b| a.cmp(b))
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|e| e.file_type().is_file()) {
            if let Some(filename) = entry.path().to_str() {
                let filename = filename.to_string();
                context.tick.fetch_add(1, Ordering::Relaxed);
                queue.push(filename);
                while let Some(sleep_duration) =
                    pace.checked_mul(context.tick
                                            .load(Ordering::Relaxed) as
                                     u32)
                        .expect("Ran too long?!")
                        .checked_sub(base_time.elapsed()) {
                    thread::sleep(sleep_duration);
                }
            }
        }
    }

    // Tell my threads to shutdown.
    for _ in 0..threads {
        queue.push(String::new());
    }

    let mut wait_count = 0;
    // Wait for the the threads to complete, but not forever.
    while let Some(item) = queue.try_pop() {
        wait_count += 1;
        queue.push(item);
        thread::sleep(time::Duration::from_secs(1));
        if wait_count > cmp::max(30, threads) {
            println!("Timeout waiting for worker thread shutdown. Aborting.");
            process::exit(1);
        }
    }
}
<|endoftext|>